linreg-core 0.6.0 → 0.6.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/README.md +518 -8
- package/linreg_core.d.ts +226 -0
- package/linreg_core.js +384 -0
- package/linreg_core_bg.wasm +0 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/linreg-core)
|
|
8
8
|
[](https://pypi.org/project/linreg-core/)
|
|
9
9
|
[](https://docs.rs/linreg-core)
|
|
10
|
+
[](https://jesse-anderson.net/linreg-core/)
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
A lightweight, self-contained linear regression library written in Rust. Compiles to WebAssembly for browser use, Python bindings via PyO3, or runs as a native Rust crate.
|
|
@@ -37,6 +38,8 @@ A lightweight, self-contained linear regression library written in Rust. Compile
|
|
|
37
38
|
- **Lasso Regression:** L1-regularized regression via coordinate descent with automatic variable selection, convergence tracking, model selection criteria
|
|
38
39
|
- **Elastic Net:** Combined L1 + L2 regularization for variable selection with multicollinearity handling, active set convergence, model selection criteria
|
|
39
40
|
- **LOESS:** Locally estimated scatterplot smoothing for non-parametric curve fitting with configurable span, polynomial degree, and robust fitting
|
|
41
|
+
- **WLS (Weighted Least Squares):** Regression with observation weights for heteroscedastic data, includes confidence intervals
|
|
42
|
+
- **K-Fold Cross Validation:** Model evaluation and hyperparameter tuning for all regression types (OLS, Ridge, Lasso, Elastic Net) with customizable folds, shuffling, and seeding
|
|
40
43
|
- **Lambda Path Generation:** Create regularization paths for cross-validation
|
|
41
44
|
|
|
42
45
|
### Model Statistics
|
|
@@ -64,7 +67,7 @@ Add to your `Cargo.toml`:
|
|
|
64
67
|
|
|
65
68
|
```toml
|
|
66
69
|
[dependencies]
|
|
67
|
-
linreg-core = { version = "0.
|
|
70
|
+
linreg-core = { version = "0.6", default-features = false }
|
|
68
71
|
```
|
|
69
72
|
|
|
70
73
|
### OLS Regression (Rust)
|
|
@@ -198,35 +201,199 @@ fn main() -> Result<(), linreg_core::Error> {
|
|
|
198
201
|
```rust
|
|
199
202
|
use linreg_core::diagnostics::{
|
|
200
203
|
breusch_pagan_test, durbin_watson_test, jarque_bera_test,
|
|
201
|
-
shapiro_wilk_test,
|
|
204
|
+
shapiro_wilk_test, rainbow_test, harvey_collier_test,
|
|
205
|
+
white_test, anderson_darling_test, breusch_godfrey_test,
|
|
206
|
+
cooks_distance_test, dfbetas_test, dffits_test, vif_test,
|
|
207
|
+
reset_test, BGTestType, RainbowMethod, ResetType, WhiteMethod
|
|
202
208
|
};
|
|
203
209
|
|
|
204
210
|
fn main() -> Result<(), linreg_core::Error> {
|
|
205
211
|
let y = vec![/* your data */];
|
|
206
212
|
let x = vec![vec![/* predictor 1 */], vec![/* predictor 2 */]];
|
|
207
213
|
|
|
208
|
-
// Heteroscedasticity
|
|
214
|
+
// Heteroscedasticity tests
|
|
209
215
|
let bp = breusch_pagan_test(&y, &x)?;
|
|
210
216
|
println!("Breusch-Pagan: LM={:.4}, p={:.4}", bp.statistic, bp.p_value);
|
|
211
217
|
|
|
212
|
-
|
|
218
|
+
let white = white_test(&y, &x, WhiteMethod::R)?;
|
|
219
|
+
println!("White: statistic={:.4}, p={:.4}", white.statistic, white.p_value);
|
|
220
|
+
|
|
221
|
+
// Autocorrelation tests
|
|
213
222
|
let dw = durbin_watson_test(&y, &x)?;
|
|
214
223
|
println!("Durbin-Watson: {:.4}", dw.statistic);
|
|
215
224
|
|
|
216
|
-
|
|
225
|
+
let bg = breusch_godfrey_test(&y, &x, 2, BGTestType::Chisq)?;
|
|
226
|
+
println!("Breusch-Godfrey (order 2): statistic={:.4}, p={:.4}", bg.statistic, bg.p_value);
|
|
227
|
+
|
|
228
|
+
// Normality tests
|
|
217
229
|
let jb = jarque_bera_test(&y, &x)?;
|
|
218
230
|
println!("Jarque-Bera: JB={:.4}, p={:.4}", jb.statistic, jb.p_value);
|
|
219
231
|
|
|
220
|
-
|
|
232
|
+
let sw = shapiro_wilk_test(&y, &x)?;
|
|
233
|
+
println!("Shapiro-Wilk: W={:.4}, p={:.4}", sw.statistic, sw.p_value);
|
|
234
|
+
|
|
235
|
+
let ad = anderson_darling_test(&y, &x)?;
|
|
236
|
+
println!("Anderson-Darling: A={:.4}, p={:.4}", ad.statistic, ad.p_value);
|
|
237
|
+
|
|
238
|
+
// Linearity tests
|
|
221
239
|
let rainbow = rainbow_test(&y, &x, 0.5, RainbowMethod::R)?;
|
|
222
240
|
println!("Rainbow: F={:.4}, p={:.4}",
|
|
223
241
|
rainbow.r_result.as_ref().unwrap().statistic,
|
|
224
242
|
rainbow.r_result.as_ref().unwrap().p_value);
|
|
225
243
|
|
|
244
|
+
let hc = harvey_collier_test(&y, &x)?;
|
|
245
|
+
println!("Harvey-Collier: t={:.4}, p={:.4}", hc.statistic, hc.p_value);
|
|
246
|
+
|
|
247
|
+
let reset = reset_test(&y, &x, &[2, 3], ResetType::Fitted)?;
|
|
248
|
+
println!("RESET: F={:.4}, p={:.4}", reset.f_statistic, reset.p_value);
|
|
249
|
+
|
|
250
|
+
// Influence diagnostics
|
|
251
|
+
let cd = cooks_distance_test(&y, &x)?;
|
|
252
|
+
println!("Cook's Distance: {} influential points", cd.influential_4_over_n.len());
|
|
253
|
+
|
|
254
|
+
let dfbetas = dfbetas_test(&y, &x)?;
|
|
255
|
+
println!("DFBETAS: {} influential observations", dfbetas.influential_observations.len());
|
|
256
|
+
|
|
257
|
+
let dffits = dffits_test(&y, &x)?;
|
|
258
|
+
println!("DFFITS: {} influential observations", dffits.influential_observations.len());
|
|
259
|
+
|
|
260
|
+
// Multicollinearity
|
|
261
|
+
let vif = vif_test(&y, &x)?;
|
|
262
|
+
println!("VIF: {:?}", vif.vif_values);
|
|
263
|
+
|
|
264
|
+
Ok(())
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### WLS Regression (Rust)
|
|
269
|
+
|
|
270
|
+
```rust,no_run
|
|
271
|
+
use linreg_core::weighted_regression::wls_regression;
|
|
272
|
+
|
|
273
|
+
fn main() -> Result<(), linreg_core::Error> {
|
|
274
|
+
let y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
|
|
275
|
+
let x1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
|
|
276
|
+
|
|
277
|
+
// Equal weights = OLS
|
|
278
|
+
let weights = vec![1.0, 1.0, 1.0, 1.0, 1.0];
|
|
279
|
+
|
|
280
|
+
let fit = wls_regression(&y, &[x1], &weights)?;
|
|
281
|
+
|
|
282
|
+
println!("Intercept: {} (SE: {}, t: {}, p: {})",
|
|
283
|
+
fit.coefficients[0],
|
|
284
|
+
fit.standard_errors[0],
|
|
285
|
+
fit.t_statistics[0],
|
|
286
|
+
fit.p_values[0]
|
|
287
|
+
);
|
|
288
|
+
println!("F-statistic: {} (p: {})", fit.f_statistic, fit.f_p_value);
|
|
289
|
+
println!("R-squared: {:.4}", fit.r_squared);
|
|
290
|
+
|
|
291
|
+
// Access confidence intervals
|
|
292
|
+
for (i, (&coef, &lower, &upper)) in fit.coefficients.iter()
|
|
293
|
+
.zip(fit.conf_int_lower.iter())
|
|
294
|
+
.zip(fit.conf_int_upper.iter())
|
|
295
|
+
.enumerate()
|
|
296
|
+
{
|
|
297
|
+
println!("Coefficient {}: [{}, {}]", i, lower, upper);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
Ok(())
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### LOESS Regression (Rust)
|
|
305
|
+
|
|
306
|
+
```rust,no_run
|
|
307
|
+
use linreg_core::loess::{loess_fit, LoessOptions};
|
|
308
|
+
|
|
309
|
+
fn main() -> Result<(), linreg_core::Error> {
|
|
310
|
+
// Single predictor only
|
|
311
|
+
let x = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
|
|
312
|
+
let y = vec![1.0, 3.5, 4.8, 6.2, 8.5, 11.0, 13.2, 14.8, 17.5, 19.0, 22.0];
|
|
313
|
+
|
|
314
|
+
// Default options: span=0.75, degree=2, robust iterations=0
|
|
315
|
+
let options = LoessOptions::default();
|
|
316
|
+
|
|
317
|
+
let result = loess_fit(&y, &[x], &options)?;
|
|
318
|
+
|
|
319
|
+
println!("Fitted values: {:?}", result.fitted_values);
|
|
320
|
+
println!("Residuals: {:?}", result.residuals);
|
|
321
|
+
|
|
226
322
|
Ok(())
|
|
227
323
|
}
|
|
228
324
|
```
|
|
229
325
|
|
|
326
|
+
**Custom LOESS options:**
|
|
327
|
+
|
|
328
|
+
```rust,no_run
|
|
329
|
+
use linreg_core::loess::{loess_fit, LoessOptions, LoessSurface};
|
|
330
|
+
|
|
331
|
+
let options = LoessOptions {
|
|
332
|
+
span: 0.5, // Smoothing parameter (0-1, smaller = less smooth)
|
|
333
|
+
degree: 1, // Polynomial degree (0=constant, 1=linear, 2=quadratic)
|
|
334
|
+
surface: LoessSurface::Direct, // Note: only "direct" is currently supported; "interpolate" is planned
|
|
335
|
+
robust_iterations: 3, // Number of robust fitting iterations (0 = disabled)
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
let result = loess_fit(&y, &[x], &options)?;
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### K-Fold Cross Validation (Rust)
|
|
342
|
+
|
|
343
|
+
Cross-validation is used for model evaluation and hyperparameter tuning. The library supports K-Fold CV for all regression types:
|
|
344
|
+
|
|
345
|
+
```rust,no_run
|
|
346
|
+
use linreg_core::cross_validation::{kfold_cv_ols, kfold_cv_ridge, kfold_cv_lasso, kfold_cv_elastic_net, KFoldOptions};
|
|
347
|
+
|
|
348
|
+
fn main() -> Result<(), linreg_core::Error> {
|
|
349
|
+
let y = vec![2.5, 3.7, 4.2, 5.1, 6.3, 7.0, 7.5, 8.1];
|
|
350
|
+
let x1 = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
|
351
|
+
let x2 = vec![2.0, 4.0, 5.0, 4.0, 3.0, 4.5, 5.5, 6.0];
|
|
352
|
+
let names = vec!["Intercept".to_string(), "X1".to_string(), "X2".to_string()];
|
|
353
|
+
|
|
354
|
+
// Configure CV options
|
|
355
|
+
let options = KFoldOptions {
|
|
356
|
+
n_folds: 5,
|
|
357
|
+
shuffle: true,
|
|
358
|
+
seed: Some(42), // For reproducibility
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// OLS cross-validation
|
|
362
|
+
let ols_cv = kfold_cv_ols(&y, &[x1.clone(), x2.clone()], &names, &options)?;
|
|
363
|
+
println!("OLS CV RMSE: {:.4} (±{:.4})", ols_cv.mean_rmse, ols_cv.std_rmse);
|
|
364
|
+
println!("OLS CV R²: {:.4} (±{:.4})", ols_cv.mean_r_squared, ols_cv.std_r_squared);
|
|
365
|
+
|
|
366
|
+
// Ridge cross-validation (for lambda selection)
|
|
367
|
+
let lambda = 1.0;
|
|
368
|
+
let ridge_cv = kfold_cv_ridge(&[x1.clone(), x2.clone()], &y, lambda, true, &options)?;
|
|
369
|
+
println!("Ridge CV RMSE: {:.4}", ridge_cv.mean_rmse);
|
|
370
|
+
|
|
371
|
+
// Lasso cross-validation
|
|
372
|
+
let lasso_cv = kfold_cv_lasso(&[x1.clone(), x2.clone()], &y, 0.1, true, &options)?;
|
|
373
|
+
println!("Lasso CV RMSE: {:.4}", lasso_cv.mean_rmse);
|
|
374
|
+
|
|
375
|
+
// Elastic Net cross-validation
|
|
376
|
+
let enet_cv = kfold_cv_elastic_net(&[x1, x2], &y, 0.1, 0.5, true, &options)?;
|
|
377
|
+
println!("Elastic Net CV RMSE: {:.4}", enet_cv.mean_rmse);
|
|
378
|
+
|
|
379
|
+
// Access per-fold results
|
|
380
|
+
for fold in &ols_cv.fold_results {
|
|
381
|
+
println!("Fold {}: train={}, test={}, R²={:.4}",
|
|
382
|
+
fold.fold_index, fold.train_size, fold.test_size, fold.r_squared);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
Ok(())
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**CV Result fields:**
|
|
390
|
+
- `mean_rmse`, `std_rmse` - Mean and std of RMSE across folds
|
|
391
|
+
- `mean_mae`, `std_mae` - Mean and std of MAE across folds
|
|
392
|
+
- `mean_r_squared`, `std_r_squared` - Mean and std of R² across folds
|
|
393
|
+
- `mean_train_r_squared` - Mean training R² (for overfitting detection)
|
|
394
|
+
- `fold_results` - Per-fold metrics (train/test sizes, MSE, RMSE, MAE, R²)
|
|
395
|
+
- `fold_coefficients` - Coefficients from each fold (for stability analysis)
|
|
396
|
+
|
|
230
397
|
### Lambda Path Generation (Rust)
|
|
231
398
|
|
|
232
399
|
```rust,no_run
|
|
@@ -250,10 +417,34 @@ for &lambda in lambdas.iter() {
|
|
|
250
417
|
}
|
|
251
418
|
```
|
|
252
419
|
|
|
420
|
+
### Model Save/Load (Rust)
|
|
421
|
+
|
|
422
|
+
All trained models can be saved to disk and loaded back later:
|
|
423
|
+
|
|
424
|
+
```rust,no_run
|
|
425
|
+
use linreg_core::{ModelSave, ModelLoad};
|
|
426
|
+
|
|
427
|
+
// Train a model
|
|
428
|
+
let result = ols_regression(&y, &[x1], &names)?;
|
|
429
|
+
|
|
430
|
+
// Save to file
|
|
431
|
+
result.save("my_model.json")?;
|
|
432
|
+
|
|
433
|
+
// Or with a custom name
|
|
434
|
+
result.save_with_name("my_model.json", Some("My Housing Model".to_string()))?;
|
|
435
|
+
|
|
436
|
+
// Load back
|
|
437
|
+
let loaded = linreg_core::core::RegressionOutput::load("my_model.json")?;
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
The same `save()` and `load()` methods work for all model types: `RegressionOutput`, `RidgeFit`, `LassoFit`, `ElasticNetFit`, `WlsFit`, and `LoessFit`.
|
|
441
|
+
|
|
253
442
|
---
|
|
254
443
|
|
|
255
444
|
## WebAssembly Usage
|
|
256
445
|
|
|
446
|
+
**[Live Demo →](https://jesse-anderson.net/linreg-core/)**
|
|
447
|
+
|
|
257
448
|
Build with wasm-pack:
|
|
258
449
|
|
|
259
450
|
```bash
|
|
@@ -360,6 +551,24 @@ console.log("Lambda sequence:", path.lambda_path);
|
|
|
360
551
|
console.log("Lambda max:", path.lambda_max);
|
|
361
552
|
```
|
|
362
553
|
|
|
554
|
+
### WLS Regression (WASM)
|
|
555
|
+
|
|
556
|
+
```javascript
|
|
557
|
+
const result = JSON.parse(wls_regression(
|
|
558
|
+
JSON.stringify([2, 4, 6, 8, 10]),
|
|
559
|
+
JSON.stringify([[1, 2, 3, 4, 5]]),
|
|
560
|
+
JSON.stringify([1, 1, 1, 1, 1]) // weights (equal weights = OLS)
|
|
561
|
+
));
|
|
562
|
+
|
|
563
|
+
console.log("Coefficients:", result.coefficients);
|
|
564
|
+
console.log("Standard errors:", result.standard_errors);
|
|
565
|
+
console.log("P-values:", result.p_values);
|
|
566
|
+
console.log("R-squared:", result.r_squared);
|
|
567
|
+
console.log("F-statistic:", result.f_statistic);
|
|
568
|
+
console.log("Confidence intervals (lower):", result.conf_int_lower);
|
|
569
|
+
console.log("Confidence intervals (upper):", result.conf_int_upper);
|
|
570
|
+
```
|
|
571
|
+
|
|
363
572
|
### LOESS Regression (WASM)
|
|
364
573
|
|
|
365
574
|
```javascript
|
|
@@ -368,7 +577,7 @@ const result = JSON.parse(loess_fit(
|
|
|
368
577
|
JSON.stringify(x[0]), // Single predictor only (flattened array)
|
|
369
578
|
0.5, // span (smoothing parameter: 0-1)
|
|
370
579
|
1, // degree (0=constant, 1=linear, 2=quadratic)
|
|
371
|
-
"direct", // surface method ("direct"
|
|
580
|
+
"direct", // surface method ("direct" only; "interpolate" is planned)
|
|
372
581
|
0 // robust iterations (0=disabled, >0=number of iterations)
|
|
373
582
|
));
|
|
374
583
|
|
|
@@ -376,6 +585,64 @@ console.log("Fitted values:", result.fitted_values);
|
|
|
376
585
|
console.log("Residuals:", result.residuals);
|
|
377
586
|
```
|
|
378
587
|
|
|
588
|
+
### K-Fold Cross Validation (WASM)
|
|
589
|
+
|
|
590
|
+
```javascript
|
|
591
|
+
// OLS cross-validation
|
|
592
|
+
const ols_cv = JSON.parse(kfold_cv_ols(
|
|
593
|
+
JSON.stringify(y),
|
|
594
|
+
JSON.stringify(x),
|
|
595
|
+
JSON.stringify(["Intercept", "X1", "X2"]),
|
|
596
|
+
5, // n_folds
|
|
597
|
+
"true", // shuffle (JSON boolean)
|
|
598
|
+
"42" // seed (JSON string number, or "null" for no seed)
|
|
599
|
+
));
|
|
600
|
+
|
|
601
|
+
console.log("OLS CV RMSE:", ols_cv.mean_rmse, "±", ols_cv.std_rmse);
|
|
602
|
+
console.log("OLS CV R²:", ols_cv.mean_r_squared, "±", ols_cv.std_r_squared);
|
|
603
|
+
|
|
604
|
+
// Ridge cross-validation
|
|
605
|
+
const ridge_cv = JSON.parse(kfold_cv_ridge(
|
|
606
|
+
JSON.stringify(y),
|
|
607
|
+
JSON.stringify(x),
|
|
608
|
+
1.0, // lambda
|
|
609
|
+
true, // standardize
|
|
610
|
+
5, // n_folds
|
|
611
|
+
"true", // shuffle
|
|
612
|
+
"42" // seed
|
|
613
|
+
));
|
|
614
|
+
|
|
615
|
+
// Lasso cross-validation
|
|
616
|
+
const lasso_cv = JSON.parse(kfold_cv_lasso(
|
|
617
|
+
JSON.stringify(y),
|
|
618
|
+
JSON.stringify(x),
|
|
619
|
+
0.1, // lambda
|
|
620
|
+
true, // standardize
|
|
621
|
+
5, // n_folds
|
|
622
|
+
"true", // shuffle
|
|
623
|
+
"42" // seed
|
|
624
|
+
));
|
|
625
|
+
|
|
626
|
+
// Elastic Net cross-validation
|
|
627
|
+
const enet_cv = JSON.parse(kfold_cv_elastic_net(
|
|
628
|
+
JSON.stringify(y),
|
|
629
|
+
JSON.stringify(x),
|
|
630
|
+
0.1, // lambda
|
|
631
|
+
0.5, // alpha (0 = Ridge, 1 = Lasso)
|
|
632
|
+
true, // standardize
|
|
633
|
+
5, // n_folds
|
|
634
|
+
"true", // shuffle
|
|
635
|
+
"42" // seed
|
|
636
|
+
));
|
|
637
|
+
|
|
638
|
+
// Access per-fold results
|
|
639
|
+
ols_cv.fold_results.forEach(fold => {
|
|
640
|
+
console.log(`Fold ${fold.fold_index}: R²=${fold.r_squared.toFixed(4)}`);
|
|
641
|
+
});
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**Note:** In WASM, boolean and seed parameters are passed as JSON strings. Use `"true"`/`"false"` for shuffle and `"42"` or `"null"` for seed.
|
|
645
|
+
|
|
379
646
|
### Diagnostic Tests (WASM)
|
|
380
647
|
|
|
381
648
|
```javascript
|
|
@@ -524,6 +791,43 @@ const version = get_version(); // e.g., "0.5.0"
|
|
|
524
791
|
const msg = test(); // "Rust WASM is working!"
|
|
525
792
|
```
|
|
526
793
|
|
|
794
|
+
### Model Serialization (WASM)
|
|
795
|
+
|
|
796
|
+
```javascript
|
|
797
|
+
// Train a model
|
|
798
|
+
const resultJson = ols_regression(
|
|
799
|
+
JSON.stringify(y),
|
|
800
|
+
JSON.stringify(x),
|
|
801
|
+
JSON.stringify(names)
|
|
802
|
+
);
|
|
803
|
+
const result = JSON.parse(resultJson);
|
|
804
|
+
|
|
805
|
+
// Serialize with metadata
|
|
806
|
+
const serialized = serialize_model(
|
|
807
|
+
resultJson, // model JSON
|
|
808
|
+
"OLS", // model type: "OLS", "Ridge", "Lasso", "ElasticNet", "WLS", "LOESS"
|
|
809
|
+
"My Model" // optional name (null to omit)
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
// Get metadata without loading full model
|
|
813
|
+
const metadataJson = get_model_metadata(serialized);
|
|
814
|
+
const metadata = JSON.parse(metadataJson);
|
|
815
|
+
console.log("Model type:", metadata.model_type);
|
|
816
|
+
console.log("Created:", metadata.created_at);
|
|
817
|
+
|
|
818
|
+
// Deserialize to get model data back
|
|
819
|
+
const modelJson = deserialize_model(serialized);
|
|
820
|
+
const model = JSON.parse(modelJson);
|
|
821
|
+
|
|
822
|
+
// Download in browser
|
|
823
|
+
const blob = new Blob([serialized], { type: 'application/json' });
|
|
824
|
+
const url = URL.createObjectURL(blob);
|
|
825
|
+
const a = document.createElement('a');
|
|
826
|
+
a.href = url;
|
|
827
|
+
a.download = 'model.json';
|
|
828
|
+
a.click();
|
|
829
|
+
```
|
|
830
|
+
|
|
527
831
|
### Domain Security (WASM)
|
|
528
832
|
|
|
529
833
|
Optional domain restriction via build-time environment variable:
|
|
@@ -654,6 +958,20 @@ print(f"AIC: {result.aic}")
|
|
|
654
958
|
print(f"BIC: {result.bic}")
|
|
655
959
|
```
|
|
656
960
|
|
|
961
|
+
### LOESS Regression (Python)
|
|
962
|
+
|
|
963
|
+
```python
|
|
964
|
+
result = linreg_core.loess_fit(
|
|
965
|
+
y, # Single predictor only
|
|
966
|
+
[0.5], # span (smoothing parameter: 0-1)
|
|
967
|
+
2, # degree (0=constant, 1=linear, 2=quadratic)
|
|
968
|
+
"direct", # surface ("direct" only; "interpolate" is planned)
|
|
969
|
+
0 # robust iterations (0=disabled, >0=number of iterations)
|
|
970
|
+
)
|
|
971
|
+
print(f"Fitted values: {result.fitted_values}")
|
|
972
|
+
print(f"Residuals: {result.residuals}")
|
|
973
|
+
```
|
|
974
|
+
|
|
657
975
|
### Lambda Path Generation (Python)
|
|
658
976
|
|
|
659
977
|
```python
|
|
@@ -767,6 +1085,23 @@ print(f"Numeric columns: {result.numeric_columns}")
|
|
|
767
1085
|
print(f"Data rows: {result.n_rows}")
|
|
768
1086
|
```
|
|
769
1087
|
|
|
1088
|
+
### Model Save/Load (Python)
|
|
1089
|
+
|
|
1090
|
+
```python
|
|
1091
|
+
# Train a model
|
|
1092
|
+
result = linreg_core.ols_regression(y, x, names)
|
|
1093
|
+
|
|
1094
|
+
# Save to file
|
|
1095
|
+
linreg_core.save_model(result, "my_model.json", name="My Housing Model")
|
|
1096
|
+
|
|
1097
|
+
# Load back
|
|
1098
|
+
loaded = linreg_core.load_model("my_model.json")
|
|
1099
|
+
print(f"R²: {loaded.r_squared}")
|
|
1100
|
+
print(f"Coefficients: {loaded.coefficients}")
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
The `save_model()` and `load_model()` functions work with all result types: `OLSResult`, `RidgeResult`, `LassoResult`, `ElasticNetResult`, `LoessResult`, and `WlsResult`.
|
|
1104
|
+
|
|
770
1105
|
---
|
|
771
1106
|
|
|
772
1107
|
## Feature Flags
|
|
@@ -780,7 +1115,7 @@ print(f"Data rows: {result.n_rows}")
|
|
|
780
1115
|
For native Rust without WASM overhead:
|
|
781
1116
|
|
|
782
1117
|
```toml
|
|
783
|
-
linreg-core = { version = "0.
|
|
1118
|
+
linreg-core = { version = "0.6", default-features = false }
|
|
784
1119
|
```
|
|
785
1120
|
|
|
786
1121
|
For Python bindings (built with maturin):
|
|
@@ -843,6 +1178,181 @@ This library is under active development and has not reached 1.0 stability. Whil
|
|
|
843
1178
|
|
|
844
1179
|
---
|
|
845
1180
|
|
|
1181
|
+
## Benchmarks
|
|
1182
|
+
|
|
1183
|
+
<details>
|
|
1184
|
+
<summary><strong>Click to expand v0.6.0 benchmark results</strong></summary>
|
|
1185
|
+
|
|
1186
|
+
Benchmark results run on Windows with `cargo bench --no-default-features`. Times are median values.
|
|
1187
|
+
|
|
1188
|
+
### Core Regression Benchmarks
|
|
1189
|
+
|
|
1190
|
+
| Benchmark | Size (n × p) | Time | Throughput |
|
|
1191
|
+
|-----------|--------------|------|------------|
|
|
1192
|
+
| OLS Regression | 10 × 2 | 12.46 µs | 802.71 Kelem/s |
|
|
1193
|
+
| OLS Regression | 50 × 3 | 53.72 µs | 930.69 Kelem/s |
|
|
1194
|
+
| OLS Regression | 100 × 5 | 211.09 µs | 473.73 Kelem/s |
|
|
1195
|
+
| OLS Regression | 500 × 10 | 7.46 ms | 67.04 Kelem/s |
|
|
1196
|
+
| OLS Regression | 1000 × 20 | 47.81 ms | 20.91 Kelem/s |
|
|
1197
|
+
| OLS Regression | 5000 × 50 | 2.86 s | 1.75 Kelem/s |
|
|
1198
|
+
| Ridge Regression | 50 × 3 | 9.61 µs | 5.20 Melem/s |
|
|
1199
|
+
| Ridge Regression | 100 × 5 | 70.41 µs | 1.42 Melem/s |
|
|
1200
|
+
| Ridge Regression | 500 × 10 | 842.37 µs | 593.56 Kelem/s |
|
|
1201
|
+
| Ridge Regression | 1000 × 20 | 1.38 ms | 724.71 Kelem/s |
|
|
1202
|
+
| Ridge Regression | 5000 × 50 | 10.25 ms | 487.78 Kelem/s |
|
|
1203
|
+
| Lasso Regression | 50 × 3 | 258.82 µs | 193.18 Kelem/s |
|
|
1204
|
+
| Lasso Regression | 100 × 5 | 247.89 µs | 403.41 Kelem/s |
|
|
1205
|
+
| Lasso Regression | 500 × 10 | 3.58 ms | 139.86 Kelem/s |
|
|
1206
|
+
| Lasso Regression | 1000 × 20 | 1.54 ms | 651.28 Kelem/s |
|
|
1207
|
+
| Lasso Regression | 5000 × 50 | 12.52 ms | 399.50 Kelem/s |
|
|
1208
|
+
| Elastic Net Regression | 50 × 3 | 46.15 µs | 1.08 Melem/s |
|
|
1209
|
+
| Elastic Net Regression | 100 × 5 | 358.07 µs | 279.27 Kelem/s |
|
|
1210
|
+
| Elastic Net Regression | 500 × 10 | 1.61 ms | 310.18 Kelem/s |
|
|
1211
|
+
| Elastic Net Regression | 1000 × 20 | 1.60 ms | 623.66 Kelem/s |
|
|
1212
|
+
| Elastic Net Regression | 5000 × 50 | 12.57 ms | 397.77 Kelem/s |
|
|
1213
|
+
| WLS Regression | 50 × 3 | 32.92 µs | 1.52 Melem/s |
|
|
1214
|
+
| WLS Regression | 100 × 5 | 155.30 µs | 643.93 Kelem/s |
|
|
1215
|
+
| WLS Regression | 500 × 10 | 6.63 ms | 75.37 Kelem/s |
|
|
1216
|
+
| WLS Regression | 1000 × 20 | 42.68 ms | 23.43 Kelem/s |
|
|
1217
|
+
| WLS Regression | 5000 × 50 | 2.64 s | 1.89 Kelem/s |
|
|
1218
|
+
| LOESS Fit | 50 × 1 | 132.83 µs | 376.42 Kelem/s |
|
|
1219
|
+
| LOESS Fit | 100 × 1 | 1.16 ms | 86.00 Kelem/s |
|
|
1220
|
+
| LOESS Fit | 500 × 1 | 28.42 ms | 17.59 Kelem/s |
|
|
1221
|
+
| LOESS Fit | 1000 × 1 | 113.00 ms | 8.85 Kelem/s |
|
|
1222
|
+
| LOESS Fit | 100 × 2 | 7.10 ms | 14.09 Kelem/s |
|
|
1223
|
+
| LOESS Fit | 500 × 2 | 1.05 s | 476.19 elem/s |
|
|
1224
|
+
|
|
1225
|
+
### Lambda Path & Elastic Net Path Benchmarks
|
|
1226
|
+
|
|
1227
|
+
| Benchmark | Size (n × p) | Time | Throughput |
|
|
1228
|
+
|-----------|--------------|------|------------|
|
|
1229
|
+
| Elastic Net Path | 100 × 5 | 198.60 ms | 503.52 elem/s |
|
|
1230
|
+
| Elastic Net Path | 500 × 10 | 69.46 ms | 7.20 Kelem/s |
|
|
1231
|
+
| Elastic Net Path | 1000 × 20 | 39.08 ms | 25.59 Kelem/s |
|
|
1232
|
+
| Make Lambda Path | 100 × 5 | 1.09 µs | 91.58 Melem/s |
|
|
1233
|
+
| Make Lambda Path | 500 × 10 | 8.10 µs | 61.70 Melem/s |
|
|
1234
|
+
| Make Lambda Path | 1000 × 20 | 29.96 µs | 33.37 Melem/s |
|
|
1235
|
+
| Make Lambda Path | 5000 × 50 | 424.18 µs | 11.79 Melem/s |
|
|
1236
|
+
|
|
1237
|
+
### Diagnostic Test Benchmarks
|
|
1238
|
+
|
|
1239
|
+
| Benchmark | Size (n × p) | Time |
|
|
1240
|
+
|-----------|--------------|------|
|
|
1241
|
+
| Rainbow Test | 50 × 3 | 40.34 µs |
|
|
1242
|
+
| Rainbow Test | 100 × 5 | 187.94 µs |
|
|
1243
|
+
| Rainbow Test | 500 × 10 | 8.63 ms |
|
|
1244
|
+
| Rainbow Test | 1000 × 20 | 60.09 ms |
|
|
1245
|
+
| Rainbow Test | 5000 × 50 | 3.45 s |
|
|
1246
|
+
| Harvey-Collier Test | 50 × 1 | 15.26 µs |
|
|
1247
|
+
| Harvey-Collier Test | 100 × 1 | 30.32 µs |
|
|
1248
|
+
| Harvey-Collier Test | 500 × 1 | 138.44 µs |
|
|
1249
|
+
| Harvey-Collier Test | 1000 × 1 | 298.33 µs |
|
|
1250
|
+
| Breusch-Pagan Test | 50 × 3 | 58.07 µs |
|
|
1251
|
+
| Breusch-Pagan Test | 100 × 5 | 296.74 µs |
|
|
1252
|
+
| Breusch-Pagan Test | 500 × 10 | 13.79 ms |
|
|
1253
|
+
| Breusch-Pagan Test | 1000 × 20 | 96.49 ms |
|
|
1254
|
+
| Breusch-Pagan Test | 5000 × 50 | 5.56 s |
|
|
1255
|
+
| White Test | 50 × 3 | 14.31 µs |
|
|
1256
|
+
| White Test | 100 × 5 | 44.25 µs |
|
|
1257
|
+
| White Test | 500 × 10 | 669.40 µs |
|
|
1258
|
+
| White Test | 1000 × 20 | 4.89 ms |
|
|
1259
|
+
| Jarque-Bera Test | 50 × 3 | 30.13 µs |
|
|
1260
|
+
| Jarque-Bera Test | 100 × 5 | 149.29 µs |
|
|
1261
|
+
| Jarque-Bera Test | 500 × 10 | 6.64 ms |
|
|
1262
|
+
| Jarque-Bera Test | 1000 × 20 | 47.89 ms |
|
|
1263
|
+
| Jarque-Bera Test | 5000 × 50 | 2.75 s |
|
|
1264
|
+
| Durbin-Watson Test | 50 × 3 | 31.80 µs |
|
|
1265
|
+
| Durbin-Watson Test | 100 × 5 | 152.56 µs |
|
|
1266
|
+
| Durbin-Watson Test | 500 × 10 | 6.87 ms |
|
|
1267
|
+
| Durbin-Watson Test | 1000 × 20 | 48.65 ms |
|
|
1268
|
+
| Durbin-Watson Test | 5000 × 50 | 2.76 s |
|
|
1269
|
+
| Breusch-Godfrey Test | 50 × 3 | 71.73 µs |
|
|
1270
|
+
| Breusch-Godfrey Test | 100 × 5 | 348.94 µs |
|
|
1271
|
+
| Breusch-Godfrey Test | 500 × 10 | 14.77 ms |
|
|
1272
|
+
| Breusch-Godfrey Test | 1000 × 20 | 100.08 ms |
|
|
1273
|
+
| Breusch-Godfrey Test | 5000 × 50 | 5.64 s |
|
|
1274
|
+
| Shapiro-Wilk Test | 10 × 2 | 2.04 µs |
|
|
1275
|
+
| Shapiro-Wilk Test | 50 × 3 | 4.87 µs |
|
|
1276
|
+
| Shapiro-Wilk Test | 100 × 5 | 10.67 µs |
|
|
1277
|
+
| Shapiro-Wilk Test | 500 × 10 | 110.02 µs |
|
|
1278
|
+
| Shapiro-Wilk Test | 1000 × 20 | 635.13 µs |
|
|
1279
|
+
| Shapiro-Wilk Test | 5000 × 50 | 17.53 ms |
|
|
1280
|
+
| Anderson-Darling Test | 50 × 3 | 34.02 µs |
|
|
1281
|
+
| Anderson-Darling Test | 100 × 5 | 162.28 µs |
|
|
1282
|
+
| Anderson-Darling Test | 500 × 10 | 6.95 ms |
|
|
1283
|
+
| Anderson-Darling Test | 1000 × 20 | 48.15 ms |
|
|
1284
|
+
| Anderson-Darling Test | 5000 × 50 | 2.78 s |
|
|
1285
|
+
| Cook's Distance Test | 50 × 3 | 64.52 µs |
|
|
1286
|
+
| Cook's Distance Test | 100 × 5 | 297.69 µs |
|
|
1287
|
+
| Cook's Distance Test | 500 × 10 | 12.73 ms |
|
|
1288
|
+
| Cook's Distance Test | 1000 × 20 | 94.02 ms |
|
|
1289
|
+
| Cook's Distance Test | 5000 × 50 | 5.31 s |
|
|
1290
|
+
| DFBETAS Test | 50 × 3 | 46.34 µs |
|
|
1291
|
+
| DFBETAS Test | 100 × 5 | 185.52 µs |
|
|
1292
|
+
| DFBETAS Test | 500 × 10 | 7.04 ms |
|
|
1293
|
+
| DFBETAS Test | 1000 × 20 | 49.68 ms |
|
|
1294
|
+
| DFFITS Test | 50 × 3 | 33.56 µs |
|
|
1295
|
+
| DFFITS Test | 100 × 5 | 157.62 µs |
|
|
1296
|
+
| DFFITS Test | 500 × 10 | 6.82 ms |
|
|
1297
|
+
| DFFITS Test | 1000 × 20 | 48.35 ms |
|
|
1298
|
+
| VIF Test | 50 × 3 | 5.36 µs |
|
|
1299
|
+
| VIF Test | 100 × 5 | 12.68 µs |
|
|
1300
|
+
| VIF Test | 500 × 10 | 128.04 µs |
|
|
1301
|
+
| VIF Test | 1000 × 20 | 807.30 µs |
|
|
1302
|
+
| VIF Test | 5000 × 50 | 26.33 ms |
|
|
1303
|
+
| RESET Test | 50 × 3 | 77.85 µs |
|
|
1304
|
+
| RESET Test | 100 × 5 | 359.12 µs |
|
|
1305
|
+
| RESET Test | 500 × 10 | 14.40 ms |
|
|
1306
|
+
| RESET Test | 1000 × 20 | 100.52 ms |
|
|
1307
|
+
| RESET Test | 5000 × 50 | 5.67 s |
|
|
1308
|
+
| Full Diagnostics | 100 × 5 | 2.75 ms |
|
|
1309
|
+
| Full Diagnostics | 500 × 10 | 104.01 ms |
|
|
1310
|
+
| Full Diagnostics | 1000 × 20 | 740.52 ms |
|
|
1311
|
+
|
|
1312
|
+
### Linear Algebra Benchmarks
|
|
1313
|
+
|
|
1314
|
+
| Benchmark | Size | Time |
|
|
1315
|
+
|-----------|------|------|
|
|
1316
|
+
| Matrix Transpose | 10 × 10 | 209.50 ns |
|
|
1317
|
+
| Matrix Transpose | 50 × 50 | 3.67 µs |
|
|
1318
|
+
| Matrix Transpose | 100 × 100 | 14.92 µs |
|
|
1319
|
+
| Matrix Transpose | 500 × 500 | 924.23 µs |
|
|
1320
|
+
| Matrix Transpose | 1000 × 1000 | 5.56 ms |
|
|
1321
|
+
| Matrix Multiply (matmul) | 10 × 10 × 10 | 1.54 µs |
|
|
1322
|
+
| Matrix Multiply (matmul) | 50 × 50 × 50 | 144.15 µs |
|
|
1323
|
+
| Matrix Multiply (matmul) | 100 × 100 × 100 | 1.39 ms |
|
|
1324
|
+
| Matrix Multiply (matmul) | 200 × 200 × 200 | 11.90 ms |
|
|
1325
|
+
| Matrix Multiply (matmul) | 1000 × 100 × 100 | 13.94 ms |
|
|
1326
|
+
| QR Decomposition | 10 × 5 | 1.41 µs |
|
|
1327
|
+
| QR Decomposition | 50 × 10 | 14.81 µs |
|
|
1328
|
+
| QR Decomposition | 100 × 20 | 57.61 µs |
|
|
1329
|
+
| QR Decomposition | 500 × 50 | 2.19 ms |
|
|
1330
|
+
| QR Decomposition | 1000 × 100 | 19.20 ms |
|
|
1331
|
+
| QR Decomposition | 5000 × 100 | 1.48 s |
|
|
1332
|
+
| QR Decomposition | 10000 × 100 | 8.09 s |
|
|
1333
|
+
| QR Decomposition | 1000 × 500 | 84.48 ms |
|
|
1334
|
+
| SVD | 10 × 5 | 150.36 µs |
|
|
1335
|
+
| SVD | 50 × 10 | 505.41 µs |
|
|
1336
|
+
| SVD | 100 × 20 | 2.80 ms |
|
|
1337
|
+
| SVD | 500 × 50 | 60.00 ms |
|
|
1338
|
+
| SVD | 1000 × 100 | 513.35 ms |
|
|
1339
|
+
| Matrix Invert | 5 × 5 | 877.32 ns |
|
|
1340
|
+
| Matrix Invert | 10 × 10 | 2.48 µs |
|
|
1341
|
+
| Matrix Invert | 20 × 20 | 5.46 µs |
|
|
1342
|
+
| Matrix Invert | 50 × 50 | 31.94 µs |
|
|
1343
|
+
| Matrix Invert | 100 × 100 | 141.38 µs |
|
|
1344
|
+
| Matrix Invert | 200 × 200 | 647.03 µs |
|
|
1345
|
+
|
|
1346
|
+
### Pressure Benchmarks (Large Datasets)
|
|
1347
|
+
|
|
1348
|
+
| Benchmark | Size (n) | Time |
|
|
1349
|
+
|-----------|----------|------|
|
|
1350
|
+
| Pressure (OLS + all diagnostics) | 10000 | 11.28 s |
|
|
1351
|
+
|
|
1352
|
+
</details>
|
|
1353
|
+
|
|
1354
|
+
---
|
|
1355
|
+
|
|
846
1356
|
## License
|
|
847
1357
|
|
|
848
1358
|
Dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE).
|
package/linreg_core.d.ts
CHANGED
|
@@ -95,6 +95,41 @@ export function breusch_pagan_test(y_json: string, x_vars_json: string): string;
|
|
|
95
95
|
*/
|
|
96
96
|
export function cooks_distance_test(y_json: string, x_vars_json: string): string;
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Deserialize a serialized model, extracting the inner model data.
|
|
100
|
+
*
|
|
101
|
+
* This function takes a serialized model JSON (as created by serialize_model),
|
|
102
|
+
* validates the format version, and returns the inner model data as JSON.
|
|
103
|
+
*
|
|
104
|
+
* # Arguments
|
|
105
|
+
*
|
|
106
|
+
* * `json_string` - JSON string of the serialized model (with metadata wrapper)
|
|
107
|
+
*
|
|
108
|
+
* # Returns
|
|
109
|
+
*
|
|
110
|
+
* JSON string of the inner model data (coefficients, statistics, etc.),
|
|
111
|
+
* or a JSON error object if the input is invalid, the format version is
|
|
112
|
+
* incompatible, or the domain check fails.
|
|
113
|
+
*
|
|
114
|
+
* # Example
|
|
115
|
+
*
|
|
116
|
+
* ```javascript
|
|
117
|
+
* import { deserialize_model } from './linreg_core.js';
|
|
118
|
+
*
|
|
119
|
+
* // Load from file (browser-side)
|
|
120
|
+
* const response = await fetch('my_model.json');
|
|
121
|
+
* const serialized = await response.text();
|
|
122
|
+
*
|
|
123
|
+
* // Deserialize to get the model data
|
|
124
|
+
* const modelJson = deserialize_model(serialized);
|
|
125
|
+
* const model = JSON.parse(modelJson);
|
|
126
|
+
*
|
|
127
|
+
* console.log(model.coefficients);
|
|
128
|
+
* console.log(model.r_squared);
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export function deserialize_model(json_string: string): string;
|
|
132
|
+
|
|
98
133
|
/**
|
|
99
134
|
* Performs DFBETAS analysis via WASM.
|
|
100
135
|
*
|
|
@@ -188,6 +223,46 @@ export function durbin_watson_test(y_json: string, x_vars_json: string): string;
|
|
|
188
223
|
*/
|
|
189
224
|
export function elastic_net_regression(y_json: string, x_vars_json: string, _variable_names: string, lambda: number, alpha: number, standardize: boolean, max_iter: number, tol: number): string;
|
|
190
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Extract metadata from a serialized model without deserializing the full model.
|
|
228
|
+
*
|
|
229
|
+
* This function returns only the metadata portion of a serialized model,
|
|
230
|
+
* which includes information like model type, library version, creation time,
|
|
231
|
+
* and optional model name.
|
|
232
|
+
*
|
|
233
|
+
* # Arguments
|
|
234
|
+
*
|
|
235
|
+
* * `json_string` - JSON string of the serialized model
|
|
236
|
+
*
|
|
237
|
+
* # Returns
|
|
238
|
+
*
|
|
239
|
+
* JSON string containing the metadata object with fields:
|
|
240
|
+
* - `format_version` - Format version (e.g., "1.0")
|
|
241
|
+
* - `library_version` - linreg-core version used to create the model
|
|
242
|
+
* - `model_type` - Type of model ("OLS", "Ridge", etc.)
|
|
243
|
+
* - `created_at` - ISO 8601 timestamp of creation
|
|
244
|
+
* - `name` - Optional custom model name
|
|
245
|
+
*
|
|
246
|
+
* Returns a JSON error object if the input is invalid or the domain check fails.
|
|
247
|
+
*
|
|
248
|
+
* # Example
|
|
249
|
+
*
|
|
250
|
+
* ```javascript
|
|
251
|
+
* import { get_model_metadata } from './linreg_core.js';
|
|
252
|
+
*
|
|
253
|
+
* const response = await fetch('my_model.json');
|
|
254
|
+
* const serialized = await response.text();
|
|
255
|
+
*
|
|
256
|
+
* const metadataJson = get_model_metadata(serialized);
|
|
257
|
+
* const metadata = JSON.parse(metadataJson);
|
|
258
|
+
*
|
|
259
|
+
* console.log('Model type:', metadata.model_type);
|
|
260
|
+
* console.log('Created:', metadata.created_at);
|
|
261
|
+
* console.log('Name:', metadata.name || '(unnamed)');
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
export function get_model_metadata(json_string: string): string;
|
|
265
|
+
|
|
191
266
|
/**
|
|
192
267
|
* Computes the inverse of the standard normal CDF (probit function).
|
|
193
268
|
*
|
|
@@ -291,6 +366,110 @@ export function harvey_collier_test(y_json: string, x_vars_json: string): string
|
|
|
291
366
|
*/
|
|
292
367
|
export function jarque_bera_test(y_json: string, x_vars_json: string): string;
|
|
293
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Performs K-Fold Cross Validation for Elastic Net regression via WASM.
|
|
371
|
+
*
|
|
372
|
+
* # Arguments
|
|
373
|
+
*
|
|
374
|
+
* * `y_json` - JSON array of response variable values
|
|
375
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
376
|
+
* * `lambda` - Regularization strength (>= 0)
|
|
377
|
+
* * `alpha` - Mixing parameter (0 = Ridge, 1 = Lasso)
|
|
378
|
+
* * `standardize` - Whether to standardize predictors
|
|
379
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
380
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
381
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
382
|
+
*
|
|
383
|
+
* # Returns
|
|
384
|
+
*
|
|
385
|
+
* JSON string containing CV results (same structure as OLS).
|
|
386
|
+
*
|
|
387
|
+
* # Errors
|
|
388
|
+
*
|
|
389
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
390
|
+
* or domain check fails.
|
|
391
|
+
*/
|
|
392
|
+
export function kfold_cv_elastic_net(y_json: string, x_vars_json: string, lambda: number, alpha: number, standardize: boolean, n_folds: number, shuffle_json: string, seed_json: string): string;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Performs K-Fold Cross Validation for Lasso regression via WASM.
|
|
396
|
+
*
|
|
397
|
+
* # Arguments
|
|
398
|
+
*
|
|
399
|
+
* * `y_json` - JSON array of response variable values
|
|
400
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
401
|
+
* * `lambda` - Regularization strength (>= 0)
|
|
402
|
+
* * `standardize` - Whether to standardize predictors
|
|
403
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
404
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
405
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
406
|
+
*
|
|
407
|
+
* # Returns
|
|
408
|
+
*
|
|
409
|
+
* JSON string containing CV results (same structure as OLS).
|
|
410
|
+
*
|
|
411
|
+
* # Errors
|
|
412
|
+
*
|
|
413
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
414
|
+
* or domain check fails.
|
|
415
|
+
*/
|
|
416
|
+
export function kfold_cv_lasso(y_json: string, x_vars_json: string, lambda: number, standardize: boolean, n_folds: number, shuffle_json: string, seed_json: string): string;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Performs K-Fold Cross Validation for OLS regression via WASM.
|
|
420
|
+
*
|
|
421
|
+
* # Arguments
|
|
422
|
+
*
|
|
423
|
+
* * `y_json` - JSON array of response variable values
|
|
424
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
425
|
+
* * `variable_names_json` - JSON array of variable names
|
|
426
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
427
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
428
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
429
|
+
*
|
|
430
|
+
* # Returns
|
|
431
|
+
*
|
|
432
|
+
* JSON string containing CV results:
|
|
433
|
+
* - `n_folds` - Number of folds used
|
|
434
|
+
* - `n_samples` - Total number of observations
|
|
435
|
+
* - `mean_mse`, `std_mse` - Mean and std of MSE across folds
|
|
436
|
+
* - `mean_rmse`, `std_rmse` - Mean and std of RMSE across folds
|
|
437
|
+
* - `mean_mae`, `std_mae` - Mean and std of MAE across folds
|
|
438
|
+
* - `mean_r_squared`, `std_r_squared` - Mean and std of R² across folds
|
|
439
|
+
* - `fold_results` - Array of individual fold results
|
|
440
|
+
* - `fold_coefficients` - Array of coefficient arrays from each fold
|
|
441
|
+
*
|
|
442
|
+
* # Errors
|
|
443
|
+
*
|
|
444
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
445
|
+
* or domain check fails.
|
|
446
|
+
*/
|
|
447
|
+
export function kfold_cv_ols(y_json: string, x_vars_json: string, variable_names_json: string, n_folds: number, shuffle_json: string, seed_json: string): string;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Performs K-Fold Cross Validation for Ridge regression via WASM.
|
|
451
|
+
*
|
|
452
|
+
* # Arguments
|
|
453
|
+
*
|
|
454
|
+
* * `y_json` - JSON array of response variable values
|
|
455
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
456
|
+
* * `lambda` - Regularization strength (>= 0)
|
|
457
|
+
* * `standardize` - Whether to standardize predictors
|
|
458
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
459
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
460
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
461
|
+
*
|
|
462
|
+
* # Returns
|
|
463
|
+
*
|
|
464
|
+
* JSON string containing CV results (same structure as OLS).
|
|
465
|
+
*
|
|
466
|
+
* # Errors
|
|
467
|
+
*
|
|
468
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
469
|
+
* or domain check fails.
|
|
470
|
+
*/
|
|
471
|
+
export function kfold_cv_ridge(y_json: string, x_vars_json: string, lambda: number, standardize: boolean, n_folds: number, shuffle_json: string, seed_json: string): string;
|
|
472
|
+
|
|
294
473
|
/**
|
|
295
474
|
* Performs Lasso regression via WASM.
|
|
296
475
|
*
|
|
@@ -592,6 +771,46 @@ export function reset_test(y_json: string, x_vars_json: string, powers_json: str
|
|
|
592
771
|
*/
|
|
593
772
|
export function ridge_regression(y_json: string, x_vars_json: string, _variable_names: string, lambda: number, standardize: boolean): string;
|
|
594
773
|
|
|
774
|
+
/**
|
|
775
|
+
* Serialize a model by wrapping its JSON data with metadata.
|
|
776
|
+
*
|
|
777
|
+
* This function takes a model's JSON representation (as returned by regression
|
|
778
|
+
* functions), wraps it with version and type metadata, and returns a serialized
|
|
779
|
+
* JSON string suitable for storage or download.
|
|
780
|
+
*
|
|
781
|
+
* # Arguments
|
|
782
|
+
*
|
|
783
|
+
* * `model_json` - JSON string of the model result (e.g., from ols_regression)
|
|
784
|
+
* * `model_type` - Type of model: "OLS", "Ridge", "Lasso", "ElasticNet", "WLS", or "LOESS"
|
|
785
|
+
* * `name` - Optional custom name for the model
|
|
786
|
+
*
|
|
787
|
+
* # Returns
|
|
788
|
+
*
|
|
789
|
+
* JSON string containing the serialized model with metadata, or a JSON error object
|
|
790
|
+
* if the input is invalid or the domain check fails.
|
|
791
|
+
*
|
|
792
|
+
* # Example
|
|
793
|
+
*
|
|
794
|
+
* ```javascript
|
|
795
|
+
* import { serialize_model, ols_regression } from './linreg_core.js';
|
|
796
|
+
*
|
|
797
|
+
* // Train a model
|
|
798
|
+
* const resultJson = ols_regression(yJson, xJson, namesJson);
|
|
799
|
+
*
|
|
800
|
+
* // Serialize it
|
|
801
|
+
* const serialized = serialize_model(resultJson, "OLS", "My Housing Model");
|
|
802
|
+
*
|
|
803
|
+
* // Download (browser-side)
|
|
804
|
+
* const blob = new Blob([serialized], { type: 'application/json' });
|
|
805
|
+
* const url = URL.createObjectURL(blob);
|
|
806
|
+
* const a = document.createElement('a');
|
|
807
|
+
* a.href = url;
|
|
808
|
+
* a.download = 'my_model.json';
|
|
809
|
+
* a.click();
|
|
810
|
+
* ```
|
|
811
|
+
*/
|
|
812
|
+
export function serialize_model(model_json: string, model_type: string, name?: string | null): string;
|
|
813
|
+
|
|
595
814
|
/**
|
|
596
815
|
* Performs the Shapiro-Wilk test for normality via WASM.
|
|
597
816
|
*
|
|
@@ -857,13 +1076,19 @@ export interface InitOutput {
|
|
|
857
1076
|
readonly breusch_godfrey_test: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number];
|
|
858
1077
|
readonly breusch_pagan_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
859
1078
|
readonly cooks_distance_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
1079
|
+
readonly deserialize_model: (a: number, b: number) => [number, number];
|
|
860
1080
|
readonly dfbetas_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
861
1081
|
readonly dffits_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
862
1082
|
readonly durbin_watson_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
863
1083
|
readonly elastic_net_regression: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => [number, number];
|
|
1084
|
+
readonly get_model_metadata: (a: number, b: number) => [number, number];
|
|
864
1085
|
readonly get_version: () => [number, number];
|
|
865
1086
|
readonly harvey_collier_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
866
1087
|
readonly jarque_bera_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
1088
|
+
readonly kfold_cv_elastic_net: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number) => [number, number];
|
|
1089
|
+
readonly kfold_cv_lasso: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => [number, number];
|
|
1090
|
+
readonly kfold_cv_ols: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => [number, number];
|
|
1091
|
+
readonly kfold_cv_ridge: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => [number, number];
|
|
867
1092
|
readonly lasso_regression: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => [number, number];
|
|
868
1093
|
readonly loess_fit: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => [number, number];
|
|
869
1094
|
readonly loess_predict: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => [number, number];
|
|
@@ -875,6 +1100,7 @@ export interface InitOutput {
|
|
|
875
1100
|
readonly rainbow_test: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number];
|
|
876
1101
|
readonly reset_test: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => [number, number];
|
|
877
1102
|
readonly ridge_regression: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => [number, number];
|
|
1103
|
+
readonly serialize_model: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number];
|
|
878
1104
|
readonly shapiro_wilk_test: (a: number, b: number, c: number, d: number) => [number, number];
|
|
879
1105
|
readonly stats_correlation: (a: number, b: number, c: number, d: number) => [number, number];
|
|
880
1106
|
readonly stats_mean: (a: number, b: number) => [number, number];
|
package/linreg_core.js
CHANGED
|
@@ -170,6 +170,56 @@ export function cooks_distance_test(y_json, x_vars_json) {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Deserialize a serialized model, extracting the inner model data.
|
|
175
|
+
*
|
|
176
|
+
* This function takes a serialized model JSON (as created by serialize_model),
|
|
177
|
+
* validates the format version, and returns the inner model data as JSON.
|
|
178
|
+
*
|
|
179
|
+
* # Arguments
|
|
180
|
+
*
|
|
181
|
+
* * `json_string` - JSON string of the serialized model (with metadata wrapper)
|
|
182
|
+
*
|
|
183
|
+
* # Returns
|
|
184
|
+
*
|
|
185
|
+
* JSON string of the inner model data (coefficients, statistics, etc.),
|
|
186
|
+
* or a JSON error object if the input is invalid, the format version is
|
|
187
|
+
* incompatible, or the domain check fails.
|
|
188
|
+
*
|
|
189
|
+
* # Example
|
|
190
|
+
*
|
|
191
|
+
* ```javascript
|
|
192
|
+
* import { deserialize_model } from './linreg_core.js';
|
|
193
|
+
*
|
|
194
|
+
* // Load from file (browser-side)
|
|
195
|
+
* const response = await fetch('my_model.json');
|
|
196
|
+
* const serialized = await response.text();
|
|
197
|
+
*
|
|
198
|
+
* // Deserialize to get the model data
|
|
199
|
+
* const modelJson = deserialize_model(serialized);
|
|
200
|
+
* const model = JSON.parse(modelJson);
|
|
201
|
+
*
|
|
202
|
+
* console.log(model.coefficients);
|
|
203
|
+
* console.log(model.r_squared);
|
|
204
|
+
* ```
|
|
205
|
+
* @param {string} json_string
|
|
206
|
+
* @returns {string}
|
|
207
|
+
*/
|
|
208
|
+
export function deserialize_model(json_string) {
|
|
209
|
+
let deferred2_0;
|
|
210
|
+
let deferred2_1;
|
|
211
|
+
try {
|
|
212
|
+
const ptr0 = passStringToWasm0(json_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
213
|
+
const len0 = WASM_VECTOR_LEN;
|
|
214
|
+
const ret = wasm.deserialize_model(ptr0, len0);
|
|
215
|
+
deferred2_0 = ret[0];
|
|
216
|
+
deferred2_1 = ret[1];
|
|
217
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
218
|
+
} finally {
|
|
219
|
+
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
173
223
|
/**
|
|
174
224
|
* Performs DFBETAS analysis via WASM.
|
|
175
225
|
*
|
|
@@ -343,6 +393,61 @@ export function elastic_net_regression(y_json, x_vars_json, _variable_names, lam
|
|
|
343
393
|
}
|
|
344
394
|
}
|
|
345
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Extract metadata from a serialized model without deserializing the full model.
|
|
398
|
+
*
|
|
399
|
+
* This function returns only the metadata portion of a serialized model,
|
|
400
|
+
* which includes information like model type, library version, creation time,
|
|
401
|
+
* and optional model name.
|
|
402
|
+
*
|
|
403
|
+
* # Arguments
|
|
404
|
+
*
|
|
405
|
+
* * `json_string` - JSON string of the serialized model
|
|
406
|
+
*
|
|
407
|
+
* # Returns
|
|
408
|
+
*
|
|
409
|
+
* JSON string containing the metadata object with fields:
|
|
410
|
+
* - `format_version` - Format version (e.g., "1.0")
|
|
411
|
+
* - `library_version` - linreg-core version used to create the model
|
|
412
|
+
* - `model_type` - Type of model ("OLS", "Ridge", etc.)
|
|
413
|
+
* - `created_at` - ISO 8601 timestamp of creation
|
|
414
|
+
* - `name` - Optional custom model name
|
|
415
|
+
*
|
|
416
|
+
* Returns a JSON error object if the input is invalid or the domain check fails.
|
|
417
|
+
*
|
|
418
|
+
* # Example
|
|
419
|
+
*
|
|
420
|
+
* ```javascript
|
|
421
|
+
* import { get_model_metadata } from './linreg_core.js';
|
|
422
|
+
*
|
|
423
|
+
* const response = await fetch('my_model.json');
|
|
424
|
+
* const serialized = await response.text();
|
|
425
|
+
*
|
|
426
|
+
* const metadataJson = get_model_metadata(serialized);
|
|
427
|
+
* const metadata = JSON.parse(metadataJson);
|
|
428
|
+
*
|
|
429
|
+
* console.log('Model type:', metadata.model_type);
|
|
430
|
+
* console.log('Created:', metadata.created_at);
|
|
431
|
+
* console.log('Name:', metadata.name || '(unnamed)');
|
|
432
|
+
* ```
|
|
433
|
+
* @param {string} json_string
|
|
434
|
+
* @returns {string}
|
|
435
|
+
*/
|
|
436
|
+
export function get_model_metadata(json_string) {
|
|
437
|
+
let deferred2_0;
|
|
438
|
+
let deferred2_1;
|
|
439
|
+
try {
|
|
440
|
+
const ptr0 = passStringToWasm0(json_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
441
|
+
const len0 = WASM_VECTOR_LEN;
|
|
442
|
+
const ret = wasm.get_model_metadata(ptr0, len0);
|
|
443
|
+
deferred2_0 = ret[0];
|
|
444
|
+
deferred2_1 = ret[1];
|
|
445
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
446
|
+
} finally {
|
|
447
|
+
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
346
451
|
/**
|
|
347
452
|
* Computes the inverse of the standard normal CDF (probit function).
|
|
348
453
|
*
|
|
@@ -511,6 +616,220 @@ export function jarque_bera_test(y_json, x_vars_json) {
|
|
|
511
616
|
}
|
|
512
617
|
}
|
|
513
618
|
|
|
619
|
+
/**
|
|
620
|
+
* Performs K-Fold Cross Validation for Elastic Net regression via WASM.
|
|
621
|
+
*
|
|
622
|
+
* # Arguments
|
|
623
|
+
*
|
|
624
|
+
* * `y_json` - JSON array of response variable values
|
|
625
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
626
|
+
* * `lambda` - Regularization strength (>= 0)
|
|
627
|
+
* * `alpha` - Mixing parameter (0 = Ridge, 1 = Lasso)
|
|
628
|
+
* * `standardize` - Whether to standardize predictors
|
|
629
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
630
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
631
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
632
|
+
*
|
|
633
|
+
* # Returns
|
|
634
|
+
*
|
|
635
|
+
* JSON string containing CV results (same structure as OLS).
|
|
636
|
+
*
|
|
637
|
+
* # Errors
|
|
638
|
+
*
|
|
639
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
640
|
+
* or domain check fails.
|
|
641
|
+
* @param {string} y_json
|
|
642
|
+
* @param {string} x_vars_json
|
|
643
|
+
* @param {number} lambda
|
|
644
|
+
* @param {number} alpha
|
|
645
|
+
* @param {boolean} standardize
|
|
646
|
+
* @param {number} n_folds
|
|
647
|
+
* @param {string} shuffle_json
|
|
648
|
+
* @param {string} seed_json
|
|
649
|
+
* @returns {string}
|
|
650
|
+
*/
|
|
651
|
+
export function kfold_cv_elastic_net(y_json, x_vars_json, lambda, alpha, standardize, n_folds, shuffle_json, seed_json) {
|
|
652
|
+
let deferred5_0;
|
|
653
|
+
let deferred5_1;
|
|
654
|
+
try {
|
|
655
|
+
const ptr0 = passStringToWasm0(y_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
656
|
+
const len0 = WASM_VECTOR_LEN;
|
|
657
|
+
const ptr1 = passStringToWasm0(x_vars_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
658
|
+
const len1 = WASM_VECTOR_LEN;
|
|
659
|
+
const ptr2 = passStringToWasm0(shuffle_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
660
|
+
const len2 = WASM_VECTOR_LEN;
|
|
661
|
+
const ptr3 = passStringToWasm0(seed_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
662
|
+
const len3 = WASM_VECTOR_LEN;
|
|
663
|
+
const ret = wasm.kfold_cv_elastic_net(ptr0, len0, ptr1, len1, lambda, alpha, standardize, n_folds, ptr2, len2, ptr3, len3);
|
|
664
|
+
deferred5_0 = ret[0];
|
|
665
|
+
deferred5_1 = ret[1];
|
|
666
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
667
|
+
} finally {
|
|
668
|
+
wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Performs K-Fold Cross Validation for Lasso regression via WASM.
|
|
674
|
+
*
|
|
675
|
+
* # Arguments
|
|
676
|
+
*
|
|
677
|
+
* * `y_json` - JSON array of response variable values
|
|
678
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
679
|
+
* * `lambda` - Regularization strength (>= 0)
|
|
680
|
+
* * `standardize` - Whether to standardize predictors
|
|
681
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
682
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
683
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
684
|
+
*
|
|
685
|
+
* # Returns
|
|
686
|
+
*
|
|
687
|
+
* JSON string containing CV results (same structure as OLS).
|
|
688
|
+
*
|
|
689
|
+
* # Errors
|
|
690
|
+
*
|
|
691
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
692
|
+
* or domain check fails.
|
|
693
|
+
* @param {string} y_json
|
|
694
|
+
* @param {string} x_vars_json
|
|
695
|
+
* @param {number} lambda
|
|
696
|
+
* @param {boolean} standardize
|
|
697
|
+
* @param {number} n_folds
|
|
698
|
+
* @param {string} shuffle_json
|
|
699
|
+
* @param {string} seed_json
|
|
700
|
+
* @returns {string}
|
|
701
|
+
*/
|
|
702
|
+
export function kfold_cv_lasso(y_json, x_vars_json, lambda, standardize, n_folds, shuffle_json, seed_json) {
|
|
703
|
+
let deferred5_0;
|
|
704
|
+
let deferred5_1;
|
|
705
|
+
try {
|
|
706
|
+
const ptr0 = passStringToWasm0(y_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
707
|
+
const len0 = WASM_VECTOR_LEN;
|
|
708
|
+
const ptr1 = passStringToWasm0(x_vars_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
709
|
+
const len1 = WASM_VECTOR_LEN;
|
|
710
|
+
const ptr2 = passStringToWasm0(shuffle_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
711
|
+
const len2 = WASM_VECTOR_LEN;
|
|
712
|
+
const ptr3 = passStringToWasm0(seed_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
713
|
+
const len3 = WASM_VECTOR_LEN;
|
|
714
|
+
const ret = wasm.kfold_cv_lasso(ptr0, len0, ptr1, len1, lambda, standardize, n_folds, ptr2, len2, ptr3, len3);
|
|
715
|
+
deferred5_0 = ret[0];
|
|
716
|
+
deferred5_1 = ret[1];
|
|
717
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
718
|
+
} finally {
|
|
719
|
+
wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Performs K-Fold Cross Validation for OLS regression via WASM.
|
|
725
|
+
*
|
|
726
|
+
* # Arguments
|
|
727
|
+
*
|
|
728
|
+
* * `y_json` - JSON array of response variable values
|
|
729
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
730
|
+
* * `variable_names_json` - JSON array of variable names
|
|
731
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
732
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
733
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
734
|
+
*
|
|
735
|
+
* # Returns
|
|
736
|
+
*
|
|
737
|
+
* JSON string containing CV results:
|
|
738
|
+
* - `n_folds` - Number of folds used
|
|
739
|
+
* - `n_samples` - Total number of observations
|
|
740
|
+
* - `mean_mse`, `std_mse` - Mean and std of MSE across folds
|
|
741
|
+
* - `mean_rmse`, `std_rmse` - Mean and std of RMSE across folds
|
|
742
|
+
* - `mean_mae`, `std_mae` - Mean and std of MAE across folds
|
|
743
|
+
* - `mean_r_squared`, `std_r_squared` - Mean and std of R² across folds
|
|
744
|
+
* - `fold_results` - Array of individual fold results
|
|
745
|
+
* - `fold_coefficients` - Array of coefficient arrays from each fold
|
|
746
|
+
*
|
|
747
|
+
* # Errors
|
|
748
|
+
*
|
|
749
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
750
|
+
* or domain check fails.
|
|
751
|
+
* @param {string} y_json
|
|
752
|
+
* @param {string} x_vars_json
|
|
753
|
+
* @param {string} variable_names_json
|
|
754
|
+
* @param {number} n_folds
|
|
755
|
+
* @param {string} shuffle_json
|
|
756
|
+
* @param {string} seed_json
|
|
757
|
+
* @returns {string}
|
|
758
|
+
*/
|
|
759
|
+
export function kfold_cv_ols(y_json, x_vars_json, variable_names_json, n_folds, shuffle_json, seed_json) {
|
|
760
|
+
let deferred6_0;
|
|
761
|
+
let deferred6_1;
|
|
762
|
+
try {
|
|
763
|
+
const ptr0 = passStringToWasm0(y_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
764
|
+
const len0 = WASM_VECTOR_LEN;
|
|
765
|
+
const ptr1 = passStringToWasm0(x_vars_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
766
|
+
const len1 = WASM_VECTOR_LEN;
|
|
767
|
+
const ptr2 = passStringToWasm0(variable_names_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
768
|
+
const len2 = WASM_VECTOR_LEN;
|
|
769
|
+
const ptr3 = passStringToWasm0(shuffle_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
770
|
+
const len3 = WASM_VECTOR_LEN;
|
|
771
|
+
const ptr4 = passStringToWasm0(seed_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
772
|
+
const len4 = WASM_VECTOR_LEN;
|
|
773
|
+
const ret = wasm.kfold_cv_ols(ptr0, len0, ptr1, len1, ptr2, len2, n_folds, ptr3, len3, ptr4, len4);
|
|
774
|
+
deferred6_0 = ret[0];
|
|
775
|
+
deferred6_1 = ret[1];
|
|
776
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
777
|
+
} finally {
|
|
778
|
+
wasm.__wbindgen_free(deferred6_0, deferred6_1, 1);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Performs K-Fold Cross Validation for Ridge regression via WASM.
|
|
784
|
+
*
|
|
785
|
+
* # Arguments
|
|
786
|
+
*
|
|
787
|
+
* * `y_json` - JSON array of response variable values
|
|
788
|
+
* * `x_vars_json` - JSON array of predictor arrays
|
|
789
|
+
* * `lambda` - Regularization strength (>= 0)
|
|
790
|
+
* * `standardize` - Whether to standardize predictors
|
|
791
|
+
* * `n_folds` - Number of folds (must be >= 2)
|
|
792
|
+
* * `shuffle_json` - JSON boolean: whether to shuffle data before splitting
|
|
793
|
+
* * `seed_json` - JSON string with seed number or "null" for no seed
|
|
794
|
+
*
|
|
795
|
+
* # Returns
|
|
796
|
+
*
|
|
797
|
+
* JSON string containing CV results (same structure as OLS).
|
|
798
|
+
*
|
|
799
|
+
* # Errors
|
|
800
|
+
*
|
|
801
|
+
* Returns a JSON error object if parsing fails, parameters are invalid,
|
|
802
|
+
* or domain check fails.
|
|
803
|
+
* @param {string} y_json
|
|
804
|
+
* @param {string} x_vars_json
|
|
805
|
+
* @param {number} lambda
|
|
806
|
+
* @param {boolean} standardize
|
|
807
|
+
* @param {number} n_folds
|
|
808
|
+
* @param {string} shuffle_json
|
|
809
|
+
* @param {string} seed_json
|
|
810
|
+
* @returns {string}
|
|
811
|
+
*/
|
|
812
|
+
export function kfold_cv_ridge(y_json, x_vars_json, lambda, standardize, n_folds, shuffle_json, seed_json) {
|
|
813
|
+
let deferred5_0;
|
|
814
|
+
let deferred5_1;
|
|
815
|
+
try {
|
|
816
|
+
const ptr0 = passStringToWasm0(y_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
817
|
+
const len0 = WASM_VECTOR_LEN;
|
|
818
|
+
const ptr1 = passStringToWasm0(x_vars_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
819
|
+
const len1 = WASM_VECTOR_LEN;
|
|
820
|
+
const ptr2 = passStringToWasm0(shuffle_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
821
|
+
const len2 = WASM_VECTOR_LEN;
|
|
822
|
+
const ptr3 = passStringToWasm0(seed_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
823
|
+
const len3 = WASM_VECTOR_LEN;
|
|
824
|
+
const ret = wasm.kfold_cv_ridge(ptr0, len0, ptr1, len1, lambda, standardize, n_folds, ptr2, len2, ptr3, len3);
|
|
825
|
+
deferred5_0 = ret[0];
|
|
826
|
+
deferred5_1 = ret[1];
|
|
827
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
828
|
+
} finally {
|
|
829
|
+
wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
514
833
|
/**
|
|
515
834
|
* Performs Lasso regression via WASM.
|
|
516
835
|
*
|
|
@@ -1049,6 +1368,67 @@ export function ridge_regression(y_json, x_vars_json, _variable_names, lambda, s
|
|
|
1049
1368
|
}
|
|
1050
1369
|
}
|
|
1051
1370
|
|
|
1371
|
+
/**
|
|
1372
|
+
* Serialize a model by wrapping its JSON data with metadata.
|
|
1373
|
+
*
|
|
1374
|
+
* This function takes a model's JSON representation (as returned by regression
|
|
1375
|
+
* functions), wraps it with version and type metadata, and returns a serialized
|
|
1376
|
+
* JSON string suitable for storage or download.
|
|
1377
|
+
*
|
|
1378
|
+
* # Arguments
|
|
1379
|
+
*
|
|
1380
|
+
* * `model_json` - JSON string of the model result (e.g., from ols_regression)
|
|
1381
|
+
* * `model_type` - Type of model: "OLS", "Ridge", "Lasso", "ElasticNet", "WLS", or "LOESS"
|
|
1382
|
+
* * `name` - Optional custom name for the model
|
|
1383
|
+
*
|
|
1384
|
+
* # Returns
|
|
1385
|
+
*
|
|
1386
|
+
* JSON string containing the serialized model with metadata, or a JSON error object
|
|
1387
|
+
* if the input is invalid or the domain check fails.
|
|
1388
|
+
*
|
|
1389
|
+
* # Example
|
|
1390
|
+
*
|
|
1391
|
+
* ```javascript
|
|
1392
|
+
* import { serialize_model, ols_regression } from './linreg_core.js';
|
|
1393
|
+
*
|
|
1394
|
+
* // Train a model
|
|
1395
|
+
* const resultJson = ols_regression(yJson, xJson, namesJson);
|
|
1396
|
+
*
|
|
1397
|
+
* // Serialize it
|
|
1398
|
+
* const serialized = serialize_model(resultJson, "OLS", "My Housing Model");
|
|
1399
|
+
*
|
|
1400
|
+
* // Download (browser-side)
|
|
1401
|
+
* const blob = new Blob([serialized], { type: 'application/json' });
|
|
1402
|
+
* const url = URL.createObjectURL(blob);
|
|
1403
|
+
* const a = document.createElement('a');
|
|
1404
|
+
* a.href = url;
|
|
1405
|
+
* a.download = 'my_model.json';
|
|
1406
|
+
* a.click();
|
|
1407
|
+
* ```
|
|
1408
|
+
* @param {string} model_json
|
|
1409
|
+
* @param {string} model_type
|
|
1410
|
+
* @param {string | null} [name]
|
|
1411
|
+
* @returns {string}
|
|
1412
|
+
*/
|
|
1413
|
+
export function serialize_model(model_json, model_type, name) {
|
|
1414
|
+
let deferred4_0;
|
|
1415
|
+
let deferred4_1;
|
|
1416
|
+
try {
|
|
1417
|
+
const ptr0 = passStringToWasm0(model_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
1418
|
+
const len0 = WASM_VECTOR_LEN;
|
|
1419
|
+
const ptr1 = passStringToWasm0(model_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
1420
|
+
const len1 = WASM_VECTOR_LEN;
|
|
1421
|
+
var ptr2 = isLikeNone(name) ? 0 : passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
1422
|
+
var len2 = WASM_VECTOR_LEN;
|
|
1423
|
+
const ret = wasm.serialize_model(ptr0, len0, ptr1, len1, ptr2, len2);
|
|
1424
|
+
deferred4_0 = ret[0];
|
|
1425
|
+
deferred4_1 = ret[1];
|
|
1426
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
1427
|
+
} finally {
|
|
1428
|
+
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1052
1432
|
/**
|
|
1053
1433
|
* Performs the Shapiro-Wilk test for normality via WASM.
|
|
1054
1434
|
*
|
|
@@ -1579,6 +1959,10 @@ function getUint8ArrayMemory0() {
|
|
|
1579
1959
|
return cachedUint8ArrayMemory0;
|
|
1580
1960
|
}
|
|
1581
1961
|
|
|
1962
|
+
function isLikeNone(x) {
|
|
1963
|
+
return x === undefined || x === null;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1582
1966
|
function passStringToWasm0(arg, malloc, realloc) {
|
|
1583
1967
|
if (realloc === undefined) {
|
|
1584
1968
|
const buf = cachedTextEncoder.encode(arg);
|
package/linreg_core_bg.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
"Jesse Anderson"
|
|
6
6
|
],
|
|
7
7
|
"description": "Lightweight linear regression (OLS, Ridge, Lasso, Elastic Net) with diagnostic tests. Pure Rust - no external math dependencies.",
|
|
8
|
-
"version": "0.6.
|
|
8
|
+
"version": "0.6.1",
|
|
9
9
|
"license": "MIT OR Apache-2.0",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/jesse-anderson/linreg-core"
|
|
13
13
|
},
|
|
14
|
+
"homepage": "https://jesse-anderson.net/linreg-core/",
|
|
14
15
|
"files": [
|
|
15
16
|
"linreg_core_bg.wasm",
|
|
16
17
|
"linreg_core.js",
|