pond-ts 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/dist/batch/aggregate-columns.d.ts +29 -0
  3. package/dist/batch/aggregate-columns.d.ts.map +1 -1
  4. package/dist/batch/aggregate-columns.js +63 -0
  5. package/dist/batch/aggregate-columns.js.map +1 -1
  6. package/dist/batch/operators/collapse.d.ts +42 -0
  7. package/dist/batch/operators/collapse.d.ts.map +1 -0
  8. package/dist/batch/operators/collapse.js +83 -0
  9. package/dist/batch/operators/collapse.js.map +1 -0
  10. package/dist/batch/operators/cumulative.d.ts +40 -0
  11. package/dist/batch/operators/cumulative.d.ts.map +1 -0
  12. package/dist/batch/operators/cumulative.js +74 -0
  13. package/dist/batch/operators/cumulative.js.map +1 -0
  14. package/dist/batch/operators/diff-rate.d.ts +52 -0
  15. package/dist/batch/operators/diff-rate.d.ts.map +1 -0
  16. package/dist/batch/operators/diff-rate.js +99 -0
  17. package/dist/batch/operators/diff-rate.js.map +1 -0
  18. package/dist/batch/operators/fill.d.ts +66 -0
  19. package/dist/batch/operators/fill.d.ts.map +1 -0
  20. package/dist/batch/operators/fill.js +219 -0
  21. package/dist/batch/operators/fill.js.map +1 -0
  22. package/dist/batch/operators/map.d.ts +44 -0
  23. package/dist/batch/operators/map.d.ts.map +1 -0
  24. package/dist/batch/operators/map.js +77 -0
  25. package/dist/batch/operators/map.js.map +1 -0
  26. package/dist/batch/operators/shift.d.ts +31 -0
  27. package/dist/batch/operators/shift.d.ts.map +1 -0
  28. package/dist/batch/operators/shift.js +56 -0
  29. package/dist/batch/operators/shift.js.map +1 -0
  30. package/dist/batch/time-series.d.ts +34 -3
  31. package/dist/batch/time-series.d.ts.map +1 -1
  32. package/dist/batch/time-series.js +173 -371
  33. package/dist/batch/time-series.js.map +1 -1
  34. package/dist/columnar/index.d.ts +1 -1
  35. package/dist/columnar/index.d.ts.map +1 -1
  36. package/dist/columnar/index.js +1 -1
  37. package/dist/columnar/index.js.map +1 -1
  38. package/dist/columnar/view.d.ts +32 -0
  39. package/dist/columnar/view.d.ts.map +1 -1
  40. package/dist/columnar/view.js +62 -0
  41. package/dist/columnar/view.js.map +1 -1
  42. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,13 +7,60 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
7
7
  file covers both packages. Pre-1.0: minor bumps may include new features and
8
8
  type-level changes; patch bumps are strictly additive.
9
9
 
10
- [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...HEAD
10
+ [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.21.0...HEAD
11
+ [0.21.0]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...v0.21.0
11
12
  [0.20.0]: https://github.com/pjm17971/pond-ts/compare/v0.19.0...v0.20.0
12
13
  [0.19.0]: https://github.com/pjm17971/pond-ts/compare/v0.18.0...v0.19.0
13
14
  [0.18.0]: https://github.com/pjm17971/pond-ts/compare/v0.17.1...v0.18.0
14
15
 
15
16
  ## [Unreleased]
16
17
 
18
+ ## [0.21.0] — 2026-06-11
19
+
20
+ ### Added
21
+
22
+ - **`TimeSeries.mapColumns({ col: (value) => newValue })`** — a per-cell column
23
+ value transform. The column-scoped counterpart of the event-based `map()`:
24
+ where `map(schema, event => newEvent)` rebuilds whole rows through an
25
+ arbitrary closure (and can change the schema/key), `mapColumns` transforms
26
+ individual columns' values in place, reading the columns directly (no
27
+ per-row `Event`) so it stays on the fast columnar path. Same kind in/out
28
+ (number→number, string→string, …), so the schema is unchanged; missing cells
29
+ carry (the mapper isn't called on `undefined`). ~5–6× faster than the
30
+ `map()` workaround on a build → transform → read pipeline.
31
+
32
+ ### Changed
33
+
34
+ - **`select` / `rename` / `slice` / `cumulative` / `diff` / `rate` /
35
+ `pctChange` / `fill` / `shift` / `collapse` are now column-native.** They
36
+ reshape the columnar store directly instead of materializing events, so the
37
+ columnar construction win is preserved through these transforms — build →
38
+ transform → read pipelines run several× faster (~7–10× for `select` /
39
+ `rename` / `slice`; ~5–7× for the `cumulative` / `diff` / `rate` / `fill` /
40
+ `shift` / `collapse` folds). No API change for type-correct callers (one
41
+ narrow `fill` behavior change is noted under Fixed). `cumulative` / `diff` /
42
+ `rate` / `pctChange` / `fill` / `shift` / `collapse` are also the first
43
+ operators extracted into `batch/operators/` (internal refactor); `fill`
44
+ rebuilds only the columns it actually changes; `slice` normalizes
45
+ `Array.prototype.slice` semantics onto a zero-copy `withRowRange` reshape;
46
+ `collapse` reads only the keyed columns and passes the kept columns through
47
+ by reference.
48
+
49
+ ### Fixed
50
+
51
+ - **`rename` now rejects target-name collisions** (e.g. renaming `a` → `b`
52
+ when `b` already exists) with a clear error, instead of silently producing a
53
+ duplicate-named schema. Also fixes a prototype-chain bug where a column named
54
+ `toString` (or another `Object.prototype` member) could be corrupted during
55
+ a rename.
56
+ - **`fill` now throws on a kind-mismatched literal** (e.g.
57
+ `fill({ value: 'banana' })` where `value` is numeric — type-allowed because
58
+ mapping values are the broad `FillStrategy | ScalarValue`) with a clear
59
+ `RangeError` naming the column, instead of silently producing an
60
+ internally-inconsistent series (the old events path returned the literal
61
+ from `.get()` while the numeric column read `NaN`). The throw is
62
+ gap-dependent — it only fires when the literal would actually be placed.
63
+
17
64
  ## [0.20.0] — 2026-06-04
18
65
 
19
66
  Two internal performance improvements driven by the dashboard experiment at
@@ -1,3 +1,4 @@
1
+ import type { Column } from '../columnar/index.js';
1
2
  import type { AggregateMap, AggregateOutputMap, AggregateReducer, ScalarKind, SeriesSchema } from '../schema/index.js';
2
3
  /**
3
4
  * Normalised column spec used by both batch and live aggregation paths.
@@ -33,4 +34,32 @@ export type AggregateColumnSpec = {
33
34
  * same `mapping` shape produces the same schema.
34
35
  */
35
36
  export declare function normalizeAggregateColumns<S extends SeriesSchema>(schema: S, mapping: AggregateMap<S> | AggregateOutputMap<S>): AggregateColumnSpec[];
37
+ /**
38
+ * **Phase 4.7 step 3B — columnar fast path for time-keyed `aggregate()`.**
39
+ * On sorted time-keyed data each bucket is a contiguous index range, so
40
+ * when every mapped column is a built-in numeric reducer with a
41
+ * `reduceColumn` fast path over a **packed `Float64Column`** source, each
42
+ * bucket reduces straight off the typed-array slice — skipping the
43
+ * `series.events` materialization and the per-cell `state.add` walk the
44
+ * row path pays. Reuses the shipped step-3A `reduceColumn` kernels
45
+ * (sum/min/max/avg 59–73×, stdev 35×, median/p95 3.4×) per bucket.
46
+ *
47
+ * Returns `null` — caller takes the unchanged row path — when any column
48
+ * doesn't qualify: a custom-function reducer, a reducer with no
49
+ * `reduceColumn` (`first` / `last` / `unique` / `top` / `samples`), or a
50
+ * non-numeric / chunked / missing source column. All-or-nothing per call
51
+ * keeps the bucket walk single-pass; mixed mappings fall back wholesale.
52
+ *
53
+ * `begins` is the key column's begin axis (sorted, identical row order to
54
+ * the value columns — both read straight off the store). Bucketing
55
+ * replicates the row path exactly: `cursor` carries across buckets, and a
56
+ * bucket owns `[start, scan)` where `begins[i] ∈ [bucket.begin,
57
+ * bucket.end)`. Empty buckets reduce an empty slice — the reducer's
58
+ * empty-input result (the step-3A parity contract guarantees this matches
59
+ * a zero-`add` bucket snapshot).
60
+ */
61
+ export declare function tryAggregateColumnarTimeKeyed<B extends {
62
+ begin(): number;
63
+ end(): number;
64
+ }>(begins: Float64Array, getColumn: (name: string) => Column | undefined, buckets: ReadonlyArray<B>, columns: ReadonlyArray<AggregateColumnSpec>): Array<ReadonlyArray<unknown>> | null;
36
65
  //# sourceMappingURL=aggregate-columns.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-columns.d.ts","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAElB,gBAAgB,EAChB,UAAU,EACV,YAAY,EACb,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAkBF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,YAAY,EAC9D,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAC/C,mBAAmB,EAAE,CAuDvB"}
1
+ {"version":3,"file":"aggregate-columns.d.ts","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAElB,gBAAgB,EAEhB,UAAU,EACV,YAAY,EACb,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAkBF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,YAAY,EAC9D,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAC/C,mBAAmB,EAAE,CAuDvB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,6BAA6B,CAC3C,CAAC,SAAS;IAAE,KAAK,IAAI,MAAM,CAAC;IAAC,GAAG,IAAI,MAAM,CAAA;CAAE,EAE5C,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAC/C,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,OAAO,EAAE,aAAa,CAAC,mBAAmB,CAAC,GAC1C,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAyCtC"}
@@ -73,4 +73,67 @@ export function normalizeAggregateColumns(schema, mapping) {
73
73
  }
74
74
  return normalized;
75
75
  }
76
+ /**
77
+ * **Phase 4.7 step 3B — columnar fast path for time-keyed `aggregate()`.**
78
+ * On sorted time-keyed data each bucket is a contiguous index range, so
79
+ * when every mapped column is a built-in numeric reducer with a
80
+ * `reduceColumn` fast path over a **packed `Float64Column`** source, each
81
+ * bucket reduces straight off the typed-array slice — skipping the
82
+ * `series.events` materialization and the per-cell `state.add` walk the
83
+ * row path pays. Reuses the shipped step-3A `reduceColumn` kernels
84
+ * (sum/min/max/avg 59–73×, stdev 35×, median/p95 3.4×) per bucket.
85
+ *
86
+ * Returns `null` — caller takes the unchanged row path — when any column
87
+ * doesn't qualify: a custom-function reducer, a reducer with no
88
+ * `reduceColumn` (`first` / `last` / `unique` / `top` / `samples`), or a
89
+ * non-numeric / chunked / missing source column. All-or-nothing per call
90
+ * keeps the bucket walk single-pass; mixed mappings fall back wholesale.
91
+ *
92
+ * `begins` is the key column's begin axis (sorted, identical row order to
93
+ * the value columns — both read straight off the store). Bucketing
94
+ * replicates the row path exactly: `cursor` carries across buckets, and a
95
+ * bucket owns `[start, scan)` where `begins[i] ∈ [bucket.begin,
96
+ * bucket.end)`. Empty buckets reduce an empty slice — the reducer's
97
+ * empty-input result (the step-3A parity contract guarantees this matches
98
+ * a zero-`add` bucket snapshot).
99
+ */
100
+ export function tryAggregateColumnarTimeKeyed(begins, getColumn, buckets, columns) {
101
+ const plans = [];
102
+ for (const spec of columns) {
103
+ if (typeof spec.reducer !== 'string')
104
+ return null; // custom function
105
+ const def = resolveReducer(spec.reducer);
106
+ if (def.reduceColumn === undefined)
107
+ return null; // first/last/unique/...
108
+ const source = getColumn(spec.source);
109
+ if (source === undefined ||
110
+ source.kind !== 'number' ||
111
+ source.storage !== 'packed') {
112
+ return null; // non-numeric / chunked / missing source
113
+ }
114
+ plans.push({ column: source, reduce: def.reduceColumn });
115
+ }
116
+ const n = begins.length;
117
+ let cursor = 0;
118
+ const rows = new Array(buckets.length);
119
+ for (let b = 0; b < buckets.length; b += 1) {
120
+ const bucket = buckets[b];
121
+ const bucketBegin = bucket.begin();
122
+ const bucketEnd = bucket.end();
123
+ while (cursor < n && begins[cursor] < bucketBegin)
124
+ cursor += 1;
125
+ const start = cursor;
126
+ let scan = start;
127
+ while (scan < n && begins[scan] < bucketEnd)
128
+ scan += 1;
129
+ cursor = scan;
130
+ const reduced = new Array(plans.length);
131
+ for (let p = 0; p < plans.length; p += 1) {
132
+ const plan = plans[p];
133
+ reduced[p] = plan.reduce(plan.column.sliceByRange(start, scan));
134
+ }
135
+ rows[b] = Object.freeze([bucket, ...reduced]);
136
+ }
137
+ return rows;
138
+ }
76
139
  //# sourceMappingURL=aggregate-columns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-columns.js","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA6BtD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAc;IAEd,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,OAAO,IAAI,KAAK,CACjB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAS,EACT,OAAgD;IAEhD,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAU,CAAC,CAChE,CAAC;IACF,MAAM,UAAU,GAA0B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,MAAM,UAAU,GAAG,qBAAqB,CAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;QACzE,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,SAAS,CACjB,uDAAuD,UAAU,GAAG,CACrE,CAAC;QACJ,CAAC;QACD,IACE,YAAY,CAAC,IAAI,KAAK,QAAQ;YAC9B,YAAY,CAAC,IAAI,KAAK,QAAQ;YAC9B,YAAY,CAAC,IAAI,KAAK,SAAS;YAC/B,YAAY,CAAC,IAAI,KAAK,OAAO,EAC7B,CAAC;YACD,MAAM,IAAI,SAAS,CACjB,4BAA4B,UAAU,0BAA0B,CACjE,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,qBAAqB,CAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;QAChE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YACjE,MAAM,IAAI,SAAS,CACjB,0BAA0B,UAAU,uCAAuC,CAC5E,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,qBAAqB,CAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAC1E,IAAI,YAAwB,CAAC;QAC7B,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,YAAY,GAAG,YAAY,CAAC;QAC9B,CAAC;aAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;YACxC,IAAI,OAAO,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACpC,YAAY,GAAG,QAAQ,CAAC;YAC1B,CAAC;iBAAM,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;gBAC1C,YAAY,GAAG,OAAO,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC;YACnC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC;QACnC,CAAC;QACD,UAAU,CAAC,IAAI,CAAC;YACd,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,UAAU;YAClB,OAAO;YACP,IAAI,EAAE,YAAY;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"aggregate-columns.js","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { type ColumnarStore } from '../../columnar/index.js';
2
+ import type { ScalarValue, SeriesSchema } from '../../schema/index.js';
3
+ /** The reducer's row input — the keyed columns' values for one row. */
4
+ export type CollapseReducer = (values: Record<string, unknown>) => ScalarValue;
5
+ /**
6
+ * **Step 4 — column-native `collapse` (extracted operator).** Reduces
7
+ * the `keys` columns into a single `output` column, one value per row,
8
+ * reading **only the keyed columns** off the store (storage-agnostic
9
+ * `read(i)`) — no `series.events` materialization, no per-row `Event`.
10
+ * The kept columns + the key axis pass through by reference.
11
+ *
12
+ * Unlike the pure folds (`cumulative`, `diff`, `shift`), `collapse`
13
+ * still calls the user reducer per row over a small `{ key: value }`
14
+ * object, so the win is the more modest "read only the keyed columns +
15
+ * drop the Event allocation + share the kept columns" rather than a
16
+ * full vectorization — but the materialization tax is still removed.
17
+ *
18
+ * Semantics (matching the row path):
19
+ * - the reducer receives the keyed columns' values for the row
20
+ * (`undefined` for a missing cell), and returns a `ScalarValue`;
21
+ * - the output column's kind is inferred from the **first row's**
22
+ * result (`number` / `boolean` / else `string`) — an empty series
23
+ * yields a `string` output column, matching the old `nextEvents[0]?`
24
+ * inference;
25
+ * - **`append: false`** (default) drops the keyed columns and appends
26
+ * `output`; **`append: true`** keeps every value column and appends
27
+ * `output`.
28
+ *
29
+ * Returns the reshaped store + the output schema; the result-schema
30
+ * cast is the single trust boundary, and the `TimeSeries.collapse`
31
+ * method wraps the store via `#fromTrustedStore`. An `output` name
32
+ * that collides with a kept column throws via `withColumnAppended`.
33
+ *
34
+ * NB: `buildScalarColumn` overlaps `fillOp`'s `buildFilledColumn` and
35
+ * `mapOp`'s `columnFromValuesByKind` — three callers now; a shared
36
+ * `columnFromValuesByKind` helper is the flagged follow-up.
37
+ */
38
+ export declare function collapseOp<S extends SeriesSchema, OutSchema extends SeriesSchema>(store: ColumnarStore<S>, schema: S, keys: readonly string[], output: string, reducer: CollapseReducer, append: boolean): {
39
+ store: ColumnarStore<OutSchema>;
40
+ schema: OutSchema;
41
+ };
42
+ //# sourceMappingURL=collapse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapse.d.ts","sourceRoot":"","sources":["../../../src/batch/operators/collapse.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAOnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEvE,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC;AAiB/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,UAAU,CACxB,CAAC,SAAS,YAAY,EACtB,SAAS,SAAS,YAAY,EAE9B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,OAAO,GACd;IAAE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CA8CxD"}
@@ -0,0 +1,83 @@
1
+ import { booleanColumnFromArray, float64ColumnFromArray, stringColumnFromArray, withColumnAppended, withColumnsSelected, } from '../../columnar/index.js';
2
+ /** Builds the single output column from the reducer results + inferred kind. */
3
+ function buildScalarColumn(kind, values) {
4
+ switch (kind) {
5
+ case 'number':
6
+ return float64ColumnFromArray(values);
7
+ case 'boolean':
8
+ return booleanColumnFromArray(values);
9
+ case 'string':
10
+ return stringColumnFromArray(values);
11
+ }
12
+ }
13
+ /**
14
+ * **Step 4 — column-native `collapse` (extracted operator).** Reduces
15
+ * the `keys` columns into a single `output` column, one value per row,
16
+ * reading **only the keyed columns** off the store (storage-agnostic
17
+ * `read(i)`) — no `series.events` materialization, no per-row `Event`.
18
+ * The kept columns + the key axis pass through by reference.
19
+ *
20
+ * Unlike the pure folds (`cumulative`, `diff`, `shift`), `collapse`
21
+ * still calls the user reducer per row over a small `{ key: value }`
22
+ * object, so the win is the more modest "read only the keyed columns +
23
+ * drop the Event allocation + share the kept columns" rather than a
24
+ * full vectorization — but the materialization tax is still removed.
25
+ *
26
+ * Semantics (matching the row path):
27
+ * - the reducer receives the keyed columns' values for the row
28
+ * (`undefined` for a missing cell), and returns a `ScalarValue`;
29
+ * - the output column's kind is inferred from the **first row's**
30
+ * result (`number` / `boolean` / else `string`) — an empty series
31
+ * yields a `string` output column, matching the old `nextEvents[0]?`
32
+ * inference;
33
+ * - **`append: false`** (default) drops the keyed columns and appends
34
+ * `output`; **`append: true`** keeps every value column and appends
35
+ * `output`.
36
+ *
37
+ * Returns the reshaped store + the output schema; the result-schema
38
+ * cast is the single trust boundary, and the `TimeSeries.collapse`
39
+ * method wraps the store via `#fromTrustedStore`. An `output` name
40
+ * that collides with a kept column throws via `withColumnAppended`.
41
+ *
42
+ * NB: `buildScalarColumn` overlaps `fillOp`'s `buildFilledColumn` and
43
+ * `mapOp`'s `columnFromValuesByKind` — three callers now; a shared
44
+ * `columnFromValuesByKind` helper is the flagged follow-up.
45
+ */
46
+ export function collapseOp(store, schema, keys, output, reducer, append) {
47
+ const n = store.length;
48
+ const keyedCols = keys.map((k) => store.columns.get(k));
49
+ const out = new Array(n);
50
+ for (let i = 0; i < n; i += 1) {
51
+ const values = {};
52
+ for (let j = 0; j < keys.length; j += 1) {
53
+ values[keys[j]] = keyedCols[j].read(i);
54
+ }
55
+ out[i] = reducer(values);
56
+ }
57
+ // Output kind from the first row's result (matches the old
58
+ // `typeof nextEvents[0]?.get(output)` inference; empty ⇒ 'string').
59
+ const first = n > 0 ? out[0] : undefined;
60
+ const outKind = typeof first === 'number'
61
+ ? 'number'
62
+ : typeof first === 'boolean'
63
+ ? 'boolean'
64
+ : 'string';
65
+ const outColumn = buildScalarColumn(outKind, out);
66
+ const keptDefs = append
67
+ ? schema.slice(1)
68
+ : schema.slice(1).filter((c) => !keys.includes(c.name));
69
+ const keptNames = keptDefs.map((c) => c.name);
70
+ // key + kept value columns (zero-copy), then append the output column.
71
+ const selected = withColumnsSelected(store, keptNames);
72
+ const result = withColumnAppended(selected, output, outColumn);
73
+ const outSchema = Object.freeze([
74
+ schema[0],
75
+ ...keptDefs,
76
+ { name: output, kind: outKind },
77
+ ]);
78
+ return {
79
+ store: result,
80
+ schema: outSchema,
81
+ };
82
+ }
83
+ //# sourceMappingURL=collapse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapse.js","sourceRoot":"","sources":["../../../src/batch/operators/collapse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type ColumnarStore } from '../../columnar/index.js';
2
+ import type { SeriesSchema } from '../../schema/index.js';
3
+ /**
4
+ * A cumulative accumulator — a built-in name or a custom fold
5
+ * `(acc, value) => next`. Mirrors `TimeSeries.cumulative`'s spec values.
6
+ */
7
+ export type CumulativeReducer = 'sum' | 'max' | 'min' | 'count' | ((acc: number, value: number) => number);
8
+ /**
9
+ * **Step 4 — column-native `cumulative` (extracted operator).** Running
10
+ * accumulation per target column, computed straight off the columnar store:
11
+ * read each target's cells (storage-agnostic `read(i)`), fold a running
12
+ * accumulator, replace the column — no `series.events` materialization, no
13
+ * per-row `Event`. Non-target columns + the key axis pass through untouched
14
+ * (`withColumnReplaced` references the unchanged columns + keys zero-copy).
15
+ *
16
+ * Matches the row path's semantics exactly: a defined numeric cell updates
17
+ * the accumulator; a missing / undefined cell **carries** the current
18
+ * accumulator (does not reset it), and the output is `undefined` only until
19
+ * the first defined value (`float64ColumnFromArray` derives validity from the
20
+ * `undefined`s). A stored NaN is a defined number — applied, matching the old
21
+ * `typeof raw === 'number'` check.
22
+ *
23
+ * Returns the reshaped store + the output schema (targets widened to optional
24
+ * `number`). The result-schema cast is the single trust boundary; the
25
+ * `TimeSeries.cumulative` method wraps the store via `#fromTrustedStore`.
26
+ *
27
+ * A non-numeric target name is unreachable through the typed surface
28
+ * (`cumulative<Targets extends NumericColumnNameForSchema<S>>`); it can only
29
+ * arrive by defeating that constraint (`(series as any).cumulative(...)`).
30
+ * On such a type-illegal input the all-`undefined` replacement column is
31
+ * `kind: 'number'`, so `withColumnReplaced`'s kind guard throws a `RangeError`
32
+ * naming the column. This is a deliberate improvement over the old events path,
33
+ * which silently overwrote the column with all-`undefined` — fail-fast beats
34
+ * silent corruption on input the type already forbids.
35
+ */
36
+ export declare function cumulativeOp<S extends SeriesSchema, OutSchema extends SeriesSchema>(store: ColumnarStore<S>, schema: S, spec: Readonly<Record<string, CumulativeReducer>>): {
37
+ store: ColumnarStore<OutSchema>;
38
+ schema: OutSchema;
39
+ };
40
+ //# sourceMappingURL=cumulative.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cumulative.d.ts","sourceRoot":"","sources":["../../../src/batch/operators/cumulative.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAInB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GACzB,KAAK,GACL,KAAK,GACL,KAAK,GACL,OAAO,GACP,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAoB7C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,YAAY,EACtB,SAAS,SAAS,YAAY,EAE9B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,GAChD;IAAE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAgCxD"}
@@ -0,0 +1,74 @@
1
+ import { float64ColumnFromArray, withColumnReplaced, } from '../../columnar/index.js';
2
+ function buildApply(reducer) {
3
+ if (typeof reducer === 'function') {
4
+ return (acc, v) => (acc === undefined ? v : reducer(acc, v));
5
+ }
6
+ switch (reducer) {
7
+ case 'sum':
8
+ return (acc, v) => (acc ?? 0) + v;
9
+ case 'count':
10
+ return (acc) => (acc ?? 0) + 1;
11
+ case 'max':
12
+ return (acc, v) => (acc === undefined || v > acc ? v : acc);
13
+ case 'min':
14
+ return (acc, v) => (acc === undefined || v < acc ? v : acc);
15
+ }
16
+ }
17
+ /**
18
+ * **Step 4 — column-native `cumulative` (extracted operator).** Running
19
+ * accumulation per target column, computed straight off the columnar store:
20
+ * read each target's cells (storage-agnostic `read(i)`), fold a running
21
+ * accumulator, replace the column — no `series.events` materialization, no
22
+ * per-row `Event`. Non-target columns + the key axis pass through untouched
23
+ * (`withColumnReplaced` references the unchanged columns + keys zero-copy).
24
+ *
25
+ * Matches the row path's semantics exactly: a defined numeric cell updates
26
+ * the accumulator; a missing / undefined cell **carries** the current
27
+ * accumulator (does not reset it), and the output is `undefined` only until
28
+ * the first defined value (`float64ColumnFromArray` derives validity from the
29
+ * `undefined`s). A stored NaN is a defined number — applied, matching the old
30
+ * `typeof raw === 'number'` check.
31
+ *
32
+ * Returns the reshaped store + the output schema (targets widened to optional
33
+ * `number`). The result-schema cast is the single trust boundary; the
34
+ * `TimeSeries.cumulative` method wraps the store via `#fromTrustedStore`.
35
+ *
36
+ * A non-numeric target name is unreachable through the typed surface
37
+ * (`cumulative<Targets extends NumericColumnNameForSchema<S>>`); it can only
38
+ * arrive by defeating that constraint (`(series as any).cumulative(...)`).
39
+ * On such a type-illegal input the all-`undefined` replacement column is
40
+ * `kind: 'number'`, so `withColumnReplaced`'s kind guard throws a `RangeError`
41
+ * naming the column. This is a deliberate improvement over the old events path,
42
+ * which silently overwrote the column with all-`undefined` — fail-fast beats
43
+ * silent corruption on input the type already forbids.
44
+ */
45
+ export function cumulativeOp(store, schema, spec) {
46
+ const entries = Object.entries(spec);
47
+ if (entries.length === 0) {
48
+ throw new Error('cumulative() requires at least one column');
49
+ }
50
+ const targetSet = new Set(entries.map(([name]) => name));
51
+ const outSchema = Object.freeze(schema.map((col, i) => i === 0 || !targetSet.has(col.name)
52
+ ? col
53
+ : { ...col, kind: 'number', required: false }));
54
+ const n = store.length;
55
+ let result = store;
56
+ for (const [name, reducer] of entries) {
57
+ const col = store.columns.get(name);
58
+ const apply = buildApply(reducer);
59
+ const out = new Array(n);
60
+ let acc;
61
+ for (let i = 0; i < n; i += 1) {
62
+ const raw = col.read(i);
63
+ if (typeof raw === 'number')
64
+ acc = apply(acc, raw);
65
+ out[i] = acc;
66
+ }
67
+ result = withColumnReplaced(result, name, float64ColumnFromArray(out));
68
+ }
69
+ return {
70
+ store: result,
71
+ schema: outSchema,
72
+ };
73
+ }
74
+ //# sourceMappingURL=cumulative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cumulative.js","sourceRoot":"","sources":["../../../src/batch/operators/cumulative.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AAcjC,SAAS,UAAU,CACjB,OAA0B;IAE1B,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,KAAK;YACR,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACpC,KAAK,OAAO;YACV,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,KAAK,KAAK;YACR,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC9D,KAAK,KAAK;YACR,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,YAAY,CAI1B,KAAuB,EACvB,MAAS,EACT,IAAiD;IAEjD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAC7B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CACpB,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;QACjC,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,QAAiB,EAAE,QAAQ,EAAE,KAAc,EAAE,CAClE,CACsB,CAAC;IAE1B,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,IAAI,MAAM,GAAG,KAA+C,CAAC;IAC7D,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC;QACtC,MAAM,GAAG,GAAW,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,GAAG,GAA2B,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,GAAuB,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAuB,CAAC;YAC9C,IAAI,OAAO,GAAG,KAAK,QAAQ;gBAAE,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACnD,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;QACf,CAAC;QACD,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,IAAI,EAAE,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,OAAO;QACL,KAAK,EAAE,MAA6C;QACpD,MAAM,EAAE,SAAS;KAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { type ColumnarStore } from '../../columnar/index.js';
2
+ import type { SeriesSchema } from '../../schema/index.js';
3
+ /**
4
+ * Successive-difference family selector. `diff` is the raw delta
5
+ * `curr - prev`; `rate` divides by the elapsed seconds between the
6
+ * two rows' begins; `pctChange` divides by the previous value.
7
+ */
8
+ export type DiffRateMode = 'diff' | 'rate' | 'pctChange';
9
+ /**
10
+ * **Step 4 — column-native `diff` / `rate` / `pctChange` (extracted
11
+ * operator).** Successive differences per target column, computed
12
+ * straight off the columnar store: read each target's cells
13
+ * (storage-agnostic `read(i)`), fold the per-row difference, replace
14
+ * the column — no `series.events` materialization, no per-row
15
+ * `Event`. Non-target columns + the key axis pass through untouched
16
+ * (`withColumnReplaced` references the unchanged columns + keys
17
+ * zero-copy).
18
+ *
19
+ * Matches the row path's semantics exactly:
20
+ * - Row 0 has no predecessor, so each target is `undefined` there.
21
+ * - Row `i ≥ 1`: with both `prev` and `curr` defined numbers,
22
+ * `delta = curr - prev`; `diff` emits `delta`, `rate` emits
23
+ * `delta / dt` (`dt` = `(begin[i] − begin[i−1]) / 1000` seconds,
24
+ * `undefined` when `dt === 0`), `pctChange` emits `delta / prev`
25
+ * (`undefined` when `prev === 0`). If either side is missing /
26
+ * non-numeric, the output is `undefined`. A stored `NaN` is a
27
+ * defined number (`typeof raw === 'number'`), so it participates.
28
+ *
29
+ * **`drop`.** `drop: false` (default) keeps the predecessor-less
30
+ * first row (its targets `undefined`, its other columns + key
31
+ * intact). `drop: true` removes that row entirely — every column
32
+ * **and** the key slice to `[1, n)` via `withRowRange`, the row-range
33
+ * substrate built for exactly this. The full-length result is built
34
+ * first (the `drop: false` shape); `drop: true` just appends the
35
+ * trailing one-row slice, so the two paths share all the fold logic.
36
+ *
37
+ * Returns the reshaped store + the output schema (targets widened to
38
+ * optional `number`). The result-schema cast is the single trust
39
+ * boundary; the `TimeSeries` method wraps the store via
40
+ * `#fromTrustedStore`.
41
+ *
42
+ * A non-numeric target name is unreachable through the typed surface
43
+ * (`NumericColumnNameForSchema<S>`); defeating that constraint makes
44
+ * the all-`undefined` replacement column `kind: 'number'`, so
45
+ * `withColumnReplaced`'s kind guard throws a `RangeError` naming the
46
+ * column — fail-fast over the old path's silent all-`undefined`.
47
+ */
48
+ export declare function diffRateOp<S extends SeriesSchema, OutSchema extends SeriesSchema>(store: ColumnarStore<S>, schema: S, mode: DiffRateMode, cols: readonly string[], drop: boolean): {
49
+ store: ColumnarStore<OutSchema>;
50
+ schema: OutSchema;
51
+ };
52
+ //# sourceMappingURL=diff-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-rate.d.ts","sourceRoot":"","sources":["../../../src/batch/operators/diff-rate.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAKnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,UAAU,CACxB,CAAC,SAAS,YAAY,EACtB,SAAS,SAAS,YAAY,EAE9B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE,OAAO,GACZ;IAAE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CA8DxD"}
@@ -0,0 +1,99 @@
1
+ import { float64ColumnFromArray, withColumnReplaced, withRowRange, } from '../../columnar/index.js';
2
+ /**
3
+ * **Step 4 — column-native `diff` / `rate` / `pctChange` (extracted
4
+ * operator).** Successive differences per target column, computed
5
+ * straight off the columnar store: read each target's cells
6
+ * (storage-agnostic `read(i)`), fold the per-row difference, replace
7
+ * the column — no `series.events` materialization, no per-row
8
+ * `Event`. Non-target columns + the key axis pass through untouched
9
+ * (`withColumnReplaced` references the unchanged columns + keys
10
+ * zero-copy).
11
+ *
12
+ * Matches the row path's semantics exactly:
13
+ * - Row 0 has no predecessor, so each target is `undefined` there.
14
+ * - Row `i ≥ 1`: with both `prev` and `curr` defined numbers,
15
+ * `delta = curr - prev`; `diff` emits `delta`, `rate` emits
16
+ * `delta / dt` (`dt` = `(begin[i] − begin[i−1]) / 1000` seconds,
17
+ * `undefined` when `dt === 0`), `pctChange` emits `delta / prev`
18
+ * (`undefined` when `prev === 0`). If either side is missing /
19
+ * non-numeric, the output is `undefined`. A stored `NaN` is a
20
+ * defined number (`typeof raw === 'number'`), so it participates.
21
+ *
22
+ * **`drop`.** `drop: false` (default) keeps the predecessor-less
23
+ * first row (its targets `undefined`, its other columns + key
24
+ * intact). `drop: true` removes that row entirely — every column
25
+ * **and** the key slice to `[1, n)` via `withRowRange`, the row-range
26
+ * substrate built for exactly this. The full-length result is built
27
+ * first (the `drop: false` shape); `drop: true` just appends the
28
+ * trailing one-row slice, so the two paths share all the fold logic.
29
+ *
30
+ * Returns the reshaped store + the output schema (targets widened to
31
+ * optional `number`). The result-schema cast is the single trust
32
+ * boundary; the `TimeSeries` method wraps the store via
33
+ * `#fromTrustedStore`.
34
+ *
35
+ * A non-numeric target name is unreachable through the typed surface
36
+ * (`NumericColumnNameForSchema<S>`); defeating that constraint makes
37
+ * the all-`undefined` replacement column `kind: 'number'`, so
38
+ * `withColumnReplaced`'s kind guard throws a `RangeError` naming the
39
+ * column — fail-fast over the old path's silent all-`undefined`.
40
+ */
41
+ export function diffRateOp(store, schema, mode, cols, drop) {
42
+ if (cols.length === 0) {
43
+ throw new Error(`${mode}() requires at least one column name`);
44
+ }
45
+ const targetSet = new Set(cols);
46
+ const outSchema = Object.freeze(schema.map((col, i) => i === 0 || !targetSet.has(col.name)
47
+ ? col
48
+ : { ...col, kind: 'number', required: false }));
49
+ const n = store.length;
50
+ // For `rate`, the elapsed-seconds divisor depends only on the row
51
+ // index, not the target — precompute it once rather than per
52
+ // (target, row). dt[0] is unused (row 0 has no predecessor).
53
+ let dt;
54
+ if (mode === 'rate' && n > 0) {
55
+ dt = new Float64Array(n);
56
+ for (let i = 1; i < n; i += 1) {
57
+ dt[i] = (store.beginAt(i) - store.beginAt(i - 1)) / 1000;
58
+ }
59
+ }
60
+ let result = store;
61
+ for (const name of cols) {
62
+ const col = store.columns.get(name);
63
+ const out = new Array(n);
64
+ if (n > 0)
65
+ out[0] = undefined; // first row: no predecessor
66
+ for (let i = 1; i < n; i += 1) {
67
+ const prev = col.read(i - 1);
68
+ const curr = col.read(i);
69
+ if (typeof curr === 'number' && typeof prev === 'number') {
70
+ const delta = curr - prev;
71
+ if (mode === 'pctChange') {
72
+ out[i] = prev !== 0 ? delta / prev : undefined;
73
+ }
74
+ else if (mode === 'rate') {
75
+ const d = dt[i];
76
+ out[i] = d !== 0 ? delta / d : undefined;
77
+ }
78
+ else {
79
+ out[i] = delta;
80
+ }
81
+ }
82
+ else {
83
+ out[i] = undefined;
84
+ }
85
+ }
86
+ result = withColumnReplaced(result, name, float64ColumnFromArray(out));
87
+ }
88
+ // drop:true removes the predecessor-less first row from every column
89
+ // + the key. (n === 0 has no row to drop; the guard keeps the empty
90
+ // store untouched rather than slicing [1, 0).)
91
+ if (drop && n > 0) {
92
+ result = withRowRange(result, 1, n);
93
+ }
94
+ return {
95
+ store: result,
96
+ schema: outSchema,
97
+ };
98
+ }
99
+ //# sourceMappingURL=diff-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-rate.js","sourceRoot":"","sources":["../../../src/batch/operators/diff-rate.ts"],"names":[],"mappings":"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"}