You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Support avg in hybrid via group sum/count pre-aggregation
- Auto: 100k row threshold when row*col groups > 10k; else 250k
- Clarify forced threshold_hybrid bypasses size heuristics
- Append drill-down note to server_mode_reason; show in WarningBanner
- Tests for hybrid helpers and mount payloads
Made-with: Cursor
Server-side threshold hybrid pre-aggregation now supports **`avg` (mean)** in addition to `sum`, `count`, `min`, and `max`. Auto-selection uses a **lower row threshold (100k)** when estimated pivot cardinality is high. Forced **`execution_mode="threshold_hybrid"`** returns an explicit reason that automatic thresholds are skipped. **`server_mode_reason`** includes a drill-down explanation, and the **frontend shows that text** in the existing warning banner when `execution_mode === "threshold_hybrid"`.
11
+
12
+
**Standard deviation (`std`)** was **not** added: it is not part of the public `VALID_AGGREGATIONS` / frontend aggregation union, and correct roll-up of subgroup variances would require extra state (or would be wrong if re-aggregated like means).
-**`_prepare_threshold_hybrid_frame`**: for `avg`, uses `pandas.NamedAgg` with per-group `sum` and `count`, then **`mean = sum / count`** (same as `groupby(...).mean()` for numeric data). Handles the no–group-by case as a single aggregate row. Empty `values` returns an empty frame with group columns only when grouping keys exist.
18
+
-**`_should_use_threshold_hybrid`**:
19
+
-**`threshold_hybrid`**: if compatible, always enables hybrid and explains that **row-count heuristics are not applied** (clarified vs. older wording).
20
+
-**`auto`**: `estimated_pivot_groups = row_groups * col_groups`; if `> 10_000`, **`row_threshold = 100_000`**, else **`250_000`**. Shape gate unchanged (`visible_cells > 5000` or `col_groups > 200` or `row_groups > 5000`).
21
+
-**`st_pivot_table`**: when hybrid is active, **`server_mode_reason`** appends a short **drill-down unavailable** sentence (unless already present).
22
+
-**`import pandas as pd`** for `NamedAgg` / frame helpers (pandas is already required via Streamlit).
23
+
24
+
## Aggregation coverage in hybrid
25
+
26
+
| Supported in hybrid | Notes |
27
+
|---------------------|--------|
28
+
|`sum`, `count`, `min`, `max`| Unchanged |
29
+
|`avg`| Pre-aggregated as true group mean (sum/count) |
30
+
31
+
Still **not** supported in hybrid (unchanged): `count_distinct`, `median`, `percentile_90`, `first`, `last`, synthetic measures.
32
+
33
+
**Coverage vs. “common” configs:** The default toolbar-style set is typically **sum, avg, count, min, max** — hybrid now supports **all five**, up from four previously (~80% of that set by count; previously 4/5 = 80% of this slice). Against **all**`VALID_AGGREGATIONS` entries, hybrid covers **5 / 10** named types (50%); the remaining five are specialized.
34
+
35
+
## Threshold tuning
36
+
37
+
| Condition | Row threshold | Rationale |
38
+
|-----------|---------------|-----------|
39
+
|`row_groups * col_groups > 10_000`|**100,000**| High cardinality benefits earlier server reduction |
40
+
| Otherwise |**250,000**| Preserves previous behavior for moderate shapes |
41
+
|`execution_mode="threshold_hybrid"`| N/A (always on if compatible) | Explicit force path; no size checks |
42
+
43
+
## Frontend changes
44
+
45
+
-**`index.tsx`**: passes **`server_mode_reason`** into `PivotRoot`.
46
+
-**`PivotRoot.tsx`**: when **`execution_mode === "threshold_hybrid"`**, appends **`server_mode_reason`** (or a short fallback) to **`allWarnings`** so the **WarningBanner** explains hybrid + drill-down limits.
47
+
48
+
No change to `PivotData` / worker paths for `avg`: pre-aggregated means are shipped as ordinary numeric cells at the final granularity.
49
+
50
+
## Tests
51
+
52
+
### Python (`python -m pytest tests/ -v`)
53
+
54
+
-**32 passed** (0 failed). Includes new **`tests/test_threshold_hybrid.py`** and updated mount tests (`median` for incompatible hybrid; `server_mode_reason` / drill-down assertion for hybrid mount).
55
+
56
+
### Frontend (`npm test`)
57
+
58
+
-**528 passed** (15 files).
59
+
60
+
## Benchmarks (frontend)
61
+
62
+
`npm run bench:ci` — representative lines from latest run:
63
+
64
+
- small dataset (1K): ~2.35k hz
65
+
- medium (50K, 100×20): ~44.5 hz
66
+
- stress (200K, 500×20): ~9.9 hz
67
+
- parseArrow 50K / 200K: ~112.7 hz / ~25.3 hz
68
+
69
+
Output also written to `streamlit_pivot/frontend/bench-results.json`.
70
+
71
+
**Interpretation:** Numbers are **unchanged in spirit** from a server-only change; the client still runs the same pivot code on (usually) fewer rows in hybrid mode.
Written to `streamlit_pivot/frontend/perf-results/memory-profile.json`.
81
+
82
+
**Python RSS:** Not instrumented in this pass; hybrid reduces rows transferred to the browser, which typically lowers browser memory for large raw datasets.
83
+
84
+
## Risks
85
+
86
+
1.**Mean of means:** If the client **re-aggregates** pre-aggregated means across groups (e.g. subtotals / roll-ups), the result is not the global mean of underlying raw rows. Same class of issue as any pre-aggregated measure; **documented** in code comments / this report.
87
+
2.**Advanced aggregations** still force **client_only** for large data or require a different server strategy.
88
+
3.**100k + high-cardinality auto path** may increase server CPU and shift load earlier; tune `10_000` / thresholds if needed in production.
89
+
90
+
## Preliminary verdict
91
+
92
+
**Ship:**`avg` in hybrid, clearer forced-mode messaging, improved auto thresholds for high-cardinality layouts, and visible **hybrid + drill-down** guidance in the UI. **Defer `std`** until API, frontend, and roll-up semantics are defined.
0 commit comments