fastlowess-wasm 1.2.0 → 2.0.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/README.md +139 -92
- package/fastlowess_wasm.d.ts +159 -75
- package/fastlowess_wasm.js +228 -151
- package/fastlowess_wasm_bg.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ The fastest, most robust, and most feature-complete language-agnostic LOWESS (Lo
|
|
|
45
45
|
|
|
46
46
|
> [!NOTE]
|
|
47
47
|
>
|
|
48
|
-
> Currently available for R, Python, Rust, Julia, Node.js, WebAssembly, and C++. See [
|
|
48
|
+
> Currently available for R, Python, Rust, Julia, Node.js, WebAssembly, and C++. See the [Installation Guide](https://lowess.readthedocs.io/getting-started/installation/) for detailed installation instructions.
|
|
49
49
|
|
|
50
50
|
## Documentation
|
|
51
51
|
|
|
@@ -57,12 +57,12 @@ The fastest, most robust, and most feature-complete language-agnostic LOWESS (Lo
|
|
|
57
57
|
|
|
58
58
|
## LOESS vs. LOWESS
|
|
59
59
|
|
|
60
|
-
| Feature
|
|
61
|
-
|
|
62
|
-
| **Polynomial Degree** | Linear, Quadratic, Cubic, Quartic | Linear (Degree 1)
|
|
63
|
-
| **Dimensions**
|
|
64
|
-
| **Flexibility**
|
|
65
|
-
| **Complexity**
|
|
60
|
+
| Feature | LOESS (This Crate) | LOWESS |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| **Polynomial Degree** | Linear, Quadratic, Cubic, Quartic | Linear (Degree 1) |
|
|
63
|
+
| **Dimensions** | Multivariate (n-D support) | Univariate (1-D only) |
|
|
64
|
+
| **Flexibility** | High (Distance metrics) | Standard |
|
|
65
|
+
| **Complexity** | Higher (Matrix inversion) | Lower (Weighted average/slope) |
|
|
66
66
|
|
|
67
67
|
> [!TIP]
|
|
68
68
|
> **Note:** For a **LOESS** implementation, use [`loess-project`](https://github.com/thisisamirv/loess-project).
|
|
@@ -75,7 +75,7 @@ The fastest, most robust, and most feature-complete language-agnostic LOWESS (Lo
|
|
|
75
75
|
|
|
76
76
|
The `lowess` project beats the competition in terms of speed, whether in single-threaded or multi-threaded parallel execution. It is on average **200-327x faster** than Python's `statsmodels.lowess` and **2-3x faster** than R's `lowess`.
|
|
77
77
|
|
|
78
|
-
For more details on the performance comparison, see the [
|
|
78
|
+
For more details on the performance comparison, see the [Benchmarks](https://lowess.readthedocs.io/benchmarks/) page.
|
|
79
79
|
|
|
80
80
|
### Robustness
|
|
81
81
|
|
|
@@ -117,22 +117,22 @@ This crate applies a range of different *boundary policies* at dataset edges:
|
|
|
117
117
|
|
|
118
118
|
A variety of features, supporting a range of use cases:
|
|
119
119
|
|
|
120
|
-
| Feature
|
|
121
|
-
|
|
122
|
-
| Kernel
|
|
123
|
-
| Robustness Weighting | 3 options
|
|
124
|
-
| Scale Estimation
|
|
125
|
-
| Boundary Padding
|
|
126
|
-
| Zero Weight Fallback | 3 options
|
|
127
|
-
| Auto Convergence
|
|
128
|
-
| Online Mode
|
|
129
|
-
| Streaming Mode
|
|
130
|
-
| Confidence Intervals | yes
|
|
131
|
-
| Prediction Intervals | yes
|
|
132
|
-
| Cross-Validation
|
|
133
|
-
| Parallel Execution
|
|
134
|
-
| GPU Acceleration
|
|
135
|
-
| `no-std` Support
|
|
120
|
+
| Feature | This package | statsmodels | R (stats) |
|
|
121
|
+
| --- | :---: | :---: | :---: |
|
|
122
|
+
| Kernel | 7 options | only Tricube | only Tricube |
|
|
123
|
+
| Robustness Weighting | 3 options | only Huber | only Huber |
|
|
124
|
+
| Scale Estimation | 2 options | only MAR | only MAR |
|
|
125
|
+
| Boundary Padding | 4 options | no padding | no padding |
|
|
126
|
+
| Zero Weight Fallback | 3 options | no | no |
|
|
127
|
+
| Auto Convergence | yes | no | no |
|
|
128
|
+
| Online Mode | yes | no | no |
|
|
129
|
+
| Streaming Mode | yes | no | no |
|
|
130
|
+
| Confidence Intervals | yes | no | no |
|
|
131
|
+
| Prediction Intervals | yes | no | no |
|
|
132
|
+
| Cross-Validation | 2 options | no | no |
|
|
133
|
+
| Parallel Execution | yes | no | no |
|
|
134
|
+
| GPU Acceleration | yes* | no | no |
|
|
135
|
+
| `no-std` Support | yes | no | no |
|
|
136
136
|
|
|
137
137
|
\* GPU acceleration is currently in beta and may not be available on all platforms.
|
|
138
138
|
|
|
@@ -140,23 +140,26 @@ A variety of features, supporting a range of use cases:
|
|
|
140
140
|
|
|
141
141
|
All implementations are **numerical twins** of R's `lowess`:
|
|
142
142
|
|
|
143
|
-
| Aspect
|
|
144
|
-
|
|
145
|
-
| **Accuracy**
|
|
146
|
-
| **Consistency** | ✅ PERFECT
|
|
147
|
-
| **Robustness**
|
|
143
|
+
| Aspect | Status | Details |
|
|
144
|
+
| --- | --- | --- |
|
|
145
|
+
| **Accuracy** | ✅ EXACT MATCH | Max diff < 1e-12 across all scenarios |
|
|
146
|
+
| **Consistency** | ✅ PERFECT | Multiple scenarios pass with strict tolerance |
|
|
147
|
+
| **Robustness** | ✅ VERIFIED | Robust smoothing matches R exactly |
|
|
148
148
|
|
|
149
149
|
## API Reference
|
|
150
150
|
|
|
151
151
|
**R:**
|
|
152
152
|
|
|
153
153
|
```r
|
|
154
|
-
|
|
154
|
+
library(rfastlowess)
|
|
155
|
+
|
|
156
|
+
model <- Lowess(
|
|
155
157
|
fraction = 0.5,
|
|
156
158
|
iterations = 3L,
|
|
157
159
|
delta = 0.01,
|
|
158
160
|
weight_function = "tricube",
|
|
159
161
|
robustness_method = "bisquare",
|
|
162
|
+
scaling_method = "mad",
|
|
160
163
|
zero_weight_fallback = "use_local_mean",
|
|
161
164
|
boundary_policy = "extend",
|
|
162
165
|
confidence_intervals = 0.95,
|
|
@@ -164,12 +167,16 @@ Lowess(
|
|
|
164
167
|
return_diagnostics = TRUE,
|
|
165
168
|
return_residuals = TRUE,
|
|
166
169
|
return_robustness_weights = TRUE,
|
|
170
|
+
return_se = TRUE,
|
|
167
171
|
cv_fractions = c(0.3, 0.5, 0.7),
|
|
168
172
|
cv_method = "kfold",
|
|
169
173
|
cv_k = 5L,
|
|
174
|
+
cv_seed = 123L,
|
|
170
175
|
auto_converge = 1e-4,
|
|
171
176
|
parallel = TRUE
|
|
172
|
-
)
|
|
177
|
+
)
|
|
178
|
+
custom_weights <- rep(1, length(x))
|
|
179
|
+
result <- model$fit(x, y, custom_weights = custom_weights)
|
|
173
180
|
|
|
174
181
|
# Result structure:
|
|
175
182
|
result$x,
|
|
@@ -198,6 +205,7 @@ model = Lowess(
|
|
|
198
205
|
delta=0.01,
|
|
199
206
|
weight_function="tricube",
|
|
200
207
|
robustness_method="bisquare",
|
|
208
|
+
scaling_method="mad",
|
|
201
209
|
zero_weight_fallback="use_local_mean",
|
|
202
210
|
boundary_policy="extend",
|
|
203
211
|
confidence_intervals=0.95,
|
|
@@ -205,13 +213,16 @@ model = Lowess(
|
|
|
205
213
|
return_diagnostics=True,
|
|
206
214
|
return_residuals=True,
|
|
207
215
|
return_robustness_weights=True,
|
|
216
|
+
return_se=True,
|
|
208
217
|
cv_fractions=[0.3, 0.5, 0.7],
|
|
209
218
|
cv_method="kfold",
|
|
210
219
|
cv_k=5,
|
|
220
|
+
cv_seed=123,
|
|
211
221
|
auto_converge=1e-4,
|
|
212
222
|
parallel=True
|
|
213
223
|
)
|
|
214
|
-
|
|
224
|
+
custom_weights = [1.0] * len(x)
|
|
225
|
+
result = model.fit(x, y, custom_weights=custom_weights)
|
|
215
226
|
|
|
216
227
|
# Result structure:
|
|
217
228
|
result.x,
|
|
@@ -232,27 +243,32 @@ result.cv_scores
|
|
|
232
243
|
**Rust:**
|
|
233
244
|
|
|
234
245
|
```rust
|
|
235
|
-
|
|
246
|
+
use lowess::prelude::*;
|
|
247
|
+
|
|
248
|
+
let model = Lowess::new()
|
|
236
249
|
.fraction(0.5)
|
|
237
250
|
.iterations(3)
|
|
238
251
|
.delta(0.01)
|
|
239
|
-
.weight_function(
|
|
240
|
-
.robustness_method(
|
|
241
|
-
.
|
|
242
|
-
.
|
|
252
|
+
.weight_function("tricube")
|
|
253
|
+
.robustness_method("bisquare")
|
|
254
|
+
.scaling_method("mad")
|
|
255
|
+
.zero_weight_fallback("use_local_mean")
|
|
256
|
+
.boundary_policy("extend")
|
|
257
|
+
.return_se()
|
|
243
258
|
.confidence_intervals(0.95)
|
|
244
259
|
.prediction_intervals(0.95)
|
|
245
260
|
.return_diagnostics()
|
|
246
261
|
.return_residuals()
|
|
247
262
|
.return_robustness_weights()
|
|
248
|
-
.
|
|
263
|
+
.cv_method("kfold")
|
|
264
|
+
.cv_k(5)
|
|
265
|
+
.cv_fractions(vec![0.3, 0.5, 0.7])
|
|
266
|
+
.cv_seed(123)
|
|
249
267
|
.auto_converge(1e-4)
|
|
250
|
-
.
|
|
251
|
-
.parallel(true) // fastLowess only
|
|
252
|
-
.backend(CPU) // fastLowess only: CPU or GPU
|
|
268
|
+
.custom_weights(vec![1.0; x.len()])
|
|
253
269
|
.build()?;
|
|
254
270
|
|
|
255
|
-
let result = model.fit(x, y)
|
|
271
|
+
let result = model.fit(&x, &y)?;
|
|
256
272
|
|
|
257
273
|
// Result structure:
|
|
258
274
|
pub struct LowessResult<T> {
|
|
@@ -275,12 +291,15 @@ pub struct LowessResult<T> {
|
|
|
275
291
|
**Julia:**
|
|
276
292
|
|
|
277
293
|
```julia
|
|
278
|
-
|
|
294
|
+
using FastLOWESS
|
|
295
|
+
|
|
296
|
+
model = Lowess(;
|
|
279
297
|
fraction=0.5,
|
|
280
298
|
iterations=3,
|
|
281
299
|
delta=NaN, # NaN for auto
|
|
282
300
|
weight_function="tricube",
|
|
283
301
|
robustness_method="bisquare",
|
|
302
|
+
scaling_method="mad",
|
|
284
303
|
zero_weight_fallback="use_local_mean",
|
|
285
304
|
boundary_policy="extend",
|
|
286
305
|
confidence_intervals=NaN,
|
|
@@ -288,12 +307,16 @@ Lowess(;
|
|
|
288
307
|
return_diagnostics=true,
|
|
289
308
|
return_residuals=true,
|
|
290
309
|
return_robustness_weights=true,
|
|
310
|
+
return_se=true,
|
|
291
311
|
cv_fractions=Float64[], # e.g. [0.3, 0.5]
|
|
292
312
|
cv_method="kfold",
|
|
293
313
|
cv_k=5,
|
|
314
|
+
cv_seed=123,
|
|
294
315
|
auto_converge=NaN,
|
|
295
316
|
parallel=true
|
|
296
317
|
)
|
|
318
|
+
custom_weights = ones(length(x))
|
|
319
|
+
result = fit(model, x, y; custom_weights=custom_weights)
|
|
297
320
|
|
|
298
321
|
# Result structure:
|
|
299
322
|
result.x,
|
|
@@ -314,90 +337,107 @@ result.cv_scores
|
|
|
314
337
|
**Node.js:**
|
|
315
338
|
|
|
316
339
|
```javascript
|
|
317
|
-
|
|
340
|
+
import { Lowess } from "fastlowess"
|
|
341
|
+
|
|
342
|
+
const model = new Lowess({
|
|
318
343
|
fraction: 0.5,
|
|
319
344
|
iterations: 3,
|
|
320
345
|
delta: 0.01,
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
346
|
+
weight_function: "tricube",
|
|
347
|
+
robustness_method: "bisquare",
|
|
348
|
+
scaling_method: "mad",
|
|
349
|
+
zero_weight_fallback: "use_local_mean",
|
|
350
|
+
boundary_policy: "extend",
|
|
351
|
+
return_se: true,
|
|
352
|
+
confidence_intervals: 0.95,
|
|
353
|
+
prediction_intervals: 0.95,
|
|
354
|
+
return_diagnostics: true,
|
|
355
|
+
return_residuals: true,
|
|
356
|
+
return_robustness_weights: true,
|
|
357
|
+
cv_fractions: [0.3, 0.5, 0.7],
|
|
358
|
+
cv_method: "kfold",
|
|
359
|
+
cv_k: 5,
|
|
360
|
+
cv_seed: 123,
|
|
361
|
+
auto_converge: 1e-4,
|
|
334
362
|
parallel: true
|
|
335
|
-
})
|
|
363
|
+
})
|
|
364
|
+
const custom_weights = Array(x.length).fill(1.0)
|
|
365
|
+
const result = model.fit(x, y, custom_weights)
|
|
336
366
|
|
|
337
367
|
// Result structure:
|
|
338
368
|
result.x,
|
|
339
369
|
result.y,
|
|
340
|
-
result.
|
|
341
|
-
result.
|
|
342
|
-
result.
|
|
343
|
-
result.
|
|
344
|
-
result.
|
|
370
|
+
result.standard_errors,
|
|
371
|
+
result.confidence_lower,
|
|
372
|
+
result.confidence_upper,
|
|
373
|
+
result.prediction_lower,
|
|
374
|
+
result.prediction_upper,
|
|
345
375
|
result.residuals,
|
|
346
|
-
result.
|
|
376
|
+
result.robustness_weights,
|
|
347
377
|
result.diagnostics,
|
|
348
|
-
result.
|
|
349
|
-
result.
|
|
350
|
-
result.
|
|
378
|
+
result.iterations_used,
|
|
379
|
+
result.fraction_used,
|
|
380
|
+
result.cv_scores
|
|
351
381
|
```
|
|
352
382
|
|
|
353
383
|
**WebAssembly:**
|
|
354
384
|
|
|
355
385
|
```javascript
|
|
356
|
-
|
|
386
|
+
import { Lowess } from "fastlowess-wasm"
|
|
387
|
+
|
|
388
|
+
const model = new Lowess({
|
|
357
389
|
fraction: 0.5,
|
|
358
390
|
iterations: 3,
|
|
359
391
|
delta: 0.01,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
392
|
+
weight_function: "tricube",
|
|
393
|
+
robustness_method: "bisquare",
|
|
394
|
+
scaling_method: "mad",
|
|
395
|
+
zero_weight_fallback: "use_local_mean",
|
|
396
|
+
boundary_policy: "extend",
|
|
397
|
+
return_se: true,
|
|
398
|
+
confidence_intervals: 0.95,
|
|
399
|
+
prediction_intervals: 0.95,
|
|
400
|
+
return_diagnostics: true,
|
|
401
|
+
return_residuals: true,
|
|
402
|
+
return_robustness_weights: true,
|
|
403
|
+
cv_fractions: [0.3, 0.5, 0.7],
|
|
404
|
+
cv_method: "kfold",
|
|
405
|
+
cv_k: 5,
|
|
406
|
+
cv_seed: 123,
|
|
407
|
+
auto_converge: 1e-4,
|
|
373
408
|
parallel: true
|
|
374
409
|
})
|
|
410
|
+
const custom_weights = new Float64Array(x.length).fill(1)
|
|
411
|
+
const result = model.fit(x, y, custom_weights)
|
|
375
412
|
|
|
376
413
|
// Result structure:
|
|
377
414
|
result.x,
|
|
378
415
|
result.y,
|
|
379
|
-
result.
|
|
380
|
-
result.
|
|
381
|
-
result.
|
|
382
|
-
result.
|
|
383
|
-
result.
|
|
416
|
+
result.standard_errors,
|
|
417
|
+
result.confidence_lower,
|
|
418
|
+
result.confidence_upper,
|
|
419
|
+
result.prediction_lower,
|
|
420
|
+
result.prediction_upper,
|
|
384
421
|
result.residuals,
|
|
385
|
-
result.
|
|
422
|
+
result.robustness_weights,
|
|
386
423
|
result.diagnostics,
|
|
387
|
-
result.
|
|
388
|
-
result.
|
|
389
|
-
result.
|
|
424
|
+
result.iterations_used,
|
|
425
|
+
result.fraction_used,
|
|
426
|
+
result.cv_scores
|
|
390
427
|
```
|
|
391
428
|
|
|
392
429
|
**C++:**
|
|
393
430
|
|
|
394
431
|
```cpp
|
|
432
|
+
#include "fastlowess.hpp"
|
|
433
|
+
|
|
395
434
|
fastlowess::LowessOptions options;
|
|
396
435
|
options.fraction = 0.5;
|
|
397
436
|
options.iterations = 3;
|
|
398
437
|
options.delta = 0.01;
|
|
399
438
|
options.weight_function = "tricube";
|
|
400
439
|
options.robustness_method = "bisquare";
|
|
440
|
+
options.scaling_method = "mad";
|
|
401
441
|
options.zero_weight_fallback = "use_local_mean";
|
|
402
442
|
options.boundary_policy = "extend";
|
|
403
443
|
options.confidence_intervals = 0.95;
|
|
@@ -405,14 +445,17 @@ options.prediction_intervals = 0.95;
|
|
|
405
445
|
options.return_diagnostics = true;
|
|
406
446
|
options.return_residuals = true;
|
|
407
447
|
options.return_robustness_weights = true;
|
|
448
|
+
options.return_se = true;
|
|
408
449
|
options.cv_fractions = {0.3, 0.5, 0.7};
|
|
409
450
|
options.cv_method = "kfold";
|
|
410
451
|
options.cv_k = 5;
|
|
452
|
+
options.cv_seed = 123;
|
|
411
453
|
options.auto_converge = 1e-4;
|
|
412
454
|
options.parallel = true;
|
|
413
455
|
|
|
414
456
|
fastlowess::Lowess model(options);
|
|
415
|
-
|
|
457
|
+
std::vector<double> custom_weights(x.size(), 1.0);
|
|
458
|
+
const auto result = model.fit(x, y, custom_weights).value();
|
|
416
459
|
|
|
417
460
|
// Result structure:
|
|
418
461
|
result.x_vector(),
|
|
@@ -434,7 +477,11 @@ result.cv_scores()
|
|
|
434
477
|
|
|
435
478
|
## Contributing
|
|
436
479
|
|
|
437
|
-
Contributions are welcome! Please see the [
|
|
480
|
+
Contributions are welcome! Please see the [Contributing Guide](https://lowess.readthedocs.io/contributing/) for more information.
|
|
481
|
+
|
|
482
|
+
## Changelog
|
|
483
|
+
|
|
484
|
+
See the [Changelog](https://lowess.readthedocs.io/changelog/) for a history of changes.
|
|
438
485
|
|
|
439
486
|
## License
|
|
440
487
|
|