pond-ts 0.20.0 → 0.22.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.
- package/CHANGELOG.md +104 -1
- package/dist/batch/aggregate-columns.d.ts +29 -0
- package/dist/batch/aggregate-columns.d.ts.map +1 -1
- package/dist/batch/aggregate-columns.js +63 -0
- package/dist/batch/aggregate-columns.js.map +1 -1
- package/dist/batch/operators/collapse.d.ts +42 -0
- package/dist/batch/operators/collapse.d.ts.map +1 -0
- package/dist/batch/operators/collapse.js +83 -0
- package/dist/batch/operators/collapse.js.map +1 -0
- package/dist/batch/operators/cumulative.d.ts +40 -0
- package/dist/batch/operators/cumulative.d.ts.map +1 -0
- package/dist/batch/operators/cumulative.js +74 -0
- package/dist/batch/operators/cumulative.js.map +1 -0
- package/dist/batch/operators/diff-rate.d.ts +52 -0
- package/dist/batch/operators/diff-rate.d.ts.map +1 -0
- package/dist/batch/operators/diff-rate.js +99 -0
- package/dist/batch/operators/diff-rate.js.map +1 -0
- package/dist/batch/operators/fill.d.ts +66 -0
- package/dist/batch/operators/fill.d.ts.map +1 -0
- package/dist/batch/operators/fill.js +219 -0
- package/dist/batch/operators/fill.js.map +1 -0
- package/dist/batch/operators/map.d.ts +56 -0
- package/dist/batch/operators/map.d.ts.map +1 -0
- package/dist/batch/operators/map.js +109 -0
- package/dist/batch/operators/map.js.map +1 -0
- package/dist/batch/operators/shift.d.ts +31 -0
- package/dist/batch/operators/shift.d.ts.map +1 -0
- package/dist/batch/operators/shift.js +56 -0
- package/dist/batch/operators/shift.js.map +1 -0
- package/dist/batch/time-series.d.ts +36 -5
- package/dist/batch/time-series.d.ts.map +1 -1
- package/dist/batch/time-series.js +244 -387
- package/dist/batch/time-series.js.map +1 -1
- package/dist/columnar/index.d.ts +1 -1
- package/dist/columnar/index.d.ts.map +1 -1
- package/dist/columnar/index.js +1 -1
- package/dist/columnar/index.js.map +1 -1
- package/dist/columnar/view.d.ts +57 -1
- package/dist/columnar/view.d.ts.map +1 -1
- package/dist/columnar/view.js +88 -0
- package/dist/columnar/view.js.map +1 -1
- package/dist/reducers/stdev.d.ts.map +1 -1
- package/dist/reducers/stdev.js +81 -59
- package/dist/reducers/stdev.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,116 @@ 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.
|
|
10
|
+
[Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.22.0...HEAD
|
|
11
|
+
[0.22.0]: https://github.com/pjm17971/pond-ts/compare/v0.21.0...v0.22.0
|
|
12
|
+
[0.21.0]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...v0.21.0
|
|
11
13
|
[0.20.0]: https://github.com/pjm17971/pond-ts/compare/v0.19.0...v0.20.0
|
|
12
14
|
[0.19.0]: https://github.com/pjm17971/pond-ts/compare/v0.18.0...v0.19.0
|
|
13
15
|
[0.18.0]: https://github.com/pjm17971/pond-ts/compare/v0.17.1...v0.18.0
|
|
14
16
|
|
|
15
17
|
## [Unreleased]
|
|
16
18
|
|
|
19
|
+
## [0.22.0] — 2026-06-12
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`asTime` / `asTimeRange` / `asInterval` are now column-native.** They
|
|
24
|
+
reinterpret the key's kind (a "rekey") straight off the existing key's
|
|
25
|
+
`begin` / `end` buffers instead of materializing events — value columns pass
|
|
26
|
+
through by reference. `asTimeRange` and `asTime` with `begin` / `end` reuse
|
|
27
|
+
the key buffer zero-copy (≈ **9×** faster on a build → rekey → read pipeline);
|
|
28
|
+
`asTime({ at: 'center' })` adds one midpoint pass; `asInterval` builds the
|
|
29
|
+
label column (string → `StringColumn`, number → `Float64Column`, inferred
|
|
30
|
+
from the first label and required consistent across rows). `asTime` with
|
|
31
|
+
`center` / `end` throws if anchoring a source with overlapping extents would
|
|
32
|
+
produce a non-monotonic time axis (preserving the prior validation — `begin`
|
|
33
|
+
is always sorted and is exempt).
|
|
34
|
+
- **Breaking: `asInterval`'s label function now receives the interval's
|
|
35
|
+
`TimeRange` (its `[begin, end]` extent) and index — not the whole `Event`.**
|
|
36
|
+
The canonical form is unchanged: `series.asInterval(range => range.begin())`
|
|
37
|
+
works exactly as before (both `Event` and `TimeRange` expose `begin()` /
|
|
38
|
+
`end()`). Only a label fn that read a _value column_ off the event (e.g.
|
|
39
|
+
`event => event.get('label')`) needs rewriting — compute the label before
|
|
40
|
+
`asInterval`, or derive it from the extent. The constant form
|
|
41
|
+
(`asInterval('bucket')` / `asInterval(42)`) is unaffected. (Pre-1.0 minor;
|
|
42
|
+
this is the change that lets the function form stay on the columnar path.)
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **`mapColumns` rejects a non-finite numeric result at write.** A mapper on a
|
|
47
|
+
`number` column that returns `NaN` or `±Infinity` now throws a `RangeError`,
|
|
48
|
+
consistent with construction intake (which already rejects non-finite
|
|
49
|
+
numbers). Previously the value was packed into the column, where the reduce
|
|
50
|
+
fast path and the row path could disagree on the same bucket (e.g.
|
|
51
|
+
`aggregate('min')` returning a different result depending on which path ran).
|
|
52
|
+
A stored `NaN` is still a defined value the mapper sees — map it to a finite
|
|
53
|
+
number, or to `undefined` (missing), to clean it. (Closes a hole introduced
|
|
54
|
+
alongside `mapColumns` in 0.21.0.)
|
|
55
|
+
- **`aggregate('stdev')` is now numerically stable and path-independent.** The
|
|
56
|
+
bucketed row path (`bucketState`) used a one-pass `sq/n − mean²` accumulator
|
|
57
|
+
that cancels catastrophically on near-equal large-magnitude values —
|
|
58
|
+
returning `0` (e.g. `[1e10, 1e10+1, 1e10+2, 1e10+3]` → `0` instead of
|
|
59
|
+
`≈1.118`), or a negative variance whose `sqrt` is `NaN` that the validating
|
|
60
|
+
constructor then rejected with a throw. Because the columnar fast path is
|
|
61
|
+
all-or-nothing, an unrelated mapping (e.g. a `count` over a string column)
|
|
62
|
+
could silently flip the _same_ series' stdev. All three batch paths (`reduce`,
|
|
63
|
+
`reduceColumn`, `bucketState`) now share **one Welford recurrence** — O(1) per
|
|
64
|
+
element, no buffer (so the live aggregation path that shares `bucketState`
|
|
65
|
+
stays O(1)), `m2 ≥ 0` by construction — so they agree regardless of magnitude.
|
|
66
|
+
(Even the prior two-pass `Σv/n`-then-deviations drifted ~8.7% from the true
|
|
67
|
+
value at `2^52`, where the summed mean rounds — so unifying on Welford, not
|
|
68
|
+
two-pass, was necessary.) **Correction:** 0.21.0's columnar `aggregate()` fast
|
|
69
|
+
path (#186) was described as "signature + semantics unchanged", but it did
|
|
70
|
+
change released `stdev` output for fast-path-qualifying aggregates; this fix
|
|
71
|
+
makes every path agree. (`rolling`/`smooth` stdev keep the one-pass form for
|
|
72
|
+
now — a separate, deferred item.)
|
|
73
|
+
|
|
74
|
+
## [0.21.0] — 2026-06-11
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **`TimeSeries.mapColumns({ col: (value) => newValue })`** — a per-cell column
|
|
79
|
+
value transform. The column-scoped counterpart of the event-based `map()`:
|
|
80
|
+
where `map(schema, event => newEvent)` rebuilds whole rows through an
|
|
81
|
+
arbitrary closure (and can change the schema/key), `mapColumns` transforms
|
|
82
|
+
individual columns' values in place, reading the columns directly (no
|
|
83
|
+
per-row `Event`) so it stays on the fast columnar path. Same kind in/out
|
|
84
|
+
(number→number, string→string, …), so the schema is unchanged; missing cells
|
|
85
|
+
carry (the mapper isn't called on `undefined`). ~5–6× faster than the
|
|
86
|
+
`map()` workaround on a build → transform → read pipeline.
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- **`select` / `rename` / `slice` / `cumulative` / `diff` / `rate` /
|
|
91
|
+
`pctChange` / `fill` / `shift` / `collapse` are now column-native.** They
|
|
92
|
+
reshape the columnar store directly instead of materializing events, so the
|
|
93
|
+
columnar construction win is preserved through these transforms — build →
|
|
94
|
+
transform → read pipelines run several× faster (~7–10× for `select` /
|
|
95
|
+
`rename` / `slice`; ~5–7× for the `cumulative` / `diff` / `rate` / `fill` /
|
|
96
|
+
`shift` / `collapse` folds). No API change for type-correct callers (one
|
|
97
|
+
narrow `fill` behavior change is noted under Fixed). `cumulative` / `diff` /
|
|
98
|
+
`rate` / `pctChange` / `fill` / `shift` / `collapse` are also the first
|
|
99
|
+
operators extracted into `batch/operators/` (internal refactor); `fill`
|
|
100
|
+
rebuilds only the columns it actually changes; `slice` normalizes
|
|
101
|
+
`Array.prototype.slice` semantics onto a zero-copy `withRowRange` reshape;
|
|
102
|
+
`collapse` reads only the keyed columns and passes the kept columns through
|
|
103
|
+
by reference.
|
|
104
|
+
|
|
105
|
+
### Fixed
|
|
106
|
+
|
|
107
|
+
- **`rename` now rejects target-name collisions** (e.g. renaming `a` → `b`
|
|
108
|
+
when `b` already exists) with a clear error, instead of silently producing a
|
|
109
|
+
duplicate-named schema. Also fixes a prototype-chain bug where a column named
|
|
110
|
+
`toString` (or another `Object.prototype` member) could be corrupted during
|
|
111
|
+
a rename.
|
|
112
|
+
- **`fill` now throws on a kind-mismatched literal** (e.g.
|
|
113
|
+
`fill({ value: 'banana' })` where `value` is numeric — type-allowed because
|
|
114
|
+
mapping values are the broad `FillStrategy | ScalarValue`) with a clear
|
|
115
|
+
`RangeError` naming the column, instead of silently producing an
|
|
116
|
+
internally-inconsistent series (the old events path returned the literal
|
|
117
|
+
from `.get()` while the numeric column read `NaN`). The throw is
|
|
118
|
+
gap-dependent — it only fires when the literal would actually be placed.
|
|
119
|
+
|
|
17
120
|
## [0.20.0] — 2026-06-04
|
|
18
121
|
|
|
19
122
|
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,
|
|
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;
|
|
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":"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"}
|
|
@@ -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"}
|