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 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 [INSTALLATION.md](https://github.com/thisisamirv/lowess-project/blob/main/INSTALLATION.md) for detailed installation instructions.
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 | 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) |
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 [BENCHMARKS](https://github.com/thisisamirv/lowess-project/blob/main/BENCHMARKS.md) file.
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 | 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 |
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 | 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 |
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
- Lowess(
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
- )$fit(x, y)
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
- result = model.fit(x, y)
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
- Lowess::new()
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(Tricube)
240
- .robustness_method(Bisquare)
241
- .zero_weight_fallback(UseLocalMean)
242
- .boundary_policy(Extend)
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
- .cross_validate(KFold(5, &[0.3, 0.5, 0.7]).seed(123))
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
- .adapter(Batch)
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
- Lowess(;
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
- new Lowess({
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
- weightFunction: "tricube",
322
- robustnessMethod: "bisquare",
323
- zeroWeightFallback: "use_local_mean",
324
- boundaryPolicy: "extend",
325
- confidenceIntervals: 0.95,
326
- predictionIntervals: 0.95,
327
- returnDiagnostics: true,
328
- returnResiduals: true,
329
- returnRobustnessWeights: true,
330
- cvFractions: [0.3, 0.5, 0.7],
331
- cvMethod: "kfold",
332
- cvK: 5,
333
- autoConverge: 1e-4,
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
- }).fit(x, y)
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.standardErrors,
341
- result.confidenceLower,
342
- result.confidenceUpper,
343
- result.predictionLower,
344
- result.predictionUpper,
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.robustnessWeights,
376
+ result.robustness_weights,
347
377
  result.diagnostics,
348
- result.iterationsUsed,
349
- result.fractionUsed,
350
- result.cvScores
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
- smooth(x, y, {
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
- weightFunction: "tricube",
361
- robustnessMethod: "bisquare",
362
- zeroWeightFallback: "use_local_mean",
363
- boundaryPolicy: "extend",
364
- confidenceIntervals: 0.95,
365
- predictionIntervals: 0.95,
366
- returnDiagnostics: true,
367
- returnResiduals: true,
368
- returnRobustnessWeights: true,
369
- cvFractions: [0.3, 0.5, 0.7],
370
- cvMethod: "kfold",
371
- cvK: 5,
372
- autoConverge: 1e-4,
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.standardErrors,
380
- result.confidenceLower,
381
- result.confidenceUpper,
382
- result.predictionLower,
383
- result.predictionUpper,
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.robustnessWeights,
422
+ result.robustness_weights,
386
423
  result.diagnostics,
387
- result.iterationsUsed,
388
- result.fractionUsed,
389
- result.cvScores
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
- auto result = model.fit(x, y);
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 [CONTRIBUTING.md](https://github.com/thisisamirv/lowess-project/blob/main/CONTRIBUTING.md) file for more information.
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