@pond-ts/react 0.23.0 → 0.24.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 (3) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/README.md +18 -14
  3. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,7 +7,8 @@ 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.23.0...HEAD
10
+ [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.24.0...HEAD
11
+ [0.24.0]: https://github.com/pjm17971/pond-ts/compare/v0.23.0...v0.24.0
11
12
  [0.23.0]: https://github.com/pjm17971/pond-ts/compare/v0.22.0...v0.23.0
12
13
  [0.22.0]: https://github.com/pjm17971/pond-ts/compare/v0.21.0...v0.22.0
13
14
  [0.21.0]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...v0.21.0
@@ -17,6 +18,53 @@ type-level changes; patch bumps are strictly additive.
17
18
 
18
19
  ## [Unreleased]
19
20
 
21
+ ## [0.24.0] — 2026-06-14
22
+
23
+ ### Changed
24
+
25
+ - **`TimeSeries.timeRange()` is now a columnar key-axis read instead of a
26
+ reduce over materialized events.** Behavior is unchanged, but the old
27
+ implementation materialized every `Event` on its first call — and because
28
+ `aggregate()` defaults its `range` to `series.timeRange()`, a one-shot
29
+ `aggregate()` paid full event materialization before the columnar fast
30
+ path could run, erasing the win. The new path reads the key column's
31
+ begin/end axis directly: O(1) for time-keyed series, a typed-array scan
32
+ for range/interval-keyed series, with no event materialization. Measured
33
+ on 1M rows: `timeRange()` itself ~407 ms → ~0.002 ms (time-keyed); cold
34
+ `aggregate()` with a defaulted range ~387 ms → ~6 ms (~63×). Every
35
+ `timeRange()` / `overlaps` / `contains` / `intersection` caller benefits.
36
+ (Audit v2 §3.3.)
37
+ - **`aggregate()` now takes the columnar fast path when a mapping mixes
38
+ numeric reducers with `first` / `last`.** Previously a single `first` or
39
+ `last` column (they have no numeric `reduceColumn`) bailed the entire call
40
+ to the row path. They now qualify via a boundary scan — the first/last
41
+ _defined_ cell, on any column kind. Behavior is unchanged. The big
42
+ beneficiary is **partitioned `aggregate`**, which auto-injects a `'first'`
43
+ reducer for the partition column and so was excluded from the fast path on
44
+ every call (audit v2 §3.2/§3.3). Measured on 1M rows, flat
45
+ `{ cpu: 'avg', host: 'first' }`: ~37.7 ms → ~4.8 ms (~7.8×); the
46
+ pure-numeric path is unchanged. (The remaining `partitionBy` materialization
47
+ cost is addressed separately by the columnar `partitionBy` split.)
48
+ - **`partitionBy(...)` now splits the columnar store directly instead of
49
+ materializing events.** `collect()` / the per-partition sugar methods
50
+ (`fill` / `diff` / `rolling` / …) and `toMap()` previously walked
51
+ `this.events` to bucket rows, then rebuilt each partition via `fromEvents`
52
+ (re-validating + re-packing) — silently re-paying the event-materialization
53
+ tax the columnar wave removed, and making `partitionBy(host).fill().collect()`
54
+ the #1 batch hotspot. They now group row indices off the store and gather
55
+ each partition via a zero-materialization columnar selection. Behavior is
56
+ unchanged (partition order, the `' undefined'` missing-key bucket, composite
57
+ keys, and declared `groups` all preserved). Measured on 100k rows / 64
58
+ partitions: `toMap()` ~389 → ~25 ns/row (~15×, no event materialization at
59
+ all); `diff().collect()` ~2×; `fill(hold).collect()` ~1.7× (the residual is
60
+ `TimeSeries.concat` still materializing to re-sort — a separate follow-up).
61
+ Declared-`groups` membership is validated by the same columnar scan, so that
62
+ path is materialization-free too (~331 → ~33 ns/row). **Behavior note:**
63
+ per-partition sub-series from `toMap()` / `apply()` now lazily materialize
64
+ their own `Event` objects rather than reusing the source's instances — cell
65
+ values are identical; only object identity differs (`collect()`, which
66
+ returns the source unchanged, is unaffected). (Audit v2 §3.2.)
67
+
20
68
  ## [0.23.0] — 2026-06-13
21
69
 
22
70
  ### 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 **7.6x faster** than pondjs on average across all comparable
156
- operations, with no regressions. The advantage grows with data size.
157
-
158
- | Category | Speedup (N=16k) | Notes |
159
- | ----------------- | --------------- | --------------------------------------------- |
160
- | **Aggregation** | 25–32x | O(N+B) bucketing vs O(N×B) Pipeline |
161
- | **Alignment** | 32x | Forward cursor vs repeated binary search |
162
- | **Rate/diff** | 18x | Direct array walk vs Pipeline materialization |
163
- | **Fill** | 10–11x | Single-pass vs Pipeline per strategy |
164
- | **Transforms** | 316x | Pre-validated constructor skips re-validation |
165
- | **Construction** | 7x | Plain objects vs ImmutableJS wrapping |
166
- | **Statistics** | 79x | Direct computation vs ImmutableJS iteration |
167
- | **Serialization** | 4x | Simpler internal representation |
168
- | **Event access** | 23x | Array indexing vs ImmutableJS `get()` |
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** | 7787x | Single columnar pass vs Pipeline per strategy |
165
+ | **Aggregation** | 57–82x | O(N+B) bucketing vs O(N×B) Pipeline |
166
+ | **Statistics** | 1880x | 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.23.0",
3
+ "version": "0.24.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.23.0",
36
+ "pond-ts": "^0.24.0",
37
37
  "react": "^18.0.0 || ^19.0.0"
38
38
  },
39
39
  "devDependencies": {