fastlowess-wasm 0.99.9 β†’ 1.2.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
@@ -1,13 +1,22 @@
1
1
  <!-- markdownlint-disable MD024 MD033 -->
2
2
  # LOWESS Project
3
3
 
4
- [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT)
5
- [![Docs](https://img.shields.io/badge/docs-readthedocs-blue.svg)](https://lowess.readthedocs.io/)
6
- [![Crates.io](https://img.shields.io/crates/v/lowess.svg)](https://crates.io/crates/lowess)
7
- [![PyPI](https://img.shields.io/pypi/v/fastlowess.svg)](https://pypi.org/project/fastlowess/)
8
- [![Conda](https://anaconda.org/conda-forge/fastlowess/badges/version.svg)](https://anaconda.org/conda-forge/fastlowess)
9
- [![R-universe](https://thisisamirv.r-universe.dev/badges/rfastlowess)](https://thisisamirv.r-universe.dev/rfastlowess)
10
- [![npm](https://img.shields.io/npm/v/fastlowess.svg)](https://www.npmjs.com/package/fastlowess)
4
+ <p align="center">
5
+ <a href="https://crates.io/crates/lowess"><img src="https://img.shields.io/badge/lowess-000000?logo=rust&logoColor=white" alt="lowess"></a>
6
+ <a href="https://crates.io/crates/fastLowess"><img src="https://img.shields.io/badge/fastLowess-000000?logo=rust&logoColor=white" alt="fastLowess"></a>
7
+ <a href="https://pypi.org/project/fastlowess/"><img src="https://img.shields.io/badge/PyPI-3775A9?logo=pypi&logoColor=white" alt="PyPI"></a>
8
+ <a href="https://thisisamirv.r-universe.dev/rfastlowess"><img src="https://img.shields.io/badge/R--universe-276DC3?logo=r&logoColor=white" alt="R-universe"></a>
9
+ <a href="https://www.npmjs.com/package/fastlowess"><img src="https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white" alt="npm"></a>
10
+ <a href="https://juliahub.com/ui/Packages/General/FastLOWESS"><img src="https://img.shields.io/badge/Julia-9558B2?logo=julia&logoColor=white" alt="Julia"></a>
11
+ <a href="https://www.npmjs.com/package/fastlowess-wasm"><img src="https://img.shields.io/badge/WASM-654FF0?logo=webassembly&logoColor=white" alt="WASM"></a>
12
+ <a href="https://github.com/thisisamirv/lowess-project/releases/latest"><img src="https://img.shields.io/badge/C++-00599C?logo=cplusplus&logoColor=white" alt="C++"></a>
13
+ <br>
14
+ <a href="https://anaconda.org/conda-forge/fastlowess"><img src="https://img.shields.io/badge/fastlowess_(Python)-44A833?logo=anaconda&logoColor=white" alt="fastlowess (Python)"></a>
15
+ <a href="https://anaconda.org/conda-forge/libfastlowess"><img src="https://img.shields.io/badge/libfastlowess_(C++)-44A833?logo=anaconda&logoColor=white" alt="libfastlowess (C++)"></a>
16
+ <a href="https://anaconda.org/conda-forge/r-rfastlowess"><img src="https://img.shields.io/badge/rfastlowess_(R)-44A833?logo=anaconda&logoColor=white" alt="rfastlowess (R)"></a>
17
+ <br>
18
+ <a href="https://github.com/thisisamirv/lowess-project/actions/workflows/ci.yml"><img src="https://github.com/thisisamirv/lowess-project/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
19
+ </p>
11
20
 
12
21
  <p align="center">
13
22
  <img src="https://raw.githubusercontent.com/thisisamirv/lowess-project/main/dev/logo.png" alt="One LOWESS to Rule Them All" width="400">
@@ -21,14 +30,30 @@ The fastest, most robust, and most feature-complete language-agnostic LOWESS (Lo
21
30
  >
22
31
  > The `lowess-project` contains a complete ecosystem for LOWESS smoothing:
23
32
  >
24
- > - **[`lowess`](https://github.com/thisisamirv/lowess-project/tree/main/crates/lowess)** - Core single-threaded Rust implementation with `no_std` support
25
- > - **[`fastLowess`](https://github.com/thisisamirv/lowess-project/tree/main/crates/fastLowess)** - Parallel CPU and GPU-accelerated Rust wrapper with ndarray integration
26
- > - **[`R bindings`](https://github.com/thisisamirv/lowess-project/tree/main/bindings/r)** - extendr-based R package
27
- > - **[`Python bindings`](https://github.com/thisisamirv/lowess-project/tree/main/bindings/python)** - PyO3-based Python package
28
- > - **[`Julia bindings`](https://github.com/thisisamirv/lowess-project/tree/main/bindings/julia)** - Native Julia package with C FFI
29
- > - **[`JavaScript bindings`](https://github.com/thisisamirv/lowess-project/tree/main/bindings/nodejs)** - Node.js package
30
- > - **[`WebAssembly bindings`](https://github.com/thisisamirv/lowess-project/tree/main/bindings/wasm)** - WASM package
31
- > - **[`C++ bindings`](https://github.com/thisisamirv/lowess-project/tree/main/bindings/cpp)** - Native C++ package with CMake integration
33
+ > - **[`lowess`](https://crates.io/crates/lowess)** - Core single-threaded Rust implementation with `no_std` support
34
+ > - **[`fastLowess`](https://crates.io/crates/fastLowess)** - Parallel CPU and GPU-accelerated Rust wrapper with ndarray integration
35
+ > - **[`R bindings`](https://thisisamirv.r-universe.dev/rfastlowess)** - extendr-based R binding
36
+ > - **[`Python bindings`](https://pypi.org/project/fastlowess/)** - PyO3-based Python binding
37
+ > - **[`Julia bindings`](https://juliahub.com/ui/Packages/General/FastLOWESS)** - Native Julia binding with C FFI
38
+ > - **[`JavaScript bindings`](https://www.npmjs.com/package/fastlowess)** - Node.js binding
39
+ > - **[`WebAssembly bindings`](https://www.npmjs.com/package/fastlowess-wasm)** - WASM binding
40
+ > - **[`C++ bindings`](https://github.com/thisisamirv/lowess-project/releases/latest)** - Native C++ binding with CMake integration
41
+
42
+ ---
43
+
44
+ ## Installation
45
+
46
+ > [!NOTE]
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.
49
+
50
+ ## Documentation
51
+
52
+ > [!NOTE]
53
+ >
54
+ > ### πŸ“š [View the full documentation](https://lowess.readthedocs.io/)
55
+
56
+ ---
32
57
 
33
58
  ## LOESS vs. LOWESS
34
59
 
@@ -44,38 +69,13 @@ The fastest, most robust, and most feature-complete language-agnostic LOWESS (Lo
44
69
 
45
70
  ---
46
71
 
47
- ## Documentation
48
-
49
- > [!NOTE]
50
- >
51
- > ### πŸ“š [View the full documentation](https://lowess.readthedocs.io/)
52
-
53
72
  ## Why this package?
54
73
 
55
74
  ### Speed
56
75
 
57
- The `lowess` project crushes the competition in terms of speed, wether in single-threaded or multi-threaded parallel execution.
58
-
59
- Speedup relative to Python's `statsmodels.lowess` (higher is better):
60
-
61
- | Category | statsmodels | R (stats) | Serial | Parallel | GPU |
62
- |---------------------------------|-------------|-----------|--------|----------|---------|
63
- | **Clustered** | 163ms | 83Γ— | 203Γ— | **433Γ—** | 32Γ— |
64
- | **Constant Y** | 134ms | 92Γ— | 212Γ— | **410Γ—** | 18Γ— |
65
- | **Delta** (large–none) | 105ms | 2Γ— | 4Γ— | 6Γ— | **16Γ—** |
66
- | **Extreme Outliers** | 489ms | 106Γ— | 201Γ— | **388Γ—** | 29Γ— |
67
- | **Financial** (500–10K) | 106ms | 105Γ— | 252Γ— | **293Γ—** | 12Γ— |
68
- | **Fraction** (0.05–0.67) | 221ms | 104Γ— | 228Γ— | **391Γ—** | 22Γ— |
69
- | **Genomic** (1K–50K) | 1833ms | 7Γ— | 9Γ— | 20Γ— | **95Γ—** |
70
- | **High Noise** | 435ms | 133Γ— | 134Γ— | **375Γ—** | 32Γ— |
71
- | **Iterations** (0–10) | 204ms | 115Γ— | 224Γ— | **386Γ—** | 18Γ— |
72
- | **Scale** (1K–50K) | 1841ms | 264Γ— | 487Γ— | **581Γ—** | 98Γ— |
73
- | **Scientific** (500–10K) | 167ms | 109Γ— | 205Γ— | **314Γ—** | 15Γ— |
74
- | **Scale Large**\* (100K–2M) | β€” | β€” | 1Γ— | **1.4Γ—** | 0.3Γ— |
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`.
75
77
 
76
- \*Scale Large benchmarks are relative to Serial (statsmodels cannot handle these sizes)
77
-
78
- *The numbers are the average across a range of scenarios for each category (e.g., Delta from none, to small, medium, and large).*
78
+ For more details on the performance comparison, see the [BENCHMARKS](https://github.com/thisisamirv/lowess-project/blob/main/BENCHMARKS.md) file.
79
79
 
80
80
  ### Robustness
81
81
 
@@ -146,182 +146,6 @@ All implementations are **numerical twins** of R's `lowess`:
146
146
  | **Consistency** | βœ… PERFECT | Multiple scenarios pass with strict tolerance |
147
147
  | **Robustness** | βœ… VERIFIED | Robust smoothing matches R exactly |
148
148
 
149
- ## Installation
150
-
151
- Currently available for R, Python, Julia, and Rust:
152
-
153
- **R** (from R-universe, recommended):
154
-
155
- ```r
156
- install.packages("rfastlowess", repos = "https://thisisamirv.r-universe.dev")
157
- ```
158
-
159
- **Python** (from PyPI):
160
-
161
- ```bash
162
- pip install fastlowess
163
- ```
164
-
165
- Or from conda-forge:
166
-
167
- ```bash
168
- conda install -c conda-forge fastlowess
169
- ```
170
-
171
- **Rust** (lowess, no_std compatible):
172
-
173
- ```toml
174
- [dependencies]
175
- lowess = "0.99"
176
- ```
177
-
178
- **Rust** (fastLowess, parallel + GPU):
179
-
180
- ```toml
181
- [dependencies]
182
- fastLowess = { version = "0.99", features = ["cpu"] }
183
- ```
184
-
185
- **Julia** (from Julia General Registry):
186
-
187
- ```julia
188
- using Pkg
189
- Pkg.add("fastLowess")
190
- ```
191
-
192
- **Node.js** (from npm):
193
-
194
- ```bash
195
- npm install fastlowess
196
- ```
197
-
198
- **WebAssembly** (from npm):
199
-
200
- ```bash
201
- npm install fastlowess-wasm
202
- ```
203
-
204
- Or via CDN:
205
-
206
- ```html
207
- <script type="module">
208
- import init, { smooth } from 'https://unpkg.com/fastlowess-wasm@latest';
209
- await init();
210
- </script>
211
- ```
212
-
213
- **C++** (build from source):
214
-
215
- ```bash
216
- make cpp
217
- # Links against libfastlowess_cpp.so
218
- ```
219
-
220
- ## Quick Example
221
-
222
- **R:**
223
-
224
- ```r
225
- library(rfastlowess)
226
-
227
- x <- c(1, 2, 3, 4, 5)
228
- y <- c(2.0, 4.1, 5.9, 8.2, 9.8)
229
-
230
- model <- Lowess(fraction = 0.5, iterations = 3)
231
- result <- model$fit(x, y)
232
- print(result$y)
233
- ```
234
-
235
- **Python:**
236
-
237
- ```python
238
- import fastlowess as fl
239
- import numpy as np
240
-
241
- x = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
242
- y = np.array([2.0, 4.1, 5.9, 8.2, 9.8])
243
-
244
- result = fl.smooth(x, y, fraction=0.5, iterations=3)
245
- print(result["y"])
246
- ```
247
-
248
- **Rust:**
249
-
250
- ```rust
251
- use lowess::prelude::*;
252
-
253
- let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
254
- let y = vec![2.0, 4.1, 5.9, 8.2, 9.8];
255
-
256
- let model = Lowess::new()
257
- .fraction(0.5)
258
- .iterations(3)
259
- .adapter(Batch)
260
- .build()?;
261
-
262
- let result = model.fit(&x, &y)?;
263
- println!("{}", result);
264
- ```
265
-
266
- **Julia:**
267
-
268
- ```julia
269
- using fastlowess
270
-
271
- x = [1.0, 2.0, 3.0, 4.0, 5.0]
272
- y = [2.0, 4.1, 5.9, 8.2, 9.8]
273
-
274
- result = fit(Lowess(fraction=0.5, iterations=3), x, y)
275
- println(result.y)
276
- ```
277
-
278
- **Node.js:**
279
-
280
- ```javascript
281
- const { Lowess } = require('fastlowess');
282
-
283
- const x = [1.0, 2.0, 3.0, 4.0, 5.0];
284
- const y = [2.0, 4.1, 5.9, 8.2, 9.8];
285
-
286
- const model = new Lowess({ fraction: 0.5, iterations: 3 });
287
- const result = model.fit(x, y);
288
- console.log(result.y);
289
- ```
290
-
291
- **WebAssembly:**
292
-
293
- ```javascript
294
- import init, { smooth } from 'fastlowess-wasm';
295
-
296
- await init();
297
-
298
- const x = new Float64Array([1.0, 2.0, 3.0, 4.0, 5.0]);
299
- const y = new Float64Array([2.0, 4.1, 5.9, 8.2, 9.8]);
300
-
301
- const result = smooth(x, y, { fraction: 0.5, iterations: 3 });
302
- console.log(result.y);
303
- ```
304
-
305
- **C++:**
306
-
307
- ```cpp
308
- #include <fastlowess.hpp>
309
-
310
- std::vector<double> x = {1.0, 2.0, 3.0, 4.0, 5.0};
311
- std::vector<double> y = {2.0, 4.1, 5.9, 8.2, 9.8};
312
-
313
- fastlowess::LowessOptions options;
314
- options.fraction = 0.5;
315
- options.iterations = 3;
316
-
317
- fastlowess::Lowess model(options);
318
- auto result = model.fit(x, y);
319
-
320
- for (double val : result.y_vector()) std::cout << val << " ";
321
- ```
322
-
323
- ---
324
-
325
149
  ## API Reference
326
150
 
327
151
  **R:**
@@ -346,13 +170,29 @@ Lowess(
346
170
  auto_converge = 1e-4,
347
171
  parallel = TRUE
348
172
  )$fit(x, y)
173
+
174
+ # Result structure:
175
+ result$x,
176
+ result$y,
177
+ result$standard_errors,
178
+ result$confidence_lower,
179
+ result$confidence_upper,
180
+ result$prediction_lower,
181
+ result$prediction_upper,
182
+ result$residuals,
183
+ result$robustness_weights,
184
+ result$diagnostics,
185
+ result$iterations_used,
186
+ result$fraction_used,
187
+ result$cv_scores
349
188
  ```
350
189
 
351
190
  **Python:**
352
191
 
353
192
  ```python
354
- fastlowess.smooth(
355
- x, y,
193
+ from fastlowess import Lowess
194
+
195
+ model = Lowess(
356
196
  fraction=0.5,
357
197
  iterations=3,
358
198
  delta=0.01,
@@ -371,16 +211,32 @@ fastlowess.smooth(
371
211
  auto_converge=1e-4,
372
212
  parallel=True
373
213
  )
214
+ result = model.fit(x, y)
215
+
216
+ # Result structure:
217
+ result.x,
218
+ result.y,
219
+ result.standard_errors,
220
+ result.confidence_lower,
221
+ result.confidence_upper,
222
+ result.prediction_lower,
223
+ result.prediction_upper,
224
+ result.residuals,
225
+ result.robustness_weights,
226
+ result.diagnostics,
227
+ result.iterations_used,
228
+ result.fraction_used,
229
+ result.cv_scores
374
230
  ```
375
231
 
376
232
  **Rust:**
377
233
 
378
234
  ```rust
379
235
  Lowess::new()
380
- .fraction(0.5) // Smoothing span (0, 1]
381
- .iterations(3) // Robustness iterations
382
- .delta(0.01) // Interpolation threshold
383
- .weight_function(Tricube) // Kernel selection
236
+ .fraction(0.5)
237
+ .iterations(3)
238
+ .delta(0.01)
239
+ .weight_function(Tricube)
384
240
  .robustness_method(Bisquare)
385
241
  .zero_weight_fallback(UseLocalMean)
386
242
  .boundary_policy(Extend)
@@ -391,10 +247,29 @@ Lowess::new()
391
247
  .return_robustness_weights()
392
248
  .cross_validate(KFold(5, &[0.3, 0.5, 0.7]).seed(123))
393
249
  .auto_converge(1e-4)
394
- .adapter(Batch) // or Streaming, Online
250
+ .adapter(Batch)
395
251
  .parallel(true) // fastLowess only
396
252
  .backend(CPU) // fastLowess only: CPU or GPU
397
253
  .build()?;
254
+
255
+ let result = model.fit(x, y);
256
+
257
+ // Result structure:
258
+ pub struct LowessResult<T> {
259
+ pub x: Vec<T>, // Sorted x values
260
+ pub y: Vec<T>, // Smoothed y values
261
+ pub standard_errors: Option<Vec<T>>,
262
+ pub confidence_lower: Option<Vec<T>>,
263
+ pub confidence_upper: Option<Vec<T>>,
264
+ pub prediction_lower: Option<Vec<T>>,
265
+ pub prediction_upper: Option<Vec<T>>,
266
+ pub residuals: Option<Vec<T>>,
267
+ pub robustness_weights: Option<Vec<T>>,
268
+ pub diagnostics: Option<Diagnostics<T>>,
269
+ pub iterations_used: Option<usize>,
270
+ pub fraction_used: T,
271
+ pub cv_scores: Option<Vec<T>>,
272
+ }
398
273
  ```
399
274
 
400
275
  **Julia:**
@@ -419,6 +294,21 @@ Lowess(;
419
294
  auto_converge=NaN,
420
295
  parallel=true
421
296
  )
297
+
298
+ # Result structure:
299
+ result.x,
300
+ result.y,
301
+ result.standard_errors,
302
+ result.confidence_lower,
303
+ result.confidence_upper,
304
+ result.prediction_lower,
305
+ result.prediction_upper,
306
+ result.residuals,
307
+ result.robustness_weights,
308
+ result.diagnostics,
309
+ result.iterations_used,
310
+ result.fraction_used,
311
+ result.cv_scores
422
312
  ```
423
313
 
424
314
  **Node.js:**
@@ -443,6 +333,21 @@ new Lowess({
443
333
  autoConverge: 1e-4,
444
334
  parallel: true
445
335
  }).fit(x, y)
336
+
337
+ // Result structure:
338
+ result.x,
339
+ result.y,
340
+ result.standardErrors,
341
+ result.confidenceLower,
342
+ result.confidenceUpper,
343
+ result.predictionLower,
344
+ result.predictionUpper,
345
+ result.residuals,
346
+ result.robustnessWeights,
347
+ result.diagnostics,
348
+ result.iterationsUsed,
349
+ result.fractionUsed,
350
+ result.cvScores
446
351
  ```
447
352
 
448
353
  **WebAssembly:**
@@ -464,8 +369,24 @@ smooth(x, y, {
464
369
  cvFractions: [0.3, 0.5, 0.7],
465
370
  cvMethod: "kfold",
466
371
  cvK: 5,
467
- autoConverge: 1e-4
372
+ autoConverge: 1e-4,
373
+ parallel: true
468
374
  })
375
+
376
+ // Result structure:
377
+ result.x,
378
+ result.y,
379
+ result.standardErrors,
380
+ result.confidenceLower,
381
+ result.confidenceUpper,
382
+ result.predictionLower,
383
+ result.predictionUpper,
384
+ result.residuals,
385
+ result.robustnessWeights,
386
+ result.diagnostics,
387
+ result.iterationsUsed,
388
+ result.fractionUsed,
389
+ result.cvScores
469
390
  ```
470
391
 
471
392
  **C++:**
@@ -492,98 +413,21 @@ options.parallel = true;
492
413
 
493
414
  fastlowess::Lowess model(options);
494
415
  auto result = model.fit(x, y);
495
- ```
496
-
497
- ## Result Structure
498
416
 
499
- **R:**
500
-
501
- ```r
502
- result$x, result$y, result$standard_errors
503
- result$confidence_lower, result$confidence_upper
504
- result$prediction_lower, result$prediction_upper
505
- result$residuals, result$robustness_weights
506
- result$diagnostics, result$iterations_used
507
- result$fraction_used, result$cv_scores
508
- ```
509
-
510
- **Python:**
511
-
512
- ```python
513
- result.x, result.y, result.standard_errors
514
- result.confidence_lower, result.confidence_upper
515
- result.prediction_lower, result.prediction_upper
516
- result.residuals, result.robustness_weights
517
- result.diagnostics, result.iterations_used
518
- result.fraction_used, result.cv_scores
519
- ```
520
-
521
- **Rust:**
522
-
523
- ```rust
524
- pub struct LowessResult<T> {
525
- pub x: Vec<T>, // Sorted x values
526
- pub y: Vec<T>, // Smoothed y values
527
- pub standard_errors: Option<Vec<T>>,
528
- pub confidence_lower: Option<Vec<T>>,
529
- pub confidence_upper: Option<Vec<T>>,
530
- pub prediction_lower: Option<Vec<T>>,
531
- pub prediction_upper: Option<Vec<T>>,
532
- pub residuals: Option<Vec<T>>,
533
- pub robustness_weights: Option<Vec<T>>,
534
- pub diagnostics: Option<Diagnostics<T>>,
535
- pub iterations_used: Option<usize>,
536
- pub fraction_used: T,
537
- pub cv_scores: Option<Vec<T>>,
538
- }
539
- ```
540
-
541
- **Julia:**
542
-
543
- ```julia
544
- result.x, result.y, result.standard_errors
545
- result.confidence_lower, result.confidence_upper
546
- result.prediction_lower, result.prediction_upper
547
- result.residuals, result.robustness_weights
548
- result.diagnostics, result.iterations_used
549
- result.fraction_used
550
- ```
551
-
552
- **Node.js:**
553
-
554
- ```javascript
555
- result.x, result.y, result.standardErrors
556
- result.confidenceLower, result.confidenceUpper
557
- result.predictionLower, result.predictionUpper
558
- result.residuals, result.robustnessWeights
559
- result.diagnostics, result.iterationsUsed
560
- result.fractionUsed, result.cvScores
561
- ```
562
-
563
- **WebAssembly:**
564
-
565
- ```javascript
566
- result.x, result.y, result.standardErrors
567
- result.confidenceLower, result.confidenceUpper
568
- result.predictionLower, result.predictionUpper
569
- result.residuals, result.robustnessWeights
570
- result.diagnostics, result.iterationsUsed
571
- result.fractionUsed, result.cvScores
572
- ```
573
-
574
- **C++:**
575
-
576
- ```cpp
577
- result.y_vector() // std::vector<double>
578
- result.confidence_lower() // std::vector<double>
579
- result.confidence_upper() // std::vector<double>
580
- result.prediction_lower() // std::vector<double>
581
- result.prediction_upper() // std::vector<double>
582
- result.residuals() // std::vector<double>
583
- result.robustness_weights() // std::vector<double>
584
- result.diagnostics() // Diagnostics struct
585
- result.iterations_used() // size_t
586
- result.fraction_used() // double
417
+ // Result structure:
418
+ result.x_vector(),
419
+ result.y_vector(),
420
+ result.standard_errors(),
421
+ result.confidence_lower(),
422
+ result.confidence_upper(),
423
+ result.prediction_lower(),
424
+ result.prediction_upper(),
425
+ result.residuals(),
426
+ result.robustness_weights(),
427
+ result.diagnostics(),
428
+ result.iterations_used(),
429
+ result.fraction_used(),
430
+ result.cv_scores()
587
431
  ```
588
432
 
589
433
  ---
@@ -594,17 +438,7 @@ Contributions are welcome! Please see the [CONTRIBUTING.md](https://github.com/t
594
438
 
595
439
  ## License
596
440
 
597
- Licensed under either of:
598
-
599
- - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
600
- - MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
601
-
602
- at your option.
603
-
604
- ## References
605
-
606
- - Cleveland, W.S. (1979). "Robust Locally Weighted Regression and Smoothing Scatterplots". *JASA*.
607
- - Cleveland, W.S. (1981). "LOWESS: A Program for Smoothing Scatterplots". *The American Statistician*.
441
+ Licensed under [MIT](https://github.com/thisisamirv/lowess-project/blob/main/LICENSE-MIT) or [Apache-2.0](https://github.com/thisisamirv/lowess-project/blob/main/LICENSE-APACHE).
608
442
 
609
443
  ## Citation
610
444
 
@@ -36,21 +36,43 @@ export class LowessResultWasm {
36
36
  readonly y: Float64Array;
37
37
  }
38
38
 
39
+ /**
40
+ * Online LOWESS smoother.
41
+ */
39
42
  export class OnlineLowessWasm {
40
43
  free(): void;
41
44
  [Symbol.dispose](): void;
45
+ /**
46
+ * Create a new online smoother.
47
+ */
42
48
  constructor(options: any, online_opts: any);
43
49
  update(x: number, y: number): number | undefined;
44
50
  }
45
51
 
52
+ /**
53
+ * Streaming LOWESS smoother.
54
+ */
46
55
  export class StreamingLowessWasm {
47
56
  free(): void;
48
57
  [Symbol.dispose](): void;
49
58
  finalize(): LowessResultWasm;
59
+ /**
60
+ * Create a new streaming smoother.
61
+ */
50
62
  constructor(options: any, streaming_opts: any);
51
63
  processChunk(x: Float64Array, y: Float64Array): LowessResultWasm;
52
64
  }
53
65
 
66
+ export function init_panic_hook(): void;
67
+
68
+ /**
69
+ * Fit the LOWESS model to data.
70
+ *
71
+ * @param {Float64Array} x - X coordinates.
72
+ * @param {Float64Array} y - Y coordinates.
73
+ * @param {any} [options] - Configuration object.
74
+ * @returns {LowessResultWasm} The result of the smoothing.
75
+ */
54
76
  export function smooth(x: Float64Array, y: Float64Array, options: any): LowessResultWasm;
55
77
 
56
78
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
@@ -94,11 +116,13 @@ export interface InitOutput {
94
116
  readonly streaminglowesswasm_finalize: (a: number) => [number, number, number];
95
117
  readonly streaminglowesswasm_new: (a: any, b: any) => [number, number, number];
96
118
  readonly streaminglowesswasm_processChunk: (a: number, b: any, c: any) => [number, number, number];
119
+ readonly init_panic_hook: () => void;
97
120
  readonly __wbindgen_malloc: (a: number, b: number) => number;
98
121
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
99
122
  readonly __wbindgen_exn_store: (a: number) => void;
100
123
  readonly __externref_table_alloc: () => number;
101
124
  readonly __wbindgen_externrefs: WebAssembly.Table;
125
+ readonly __wbindgen_free: (a: number, b: number, c: number) => void;
102
126
  readonly __externref_table_dealloc: (a: number) => void;
103
127
  readonly __wbindgen_start: () => void;
104
128
  }
@@ -224,6 +224,9 @@ export class LowessResultWasm {
224
224
  }
225
225
  if (Symbol.dispose) LowessResultWasm.prototype[Symbol.dispose] = LowessResultWasm.prototype.free;
226
226
 
227
+ /**
228
+ * Online LOWESS smoother.
229
+ */
227
230
  export class OnlineLowessWasm {
228
231
  __destroy_into_raw() {
229
232
  const ptr = this.__wbg_ptr;
@@ -236,6 +239,7 @@ export class OnlineLowessWasm {
236
239
  wasm.__wbg_onlinelowesswasm_free(ptr, 0);
237
240
  }
238
241
  /**
242
+ * Create a new online smoother.
239
243
  * @param {any} options
240
244
  * @param {any} online_opts
241
245
  */
@@ -263,6 +267,9 @@ export class OnlineLowessWasm {
263
267
  }
264
268
  if (Symbol.dispose) OnlineLowessWasm.prototype[Symbol.dispose] = OnlineLowessWasm.prototype.free;
265
269
 
270
+ /**
271
+ * Streaming LOWESS smoother.
272
+ */
266
273
  export class StreamingLowessWasm {
267
274
  __destroy_into_raw() {
268
275
  const ptr = this.__wbg_ptr;
@@ -285,6 +292,7 @@ export class StreamingLowessWasm {
285
292
  return LowessResultWasm.__wrap(ret[0]);
286
293
  }
287
294
  /**
295
+ * Create a new streaming smoother.
288
296
  * @param {any} options
289
297
  * @param {any} streaming_opts
290
298
  */
@@ -312,7 +320,17 @@ export class StreamingLowessWasm {
312
320
  }
313
321
  if (Symbol.dispose) StreamingLowessWasm.prototype[Symbol.dispose] = StreamingLowessWasm.prototype.free;
314
322
 
323
+ export function init_panic_hook() {
324
+ wasm.init_panic_hook();
325
+ }
326
+
315
327
  /**
328
+ * Fit the LOWESS model to data.
329
+ *
330
+ * @param {Float64Array} x - X coordinates.
331
+ * @param {Float64Array} y - Y coordinates.
332
+ * @param {any} [options] - Configuration object.
333
+ * @returns {LowessResultWasm} The result of the smoothing.
316
334
  * @param {Float64Array} x
317
335
  * @param {Float64Array} y
318
336
  * @param {any} options
@@ -329,26 +347,72 @@ export function smooth(x, y, options) {
329
347
  function __wbg_get_imports() {
330
348
  const import0 = {
331
349
  __proto__: null,
332
- __wbg___wbindgen_boolean_get_bbbb1c18aa2f5e25: function(arg0) {
350
+ __wbg_Error_83742b46f01ce22d: function(arg0, arg1) {
351
+ const ret = Error(getStringFromWasm0(arg0, arg1));
352
+ return ret;
353
+ },
354
+ __wbg_Number_a5a435bd7bbec835: function(arg0) {
355
+ const ret = Number(arg0);
356
+ return ret;
357
+ },
358
+ __wbg___wbindgen_bigint_get_as_i64_447a76b5c6ef7bda: function(arg0, arg1) {
359
+ const v = arg1;
360
+ const ret = typeof(v) === 'bigint' ? v : undefined;
361
+ getDataViewMemory0().setBigInt64(arg0 + 8 * 1, isLikeNone(ret) ? BigInt(0) : ret, true);
362
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
363
+ },
364
+ __wbg___wbindgen_boolean_get_c0f3f60bac5a78d1: function(arg0) {
333
365
  const v = arg0;
334
366
  const ret = typeof(v) === 'boolean' ? v : undefined;
335
367
  return isLikeNone(ret) ? 0xFFFFFF : ret ? 1 : 0;
336
368
  },
337
- __wbg___wbindgen_is_null_ac34f5003991759a: function(arg0) {
369
+ __wbg___wbindgen_debug_string_5398f5bb970e0daa: function(arg0, arg1) {
370
+ const ret = debugString(arg1);
371
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
372
+ const len1 = WASM_VECTOR_LEN;
373
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
374
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
375
+ },
376
+ __wbg___wbindgen_in_41dbb8413020e076: function(arg0, arg1) {
377
+ const ret = arg0 in arg1;
378
+ return ret;
379
+ },
380
+ __wbg___wbindgen_is_bigint_e2141d4f045b7eda: function(arg0) {
381
+ const ret = typeof(arg0) === 'bigint';
382
+ return ret;
383
+ },
384
+ __wbg___wbindgen_is_function_3c846841762788c1: function(arg0) {
385
+ const ret = typeof(arg0) === 'function';
386
+ return ret;
387
+ },
388
+ __wbg___wbindgen_is_null_0b605fc6b167c56f: function(arg0) {
338
389
  const ret = arg0 === null;
339
390
  return ret;
340
391
  },
341
- __wbg___wbindgen_is_undefined_9e4d92534c42d778: function(arg0) {
392
+ __wbg___wbindgen_is_object_781bc9f159099513: function(arg0) {
393
+ const val = arg0;
394
+ const ret = typeof(val) === 'object' && val !== null;
395
+ return ret;
396
+ },
397
+ __wbg___wbindgen_is_undefined_52709e72fb9f179c: function(arg0) {
342
398
  const ret = arg0 === undefined;
343
399
  return ret;
344
400
  },
345
- __wbg___wbindgen_number_get_8ff4255516ccad3e: function(arg0, arg1) {
401
+ __wbg___wbindgen_jsval_eq_ee31bfad3e536463: function(arg0, arg1) {
402
+ const ret = arg0 === arg1;
403
+ return ret;
404
+ },
405
+ __wbg___wbindgen_jsval_loose_eq_5bcc3bed3c69e72b: function(arg0, arg1) {
406
+ const ret = arg0 == arg1;
407
+ return ret;
408
+ },
409
+ __wbg___wbindgen_number_get_34bb9d9dcfa21373: function(arg0, arg1) {
346
410
  const obj = arg1;
347
411
  const ret = typeof(obj) === 'number' ? obj : undefined;
348
412
  getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true);
349
413
  getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
350
414
  },
351
- __wbg___wbindgen_string_get_72fb696202c56729: function(arg0, arg1) {
415
+ __wbg___wbindgen_string_get_395e606bd0ee4427: function(arg0, arg1) {
352
416
  const obj = arg1;
353
417
  const ret = typeof(obj) === 'string' ? obj : undefined;
354
418
  var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -356,32 +420,117 @@ function __wbg_get_imports() {
356
420
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
357
421
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
358
422
  },
359
- __wbg___wbindgen_throw_be289d5034ed271b: function(arg0, arg1) {
423
+ __wbg___wbindgen_throw_6ddd609b62940d55: function(arg0, arg1) {
360
424
  throw new Error(getStringFromWasm0(arg0, arg1));
361
425
  },
362
- __wbg_from_bddd64e7d5ff6941: function(arg0) {
363
- const ret = Array.from(arg0);
426
+ __wbg_call_e133b57c9155d22c: function() { return handleError(function (arg0, arg1) {
427
+ const ret = arg0.call(arg1);
364
428
  return ret;
365
- },
366
- __wbg_get_9b94d73e6221f75c: function(arg0, arg1) {
367
- const ret = arg0[arg1 >>> 0];
429
+ }, arguments); },
430
+ __wbg_done_08ce71ee07e3bd17: function(arg0) {
431
+ const ret = arg0.done;
368
432
  return ret;
369
433
  },
370
- __wbg_get_b3ed3ad4be2bc8ac: function() { return handleError(function (arg0, arg1) {
434
+ __wbg_error_a6fa202b58aa1cd3: function(arg0, arg1) {
435
+ let deferred0_0;
436
+ let deferred0_1;
437
+ try {
438
+ deferred0_0 = arg0;
439
+ deferred0_1 = arg1;
440
+ console.error(getStringFromWasm0(arg0, arg1));
441
+ } finally {
442
+ wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
443
+ }
444
+ },
445
+ __wbg_get_326e41e095fb2575: function() { return handleError(function (arg0, arg1) {
371
446
  const ret = Reflect.get(arg0, arg1);
372
447
  return ret;
373
448
  }, arguments); },
374
- __wbg_length_35a7bace40f36eac: function(arg0) {
449
+ __wbg_get_unchecked_329cfe50afab7352: function(arg0, arg1) {
450
+ const ret = arg0[arg1 >>> 0];
451
+ return ret;
452
+ },
453
+ __wbg_get_with_ref_key_6412cf3094599694: function(arg0, arg1) {
454
+ const ret = arg0[arg1];
455
+ return ret;
456
+ },
457
+ __wbg_instanceof_ArrayBuffer_101e2bf31071a9f6: function(arg0) {
458
+ let result;
459
+ try {
460
+ result = arg0 instanceof ArrayBuffer;
461
+ } catch (_) {
462
+ result = false;
463
+ }
464
+ const ret = result;
465
+ return ret;
466
+ },
467
+ __wbg_instanceof_Uint8Array_740438561a5b956d: function(arg0) {
468
+ let result;
469
+ try {
470
+ result = arg0 instanceof Uint8Array;
471
+ } catch (_) {
472
+ result = false;
473
+ }
474
+ const ret = result;
475
+ return ret;
476
+ },
477
+ __wbg_isArray_33b91feb269ff46e: function(arg0) {
478
+ const ret = Array.isArray(arg0);
479
+ return ret;
480
+ },
481
+ __wbg_isSafeInteger_ecd6a7f9c3e053cd: function(arg0) {
482
+ const ret = Number.isSafeInteger(arg0);
483
+ return ret;
484
+ },
485
+ __wbg_iterator_d8f549ec8fb061b1: function() {
486
+ const ret = Symbol.iterator;
487
+ return ret;
488
+ },
489
+ __wbg_length_550d8a396009cd38: function(arg0) {
490
+ const ret = arg0.length;
491
+ return ret;
492
+ },
493
+ __wbg_length_b3416cf66a5452c8: function(arg0) {
375
494
  const ret = arg0.length;
376
495
  return ret;
377
496
  },
378
- __wbg_length_f7386240689107f3: function(arg0) {
497
+ __wbg_length_ea16607d7b61445b: function(arg0) {
379
498
  const ret = arg0.length;
380
499
  return ret;
381
500
  },
382
- __wbg_prototypesetcall_aefe6319f589ab4b: function(arg0, arg1, arg2) {
501
+ __wbg_new_227d7c05414eb861: function() {
502
+ const ret = new Error();
503
+ return ret;
504
+ },
505
+ __wbg_new_5f486cdf45a04d78: function(arg0) {
506
+ const ret = new Uint8Array(arg0);
507
+ return ret;
508
+ },
509
+ __wbg_next_11b99ee6237339e3: function() { return handleError(function (arg0) {
510
+ const ret = arg0.next();
511
+ return ret;
512
+ }, arguments); },
513
+ __wbg_next_e01a967809d1aa68: function(arg0) {
514
+ const ret = arg0.next;
515
+ return ret;
516
+ },
517
+ __wbg_prototypesetcall_79daf97fb14c7a19: function(arg0, arg1, arg2) {
383
518
  Float64Array.prototype.set.call(getArrayF64FromWasm0(arg0, arg1), arg2);
384
519
  },
520
+ __wbg_prototypesetcall_d62e5099504357e6: function(arg0, arg1, arg2) {
521
+ Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
522
+ },
523
+ __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
524
+ const ret = arg1.stack;
525
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
526
+ const len1 = WASM_VECTOR_LEN;
527
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
528
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
529
+ },
530
+ __wbg_value_21fc78aab0322612: function(arg0) {
531
+ const ret = arg0.value;
532
+ return ret;
533
+ },
385
534
  __wbindgen_cast_0000000000000001: function(arg0, arg1) {
386
535
  // Cast intrinsic for `Ref(Slice(F64)) -> NamedExternref("Float64Array")`.
387
536
  const ret = getArrayF64FromWasm0(arg0, arg1);
@@ -392,6 +541,11 @@ function __wbg_get_imports() {
392
541
  const ret = getStringFromWasm0(arg0, arg1);
393
542
  return ret;
394
543
  },
544
+ __wbindgen_cast_0000000000000003: function(arg0) {
545
+ // Cast intrinsic for `U64 -> Externref`.
546
+ const ret = BigInt.asUintN(64, arg0);
547
+ return ret;
548
+ },
395
549
  __wbindgen_init_externref_table: function() {
396
550
  const table = wasm.__wbindgen_externrefs;
397
551
  const offset = table.grow(4);
@@ -427,11 +581,81 @@ function addToExternrefTable0(obj) {
427
581
  return idx;
428
582
  }
429
583
 
584
+ function debugString(val) {
585
+ // primitive types
586
+ const type = typeof val;
587
+ if (type == 'number' || type == 'boolean' || val == null) {
588
+ return `${val}`;
589
+ }
590
+ if (type == 'string') {
591
+ return `"${val}"`;
592
+ }
593
+ if (type == 'symbol') {
594
+ const description = val.description;
595
+ if (description == null) {
596
+ return 'Symbol';
597
+ } else {
598
+ return `Symbol(${description})`;
599
+ }
600
+ }
601
+ if (type == 'function') {
602
+ const name = val.name;
603
+ if (typeof name == 'string' && name.length > 0) {
604
+ return `Function(${name})`;
605
+ } else {
606
+ return 'Function';
607
+ }
608
+ }
609
+ // objects
610
+ if (Array.isArray(val)) {
611
+ const length = val.length;
612
+ let debug = '[';
613
+ if (length > 0) {
614
+ debug += debugString(val[0]);
615
+ }
616
+ for(let i = 1; i < length; i++) {
617
+ debug += ', ' + debugString(val[i]);
618
+ }
619
+ debug += ']';
620
+ return debug;
621
+ }
622
+ // Test for built-in
623
+ const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
624
+ let className;
625
+ if (builtInMatches && builtInMatches.length > 1) {
626
+ className = builtInMatches[1];
627
+ } else {
628
+ // Failed to match the standard '[object ClassName]'
629
+ return toString.call(val);
630
+ }
631
+ if (className == 'Object') {
632
+ // we're a user defined class or Object
633
+ // JSON.stringify avoids problems with cycles, and is generally much
634
+ // easier than looping through ownProperties of `val`.
635
+ try {
636
+ return 'Object(' + JSON.stringify(val) + ')';
637
+ } catch (_) {
638
+ return 'Object';
639
+ }
640
+ }
641
+ // errors
642
+ if (val instanceof Error) {
643
+ return `${val.name}: ${val.message}\n${val.stack}`;
644
+ }
645
+ // TODO we could test for more things here, like `Set`s and `Map`s.
646
+ return className;
647
+ }
648
+
430
649
  function getArrayF64FromWasm0(ptr, len) {
431
650
  ptr = ptr >>> 0;
432
651
  return getFloat64ArrayMemory0().subarray(ptr / 8, ptr / 8 + len);
433
652
  }
434
653
 
654
+ function getArrayU8FromWasm0(ptr, len) {
655
+ ptr = ptr >>> 0;
656
+ return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
657
+ }
658
+
435
659
  let cachedDataViewMemory0 = null;
436
660
  function getDataViewMemory0() {
437
661
  if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
Binary file
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "Amir Valizadeh <thisisamirv@gmail.com>"
6
6
  ],
7
7
  "description": "High-performance LOWESS (Locally Weighted Scatterplot Smoothing)",
8
- "version": "0.99.9",
8
+ "version": "1.2.0",
9
9
  "license": "MIT OR Apache-2.0",
10
10
  "repository": {
11
11
  "type": "git",