Skip to main content

GroupSpec

Struct GroupSpec 

Source
pub struct GroupSpec { /* private fields */ }
Expand description

Groups of rows with equal key values, and aggregates over each group.

use std::sync::Arc;
use arrow_array::{ArrayRef, Decimal128Array, Int64Array, RecordBatch, StringArray};
use densa_data::{Aggregate, GroupSpec, LiveTable};

let trades = |ids: Vec<i64>, desks: Vec<&str>, qty: Vec<i64>| {
    RecordBatch::try_from_iter([
        ("id", Arc::new(Int64Array::from(ids)) as ArrayRef),
        ("desk", Arc::new(StringArray::from(desks)) as ArrayRef),
        ("qty", Arc::new(Int64Array::from(qty)) as ArrayRef),
    ])
};
let mut table = LiveTable::new(trades(vec![], vec![], vec![])?.schema(), "id")?;
let by_desk = table.add_groups(
    GroupSpec::new()
        .group_by("desk")
        .aggregate(Aggregate::count())
        .aggregate(Aggregate::sum("qty")),
)?;
table.upsert(trades(vec![1, 2, 3], vec!["fx", "rates", "fx"], vec![5, 7, 10])?)?;
table.upsert(trades(vec![3], vec!["rates"], vec![10])?)?; // trade 3 moves desk

let totals = table.groups(by_desk).unwrap().to_batch()?; // fx: 1 trade, rates: 2
let qty = totals.column(2).as_any().downcast_ref::<Decimal128Array>().unwrap();
assert_eq!((qty.value(0), qty.value(1)), (5, 17));

Implementations§

Source§

impl GroupSpec

Source

pub fn new() -> Self

No keys and no aggregates yet. Without keys there is exactly one group, of every counted row, even when there are none.

Source

pub fn group_by(self, column: impl Into<String>) -> Self

Groups by column too, after the key columns given before it. Groups are listed by key in the order views sort: ascending, nulls last. -0.0 and 0.0 are one key, and so are all NaNs.

Source

pub fn group_by_time(self, column: impl Into<String>, every: Duration) -> Self

Groups by buckets of every (a minute, an hour) of column, a column of timestamps, dates, times of day or durations, as a key after the ones added before it.

The buckets are back to back from the column’s zero (midnight UTC for timestamps, whatever their time zone), so a bucket of a minute starts on a whole minute; each value is in the bucket that starts at or before it, less than every earlier. Buckets have one length: a week of dates starts on a Thursday, like 1970-01-01, and there are no months. The key column holds each bucket’s start, in the column’s type and under its name, so a column is a key once (a second key of it is DataError::DuplicateName). The lowest bucket of a type’s range, which may start before it, starts at the type’s smallest value. every is taken in whole units of the column, rounding down, and must be at least one (DataError::BucketWidth otherwise). Null times make a group of their own, last.

With a Window on the same column, the oldest bucket holds only the rows the window still keeps, and goes once they expire.

use std::sync::Arc;
use std::time::Duration;
use arrow_array::{ArrayRef, Int64Array, RecordBatch, TimestampSecondArray, UInt64Array};
use densa_data::{Aggregate, GroupSpec, LiveTable};

let requests = |ids: Vec<i64>, times: Vec<i64>| {
    RecordBatch::try_from_iter([
        ("id", Arc::new(Int64Array::from(ids)) as ArrayRef),
        ("time", Arc::new(TimestampSecondArray::from(times)) as ArrayRef),
    ])
};
let mut table = LiveTable::new(requests(vec![], vec![])?.schema(), "id")?;
let per_minute = table.add_groups(
    GroupSpec::new()
        .group_by_time("time", Duration::from_secs(60))
        .aggregate(Aggregate::count()),
)?;
table.upsert(requests(vec![1, 2, 3], vec![5, 59, 61])?)?;

let counts = table.groups(per_minute).unwrap().to_batch()?;
let starts = counts.column(0).as_any().downcast_ref::<TimestampSecondArray>().unwrap();
let count = counts.column(1).as_any().downcast_ref::<UInt64Array>().unwrap();
assert_eq!(starts.values(), &[0, 60]); // 00:00 and 00:01
assert_eq!(count.values(), &[2, 1]);
Source

pub fn group_by_step(self, column: impl Into<String>, step: f64) -> Self

Groups by buckets of step of column, a column of integers, decimals or floats, as a key after the ones added before it. step is in the column’s own units: 0.5 is fifty cents of a decimal of dollars.

As with group_by_time, the buckets are back to back from zero, each value is in the one that starts at or before it, and the key column holds each bucket’s start (the type’s smallest or largest value for a bucket that starts past the type’s range). For integers and decimals step is taken in whole units, rounding down, but a step within a billionth of its size of a whole number counts as it (0.29 is 29 cents, though 0.29 * 100.0 is just below 29); it must be at least one unit, or above zero and finite for floats (DataError::BucketWidth otherwise).

Float buckets forgive the rounding of the column’s type: a value at most two roundings below a bucket’s start, and never more than a sixteenth of a step, is in that bucket. 0.3 is in the bucket of 0.1 steps that starts at 0.3 (3.0 * 0.1, itself just above 0.3), though 0.3 / 0.1 is just below 3. NaN, the infinities and nulls are groups of their own.

Source

pub fn aggregate(self, aggregate: Aggregate) -> Self

Adds an aggregate column. Every key and aggregate needs its own name.

Source

pub fn filter(self, filter: Filter) -> Self

Counts only rows for which filter is true.

Trait Implementations§

Source§

impl Clone for GroupSpec

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for GroupSpec

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for GroupSpec

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl PartialEq for GroupSpec

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for GroupSpec

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.