GroupBy Cumulative¶
Source: src/memframe/wrappers/analytix/groupby_cumulative.py
GroupByCumulativeWrapper is the public group-wise running-computation
interface exposed through a ContextManager. It provides
pandas-groupby().cumsum()-like analytics: cumulative sum, product,
minimum, maximum, mean, count, standard deviation, and variance, each
computed over a window partitioned by the group columns and ordered by a
column you choose (or the physical row order by default).
Users normally call group-by cumulative methods through the unified
groupby builder on a dataset context returned by an upload operation:
dataset = mf.upload_df(frame)
result = dataset.groupby("region").cumsum("revenue", order_col="month")
result = dataset.groupby("region").cummean("score")
dataset = await mf.aupload_df(frame)
result = await dataset.groupby("region").acumsum("revenue", order_col="month")
Note
A flat dataset.cumsum(...) resolves to the ungrouped
CumulativeWrapper.cumsum(column, order_col, target_col) (it is
registered first and takes no group columns). The grouped form above
is only reachable through the groupby(...) builder or
GroupByCumulativeWrapper directly — the same arrangement as
grouped event_rate on the Stats page.
The lower-level files are implementation details:
src/memframe/core/analytix/groupby_cumulative.pybuilds and executes backend-specific SQL in a singleGroupbyCumulativeOpsclass (isinstancebranches for DuckDB / PostgreSQL / ClickHouse).src/memframe/core/orchestrator/analytix/groupby_cumulative.pyresolves the active dataset context and applies@record_call.src/memframe/wrappers/analytix/groupby_cumulative.pyexposes the synchronous and asynchronous builder methods plus the direct async API.src/memframe/wrappers/analytix/groupby.pyis the unifiedGroupByfacade that routesctx.groupby(*columns)calls to the stats, cumulative, and (future) window builders.
Public API¶
Each operation has a synchronous and asynchronous form. All take
(column, order_col=None, target_col=None):
| Synchronous | Asynchronous | Purpose |
|---|---|---|
cumsum(column, order_col=None, target_col=None) |
await acumsum(...) |
Group-wise running sum |
cumprod(column, order_col=None, target_col=None) |
await acumprod(...) |
Group-wise running product |
cummax(column, order_col=None, target_col=None) |
await acummax(...) |
Group-wise running maximum |
cummin(column, order_col=None, target_col=None) |
await acummin(...) |
Group-wise running minimum |
cummean(column, order_col=None, target_col=None) |
await acummean(...) |
Group-wise running mean |
cumcount(column, order_col=None, target_col=None) |
await acumcount(...) |
Group-wise running non-null count |
cumstd(column, order_col=None, target_col=None) |
await acumstd(...) |
Group-wise running population stddev |
cumvar(column, order_col=None, target_col=None) |
await acumvar(...) |
Group-wise running population variance |
Public builder methods return the raw operation envelope (see Return
Values below). Invalid operations surface is_error envelopes (or raise
OperationError once unwrapped via a ContextManager).
Usage Overview¶
dataset = mf.upload_df(frame)
sample = dataset.groupby("region").cumsum("revenue", order_col="month")
sample = dataset.groupby("region").cummean("score", target_col="running_avg")
sample = dataset.groupby("region", "month").cumcount("user_id")
dataset = await mf.aupload_df(frame)
sample = await dataset.groupby("region").acumsum("revenue", order_col="month")
sample = await dataset.groupby("region").acummean("score")
order_col is optional — omit it to use physical row order:
sample = dataset.groupby("region").cumsum("revenue")
sample = dataset.groupby("region").cummean("score")
Common Parameters¶
| Parameter | Type | Description |
|---|---|---|
column |
str |
Numeric column to accumulate. |
order_col |
str, list[str], or None |
Row ordering inside each group. None (default) uses physical row order. A list orders by multiple columns. |
target_col |
str or None |
Name for the result column. Auto-generated as cum_<column>_<op>_by_<groups> with _order_by_<orders> appended when order_col is given (e.g. cum_revenue_sum_by_region_order_by_month). |
map_feature |
bool |
When True (default False), LEFT JOIN the new feature column back onto the original table — matched on full row identity. No extra result table is created; the envelope gains mapped_table / mapped_columns. |
group_cols are fixed at builder creation (groupby("region", "month"))
and accept one or more columns.
With no order_col, rows accumulate in physical storage order
(PostgreSQL ctid, DuckDB rowid). ClickHouse has no stable row id, so
a synthetic _mf_row_num (ROW_NUMBER() OVER()) is generated in a
subquery to give the window a deterministic order, then stripped from
the output via SELECT * EXCEPT(_mf_row_num) so chained operations stay
clean.
Running Sum and Product¶
cumsum¶
Running sum within each group via SUM(col) OVER (PARTITION BY …
ORDER BY … ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW).
cumprod¶
Running product within each group via EXP(SUM(LN(NULLIF(col, 0)))
OVER ({window_spec})). Zeros are skipped rather than zeroing the tail.
Negative inputs abort the whole operation with is_error True (engines
reject LN of a negative). Restrict cumprod to non-negative columns.
Running Min, Max, and Mean¶
cummax, cummin¶
Running extrema within each group via MAX(col) / MIN(col) over the
same partitioned window frame.
result = dataset.groupby("region").cummax("price", order_col="day")
result = dataset.groupby("region").cummin("price", order_col="day")
cummean¶
Running mean within each group via AVG(col).
Running Count, Stddev, and Variance¶
cumcount¶
Running non-null count within each group via COUNT(col).
The result column is BIGINT on PostgreSQL/DuckDB and Int64 on
ClickHouse (pass target_type through the direct API to override).
cumstd, cumvar¶
Running population (not sample) statistics within each group:
STDDEV_POP(col) / VAR_POP(col) on PostgreSQL and DuckDB,
stddevPop(col) / varPop(col) on ClickHouse.
result = dataset.groupby("region").cumstd("score", order_col="month")
result = dataset.groupby("region").cumvar("score", order_col="month")
Map Feature Back (map_feature)¶
With map_feature=True, the new running-value column is LEFT JOINed back
onto the original table — every original row gains its own running value
(one output row per input row, matched on full row identity). Only the
feature column is added and no extra result table is created. Only the
group table is recorded by @record_call.
result = dataset.groupby("region").cumsum("revenue", map_feature=True)
# original table now has: region, revenue, cum_revenue_sum_by_region
Rules:
- A repeated identical call re-runs cleanly (its own column is dropped and re-added). This also holds in non-deep mode, which saves nothing.
- If the column name already exists from anything else, the call fails with an error — drop or rename it first.
- Backend swaps keep the original table name: DuckDB
CREATE OR REPLACE, PostgreSQL create-swap-rename, ClickHouse atomicEXCHANGE TABLES. - Rows with
NULLkey columns match nothing (NULL = NULLis false in SQL) and keep aNULLfeature value.
Return Values and Errors¶
Calling the builder (or the wrapper class) directly returns the raw operation envelope:
{
"is_error": False,
"message": "Cumulative sum of 'revenue' grouped by [region] ...",
"result": <pandas.DataFrame>,
"new_table": "ab12CD__op_3",
"new_columns": ["cum_revenue_sum_by_region"],
"group_cols": ["region"],
"operation": "sum",
}
Invalid operations return is_error envelopes (and raise
OperationError once unwrapped via a ContextManager): a missing
column, a missing backend/data_id, or an unsupported backend.
Generated Tables¶
Every group-by cumulative operation is non-destructive to the source upload table. It creates a new transient table holding the source rows plus the result column, then returns a preview of it:
- PostgreSQL / DuckDB:
CREATE TABLE … AS SELECT *, <window> AS <target> FROM …withPARTITION BY+ physical-row fallback ordering. - ClickHouse: the same single-pass shape with
ENGINE = MergeTree() ORDER BY tuple(); withoutorder_colthe source is wrapped in aROW_NUMBER()subquery whose helper column is excluded from the output.
Operations apply @record_call, so repeat calls are recorded in the
transient registry.
Backend Behavior¶
Group-by cumulative supports DuckDB, PostgreSQL, and ClickHouse:
- Identifiers are sanitized and quoted before SQL is generated.
group_colsmay be a single column or a list; at least one is required.order_colmay be a single column, a list, or omitted (physical order; synthetic_mf_row_numon ClickHouse).STDDEV_POP/VAR_POPmap to ClickHouse-nativestddevPop/varPop.
Errors¶
Group-by cumulative methods surface is_error envelopes (or raise
OperationError once unwrapped) for validation or backend failures:
- Missing or renamed
column/order_col/ group column. cumprodon a column containing negatives fails the whole operation.- Missing
backend/data_idat the ops layer, or an unsupported backend.
API Reference¶
memframe.wrappers.analytix.groupby_cumulative.GroupByCumulativeWrapper
¶
Bases: GroupByCumulativeOrchestrator
Wrapper around GroupByCumulativeOrchestrator fluent APIs.
groupby(*columns)
¶
Create a cumulative builder scoped to one or more group columns.
memframe.wrappers.analytix.groupby_cumulative.GroupByCumulativeBuilderWrapper
¶
Bases: GroupByCumulative
Fluent group-by cumulative builder with async/sync methods.
acumsum(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative sum.
cumsum(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative sum.
acumprod(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative product.
cumprod(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative product.
acummax(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative maximum.
cummax(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative maximum.
acummin(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative minimum.
cummin(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative minimum.
acummean(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative mean.
cummean(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative mean.
acumcount(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative count.
cumcount(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative count.
acumstd(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative standard deviation.
cumstd(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative standard deviation.
acumvar(column, order_col=None, target_col=None, map_feature=False)
async
¶
Asynchronously compute group-wise cumulative variance.
cumvar(column, order_col=None, target_col=None, map_feature=False)
async
¶
Synchronously compute group-wise cumulative variance.