@pond-ts/react 0.23.0 → 0.25.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 +96 -1
- package/README.md +18 -14
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,9 @@ 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.25.0...HEAD
|
|
11
|
+
[0.25.0]: https://github.com/pjm17971/pond-ts/compare/v0.24.0...v0.25.0
|
|
12
|
+
[0.24.0]: https://github.com/pjm17971/pond-ts/compare/v0.23.0...v0.24.0
|
|
11
13
|
[0.23.0]: https://github.com/pjm17971/pond-ts/compare/v0.22.0...v0.23.0
|
|
12
14
|
[0.22.0]: https://github.com/pjm17971/pond-ts/compare/v0.21.0...v0.22.0
|
|
13
15
|
[0.21.0]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...v0.21.0
|
|
@@ -17,6 +19,99 @@ type-level changes; patch bumps are strictly additive.
|
|
|
17
19
|
|
|
18
20
|
## [Unreleased]
|
|
19
21
|
|
|
22
|
+
## [0.25.0] — 2026-06-15
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Reducers now treat non-finite numerics (`NaN` / `±Infinity`) as missing —
|
|
27
|
+
they are skipped — uniformly across every built-in reducer and all four
|
|
28
|
+
execution paths (`reduce`, the columnar fast path, `aggregate`/bucket, and
|
|
29
|
+
`rolling`/live).** Previously the paths disagreed on non-finite input: e.g.
|
|
30
|
+
`min`/`max` returned a position-dependent wrong extreme on the batch/columnar
|
|
31
|
+
paths but the true extreme on aggregate/rolling; `sum`/`avg` propagated
|
|
32
|
+
`NaN`. Non-finite can't enter via the row API (intake rejects it) — it only
|
|
33
|
+
arises inside computed columns (`cumulative` overflow, `diff`/`rate`
|
|
34
|
+
overflow, `collapse`, trusted construction) — so this only changes results
|
|
35
|
+
for those degenerate values, and makes every path agree. The three-layer
|
|
36
|
+
contract: **intake** stays strict (rejects non-finite), **computed writers**
|
|
37
|
+
stay permissive (pack honest non-finite), **reducers** are robust (skip it).
|
|
38
|
+
A standing parity-matrix test now pins all paths together. See
|
|
39
|
+
`docs/notes/reducer-nan-policy.md`. This also resolves the `aggregate('stdev')`
|
|
40
|
+
divergence class and the `min`/`max` NaN-laundering bug.
|
|
41
|
+
- Internal: `Float64Column` gained an `allFinite` fast-path flag (data-derived
|
|
42
|
+
at construction, conservative-by-default) so reducers skip the per-element
|
|
43
|
+
finite check on provably-finite columns — keeping the policy's cost off the
|
|
44
|
+
hot path (min/max/count stay at their pre-policy speed).
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **`rolling(window, { x: 'stdev' })` is now numerically stable.** It was the
|
|
49
|
+
last batch stdev path still on the one-pass `Σx²/n − mean²`, which cancels
|
|
50
|
+
catastrophically on near-equal large values (`[1e10, 1e10+1, …]` → `0`
|
|
51
|
+
instead of ≈1.118, or a negative variance → `NaN`) and drifts on trending
|
|
52
|
+
data (cumulative distance, elevation). It now uses Welford's online variance
|
|
53
|
+
with an order-independent **delete** — deviation-space, so no cancellation,
|
|
54
|
+
and removal **by value**, which keeps it correct under the live layer's
|
|
55
|
+
`reorder`-mode eviction (a positional/FIFO remove would have broken it; the
|
|
56
|
+
documented "stdev is reorder-safe" contract is preserved). Rolling-stdev
|
|
57
|
+
values shift in the last ULPs (now correct); the path stays O(1) and within
|
|
58
|
+
run-noise of the old one-pass, and a single-element window now reports exactly
|
|
59
|
+
`0` at any magnitude. Like any subtractive sliding variance, evicting an
|
|
60
|
+
outlier far outside the residual spread loses precision — negligible until the
|
|
61
|
+
evicted point is ~1e7–1e8× the residual stdev, far beyond realistic data.
|
|
62
|
+
- A standing differential-fuzz parity suite now pins every built-in reducer's
|
|
63
|
+
execution paths (columnar fast path vs `bucket` vs `rolling`, and the FIFO
|
|
64
|
+
sliding window vs a from-scratch recompute) against silent drift across
|
|
65
|
+
randomized magnitudes and window sizes — the class of bug behind the stdev
|
|
66
|
+
and `min`/`max` divergences.
|
|
67
|
+
|
|
68
|
+
## [0.24.0] — 2026-06-14
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- **`TimeSeries.timeRange()` is now a columnar key-axis read instead of a
|
|
73
|
+
reduce over materialized events.** Behavior is unchanged, but the old
|
|
74
|
+
implementation materialized every `Event` on its first call — and because
|
|
75
|
+
`aggregate()` defaults its `range` to `series.timeRange()`, a one-shot
|
|
76
|
+
`aggregate()` paid full event materialization before the columnar fast
|
|
77
|
+
path could run, erasing the win. The new path reads the key column's
|
|
78
|
+
begin/end axis directly: O(1) for time-keyed series, a typed-array scan
|
|
79
|
+
for range/interval-keyed series, with no event materialization. Measured
|
|
80
|
+
on 1M rows: `timeRange()` itself ~407 ms → ~0.002 ms (time-keyed); cold
|
|
81
|
+
`aggregate()` with a defaulted range ~387 ms → ~6 ms (~63×). Every
|
|
82
|
+
`timeRange()` / `overlaps` / `contains` / `intersection` caller benefits.
|
|
83
|
+
(Audit v2 §3.3.)
|
|
84
|
+
- **`aggregate()` now takes the columnar fast path when a mapping mixes
|
|
85
|
+
numeric reducers with `first` / `last`.** Previously a single `first` or
|
|
86
|
+
`last` column (they have no numeric `reduceColumn`) bailed the entire call
|
|
87
|
+
to the row path. They now qualify via a boundary scan — the first/last
|
|
88
|
+
_defined_ cell, on any column kind. Behavior is unchanged. The big
|
|
89
|
+
beneficiary is **partitioned `aggregate`**, which auto-injects a `'first'`
|
|
90
|
+
reducer for the partition column and so was excluded from the fast path on
|
|
91
|
+
every call (audit v2 §3.2/§3.3). Measured on 1M rows, flat
|
|
92
|
+
`{ cpu: 'avg', host: 'first' }`: ~37.7 ms → ~4.8 ms (~7.8×); the
|
|
93
|
+
pure-numeric path is unchanged. (The remaining `partitionBy` materialization
|
|
94
|
+
cost is addressed separately by the columnar `partitionBy` split.)
|
|
95
|
+
- **`partitionBy(...)` now splits the columnar store directly instead of
|
|
96
|
+
materializing events.** `collect()` / the per-partition sugar methods
|
|
97
|
+
(`fill` / `diff` / `rolling` / …) and `toMap()` previously walked
|
|
98
|
+
`this.events` to bucket rows, then rebuilt each partition via `fromEvents`
|
|
99
|
+
(re-validating + re-packing) — silently re-paying the event-materialization
|
|
100
|
+
tax the columnar wave removed, and making `partitionBy(host).fill().collect()`
|
|
101
|
+
the #1 batch hotspot. They now group row indices off the store and gather
|
|
102
|
+
each partition via a zero-materialization columnar selection. Behavior is
|
|
103
|
+
unchanged (partition order, the `' undefined'` missing-key bucket, composite
|
|
104
|
+
keys, and declared `groups` all preserved). Measured on 100k rows / 64
|
|
105
|
+
partitions: `toMap()` ~389 → ~25 ns/row (~15×, no event materialization at
|
|
106
|
+
all); `diff().collect()` ~2×; `fill(hold).collect()` ~1.7× (the residual is
|
|
107
|
+
`TimeSeries.concat` still materializing to re-sort — a separate follow-up).
|
|
108
|
+
Declared-`groups` membership is validated by the same columnar scan, so that
|
|
109
|
+
path is materialization-free too (~331 → ~33 ns/row). **Behavior note:**
|
|
110
|
+
per-partition sub-series from `toMap()` / `apply()` now lazily materialize
|
|
111
|
+
their own `Event` objects rather than reusing the source's instances — cell
|
|
112
|
+
values are identical; only object identity differs (`collect()`, which
|
|
113
|
+
returns the source unchanged, is unaffected). (Audit v2 §3.2.)
|
|
114
|
+
|
|
20
115
|
## [0.23.0] — 2026-06-13
|
|
21
116
|
|
|
22
117
|
### Added
|
package/README.md
CHANGED
|
@@ -152,20 +152,24 @@ const points = series.sample({ reservoir: { size: 500 } }).toRows();
|
|
|
152
152
|
|
|
153
153
|
## Performance
|
|
154
154
|
|
|
155
|
-
pond-ts is
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
|
162
|
-
|
|
|
163
|
-
| **
|
|
164
|
-
| **
|
|
165
|
-
| **
|
|
166
|
-
| **Statistics** |
|
|
167
|
-
| **
|
|
168
|
-
| **
|
|
155
|
+
pond-ts is faster on **every** comparable operation, with no regressions —
|
|
156
|
+
a **~17x** geometric-mean speedup across the measurable ops, plus a handful
|
|
157
|
+
of transforms (`select` / `rename`) that are **effectively instant** (O(1)
|
|
158
|
+
column rebinds, below the timer's resolution). The advantage grows with data
|
|
159
|
+
size.
|
|
160
|
+
|
|
161
|
+
| Category | Speedup (N=16k) | Notes |
|
|
162
|
+
| ----------------- | ---------------------------------------------------- | --------------------------------------------- |
|
|
163
|
+
| **Rate** | ~120x | Single columnar walk vs Pipeline |
|
|
164
|
+
| **Fill** | 77–87x | Single columnar pass vs Pipeline per strategy |
|
|
165
|
+
| **Aggregation** | 57–82x | O(N+B) bucketing vs O(N×B) Pipeline |
|
|
166
|
+
| **Statistics** | 18–80x | Typed-array reduce vs ImmutableJS iteration |
|
|
167
|
+
| **Alignment** | 42x | Forward cursor vs repeated binary search |
|
|
168
|
+
| **Construction** | 13x | Columnar intake vs ImmutableJS wrapping |
|
|
169
|
+
| **Chained** | 8x | Derived constructors vs per-step Pipeline |
|
|
170
|
+
| **Transforms** | `select`/`rename` instant; `collapse` 30x; `map` ~4x | Column reshapes vs Pipeline |
|
|
171
|
+
| **Event access** | 6x | Array indexing vs ImmutableJS `get()` |
|
|
172
|
+
| **Serialization** | 4x | Lightweight columnar representation |
|
|
169
173
|
|
|
170
174
|
See the [full benchmark results](website/docs/reference/benchmarks.mdx)
|
|
171
175
|
for detailed numbers. Run locally:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "React hooks for pond-ts live time series",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"test:runtime": "vitest run"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"pond-ts": "^0.
|
|
36
|
+
"pond-ts": "^0.25.0",
|
|
37
37
|
"react": "^18.0.0 || ^19.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|