pond-ts 0.48.0 → 0.48.1
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 +16 -1
- package/dist/batch/time-series.js +13 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,8 @@ The `@pond-ts` packages — `pond-ts`, `@pond-ts/react`, `@pond-ts/charts`,
|
|
|
8
8
|
tag, so this file covers them all. Pre-1.0: minor bumps may include new features
|
|
9
9
|
and type-level changes; patch bumps are strictly additive.
|
|
10
10
|
|
|
11
|
-
[Unreleased]: https://github.com/pond-ts/pond/compare/v0.48.
|
|
11
|
+
[Unreleased]: https://github.com/pond-ts/pond/compare/v0.48.1...HEAD
|
|
12
|
+
[0.48.1]: https://github.com/pond-ts/pond/compare/v0.48.0...v0.48.1
|
|
12
13
|
[0.48.0]: https://github.com/pond-ts/pond/compare/v0.47.0...v0.48.0
|
|
13
14
|
[0.47.0]: https://github.com/pond-ts/pond/compare/v0.46.0...v0.47.0
|
|
14
15
|
[0.46.0]: https://github.com/pjm17971/pond-ts/compare/v0.45.0...v0.46.0
|
|
@@ -47,6 +48,20 @@ and type-level changes; patch bumps are strictly additive.
|
|
|
47
48
|
|
|
48
49
|
## [Unreleased]
|
|
49
50
|
|
|
51
|
+
## [0.48.1] — 2026-07-19
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- `smooth(column, 'loess', …)` is now numerically stable on absolute epoch-ms
|
|
56
|
+
timestamps. The local weighted regression is now conditioned on `x` centred at
|
|
57
|
+
the query point; previously the un-centred normal equations lost precision to
|
|
58
|
+
floating-point cancellation (anchors ~1.7e12 → `x²` terms ~3e24). Severity
|
|
59
|
+
scales with (anchor magnitude / window width)²: on finely-spaced series
|
|
60
|
+
(second-level cadence) the fit overshot wildly outside the data range instead
|
|
61
|
+
of smoothing, while coarser day-spaced series erred only subtly. Output is now
|
|
62
|
+
shift-invariant to the time origin. Series built from small integer anchors
|
|
63
|
+
(e.g. in unit tests) were unaffected, which is why this hid.
|
|
64
|
+
|
|
50
65
|
## [0.48.0] — 2026-07-17
|
|
51
66
|
|
|
52
67
|
### Added
|
|
@@ -187,10 +187,16 @@ function loessAt(x, anchors, values, span) {
|
|
|
187
187
|
let sumWY = 0;
|
|
188
188
|
let sumWXX = 0;
|
|
189
189
|
let sumWXY = 0;
|
|
190
|
+
// Regress in local coordinates centred on the query point (`dx = pointX - x`).
|
|
191
|
+
// Anchors are typically absolute epoch-ms (~1.7e12), so accumulating
|
|
192
|
+
// `pointX * pointX` (~3e24) into the normal equations loses all precision to
|
|
193
|
+
// floating-point cancellation and the fit overshoots wildly. Centring keeps
|
|
194
|
+
// every term small; the fitted value at the query point is then just the
|
|
195
|
+
// intercept, since `dx = 0` there.
|
|
190
196
|
for (let index = start; index < end; index++) {
|
|
191
|
-
const
|
|
197
|
+
const dx = anchors[index] - x;
|
|
192
198
|
const pointY = values[index];
|
|
193
|
-
const distance = Math.abs(
|
|
199
|
+
const distance = Math.abs(dx);
|
|
194
200
|
const ratio = distance / bandwidth;
|
|
195
201
|
const weight = ratio >= 1 ? 0 : (1 - ratio ** 3) ** 3;
|
|
196
202
|
if (weight === 0) {
|
|
@@ -198,10 +204,10 @@ function loessAt(x, anchors, values, span) {
|
|
|
198
204
|
}
|
|
199
205
|
weightedCount += 1;
|
|
200
206
|
sumW += weight;
|
|
201
|
-
sumWX += weight *
|
|
207
|
+
sumWX += weight * dx;
|
|
202
208
|
sumWY += weight * pointY;
|
|
203
|
-
sumWXX += weight *
|
|
204
|
-
sumWXY += weight *
|
|
209
|
+
sumWXX += weight * dx * dx;
|
|
210
|
+
sumWXY += weight * dx * pointY;
|
|
205
211
|
}
|
|
206
212
|
if (weightedCount === 0 || sumW === 0) {
|
|
207
213
|
return undefined;
|
|
@@ -210,9 +216,8 @@ function loessAt(x, anchors, values, span) {
|
|
|
210
216
|
if (Math.abs(denominator) < Number.EPSILON) {
|
|
211
217
|
return sumWY / sumW;
|
|
212
218
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return intercept + slope * x;
|
|
219
|
+
// Value at the query point: intercept + slope * dx, with dx = 0.
|
|
220
|
+
return (sumWY * sumWXX - sumWX * sumWXY) / denominator;
|
|
216
221
|
}
|
|
217
222
|
function lowerBound(values, target) {
|
|
218
223
|
let low = 0;
|