linreg-core 0.7.0 → 0.8.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 +224 -4
- package/linreg_core.d.ts +644 -0
- package/linreg_core.js +1244 -168
- package/linreg_core_bg.wasm +0 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -10,20 +10,22 @@
|
|
|
10
10
|
[](https://jesse-anderson.net/linreg-core/)
|
|
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.
|
|
13
|
+
A lightweight, self-contained linear regression library written in Rust. Compiles to WebAssembly for browser use, Python bindings via PyO3, a native Windows DLL for Excel VBA, or runs as a native Rust crate.
|
|
14
14
|
|
|
15
15
|
**Key design principle:** All linear algebra and statistical distribution functions are implemented from scratch — no external math libraries required. This keeps binary sizes small and makes the crate highly portable.
|
|
16
16
|
|
|
17
|
+
**[Live Demo Link](https://jesse-anderson.net/linreg-core/)**
|
|
17
18
|
---
|
|
18
19
|
|
|
19
20
|
## Table of Contents
|
|
20
21
|
|
|
21
22
|
| Section | Description |
|
|
22
23
|
|---------|-------------|
|
|
23
|
-
| [Features](#features) | Regression methods, model statistics, diagnostic tests |
|
|
24
|
+
| [Features](#features) | Regression methods, model statistics, feature importance, diagnostic tests |
|
|
24
25
|
| [Rust Usage](#rust-usage) | Native Rust crate usage |
|
|
25
26
|
| [WebAssembly Usage](#webassembly-usage) | Browser/JavaScript usage |
|
|
26
27
|
| [Python Usage](#python-usage) | Python bindings via PyO3 |
|
|
28
|
+
| [VBA / Excel Usage](#vba--excel-usage) | Excel VBA via native Windows DLL |
|
|
27
29
|
| [Feature Flags](#feature-flags) | Build configuration options |
|
|
28
30
|
| [Validation](#validation) | Testing and verification |
|
|
29
31
|
| [Implementation Notes](#implementation-notes) | Technical details |
|
|
@@ -37,10 +39,13 @@ A lightweight, self-contained linear regression library written in Rust. Compile
|
|
|
37
39
|
- **Ridge Regression:** L2-regularized regression with optional standardization, effective degrees of freedom, model selection criteria
|
|
38
40
|
- **Lasso Regression:** L1-regularized regression via coordinate descent with automatic variable selection, convergence tracking, model selection criteria
|
|
39
41
|
- **Elastic Net:** Combined L1 + L2 regularization for variable selection with multicollinearity handling, active set convergence, model selection criteria
|
|
42
|
+
- **Polynomial Regression:** Polynomial fitting of any degree with centering/standardization for numerical stability
|
|
40
43
|
- **LOESS:** Locally estimated scatterplot smoothing for non-parametric curve fitting with configurable span, polynomial degree, and robust fitting
|
|
41
44
|
- **WLS (Weighted Least Squares):** Regression with observation weights for heteroscedastic data, includes confidence intervals
|
|
45
|
+
- **Prediction Intervals:** Uncertainty bounds for individual future observations (OLS, Ridge, Lasso, Elastic Net)
|
|
42
46
|
- **K-Fold Cross Validation:** Model evaluation and hyperparameter tuning for all regression types (OLS, Ridge, Lasso, Elastic Net) with customizable folds, shuffling, and seeding
|
|
43
47
|
- **Lambda Path Generation:** Create regularization paths for cross-validation
|
|
48
|
+
- **Model Serialization:** Save/load trained models to/from JSON for all model types
|
|
44
49
|
|
|
45
50
|
### Model Statistics
|
|
46
51
|
- **Fit Metrics:** R-squared, Adjusted R-squared, F-statistic, F-test p-value
|
|
@@ -49,6 +54,12 @@ A lightweight, self-contained linear regression library written in Rust. Compile
|
|
|
49
54
|
- **Residuals:** Raw residuals, standardized residuals, fitted values, leverage (hat matrix diagonal)
|
|
50
55
|
- **Multicollinearity:** Variance Inflation Factor (VIF) for each predictor
|
|
51
56
|
|
|
57
|
+
### Feature Importance
|
|
58
|
+
- **Standardized Coefficients:** Coefficients scaled by standard deviation for cross-variable comparison
|
|
59
|
+
- **SHAP Values:** Exact SHAP (Shapley Additive Explanations) for linear models — local and global importance
|
|
60
|
+
- **Permutation Importance:** Performance drop when feature values are randomly shuffled
|
|
61
|
+
- **VIF Ranking:** Automatic multicollinearity assessment with interpretation guidance
|
|
62
|
+
|
|
52
63
|
### Diagnostic Tests
|
|
53
64
|
| Category | Tests |
|
|
54
65
|
|----------|-------|
|
|
@@ -67,7 +78,7 @@ Add to your `Cargo.toml`:
|
|
|
67
78
|
|
|
68
79
|
```toml
|
|
69
80
|
[dependencies]
|
|
70
|
-
linreg-core = { version = "0.
|
|
81
|
+
linreg-core = { version = "0.8", default-features = false }
|
|
71
82
|
```
|
|
72
83
|
|
|
73
84
|
### OLS Regression (Rust)
|
|
@@ -196,6 +207,61 @@ fn main() -> Result<(), linreg_core::Error> {
|
|
|
196
207
|
}
|
|
197
208
|
```
|
|
198
209
|
|
|
210
|
+
### Polynomial Regression (Rust)
|
|
211
|
+
|
|
212
|
+
```rust,no_run
|
|
213
|
+
use linreg_core::polynomial::{polynomial_regression, polynomial_predict};
|
|
214
|
+
|
|
215
|
+
fn main() -> Result<(), linreg_core::Error> {
|
|
216
|
+
let y = vec![2.1, 4.9, 10.8, 19.5, 32.1]; // Quadratic relationship
|
|
217
|
+
let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
|
|
218
|
+
|
|
219
|
+
// Fit degree-2 polynomial with centering for numerical stability
|
|
220
|
+
let fit = polynomial_regression(&y, &x, 2, true, true)?;
|
|
221
|
+
|
|
222
|
+
println!("R²: {:.4}", fit.ols_output.r_squared);
|
|
223
|
+
println!("Coefficients: {:?}", fit.ols_output.coefficients);
|
|
224
|
+
|
|
225
|
+
// Predict at new x values
|
|
226
|
+
let new_x = vec![2.5, 5.5];
|
|
227
|
+
let predictions = polynomial_predict(&fit, &new_x)?;
|
|
228
|
+
println!("Predictions: {:?}", predictions);
|
|
229
|
+
|
|
230
|
+
Ok(())
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Feature Importance (Rust)
|
|
235
|
+
|
|
236
|
+
```rust,no_run
|
|
237
|
+
use linreg_core::feature_importance::{
|
|
238
|
+
standardized_coefficients, shap_values_linear,
|
|
239
|
+
permutation_importance_ols, PermutationImportanceOptions,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
fn main() -> Result<(), linreg_core::Error> {
|
|
243
|
+
let y = vec![2.5, 3.7, 4.2, 5.1, 6.3];
|
|
244
|
+
let x1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
|
|
245
|
+
let x2 = vec![2.0, 4.0, 5.0, 4.0, 3.0];
|
|
246
|
+
|
|
247
|
+
// Standardized coefficients for comparison
|
|
248
|
+
let std_coef = standardized_coefficients(&[0.8, 0.5], &[x1.clone(), x2.clone()])?;
|
|
249
|
+
println!("Standardized: {:?}", std_coef.standardized_coefficients);
|
|
250
|
+
|
|
251
|
+
// SHAP values for local explanations
|
|
252
|
+
let shap = shap_values_linear(&[x1, x2], &[1.5, 0.3])?;
|
|
253
|
+
println!("SHAP: {:?}", shap.mean_abs_shap);
|
|
254
|
+
|
|
255
|
+
// Permutation importance
|
|
256
|
+
let perm = permutation_importance_ols(
|
|
257
|
+
&y, &[x1, x2], &PermutationImportanceOptions::default()
|
|
258
|
+
)?;
|
|
259
|
+
println!("Importance: {:?}", perm.importance);
|
|
260
|
+
|
|
261
|
+
Ok(())
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
199
265
|
### Diagnostic Tests (Rust)
|
|
200
266
|
|
|
201
267
|
```rust
|
|
@@ -1104,18 +1170,166 @@ The `save_model()` and `load_model()` functions work with all result types: `OLS
|
|
|
1104
1170
|
|
|
1105
1171
|
---
|
|
1106
1172
|
|
|
1173
|
+
## VBA / Excel Usage
|
|
1174
|
+
|
|
1175
|
+
The library ships as a native Windows DLL, letting you call it directly from Excel VBA via `Declare` statements. Prebuilt binaries are included in the `VBA_Example/` directory:
|
|
1176
|
+
|
|
1177
|
+
| File | Architecture |
|
|
1178
|
+
|------|-------------|
|
|
1179
|
+
| `linreg_core_x64.dll` | 64-bit Excel (Office 2010+) |
|
|
1180
|
+
| `linreg_core_x86.dll` | 32-bit Excel (legacy) |
|
|
1181
|
+
|
|
1182
|
+
### Installation
|
|
1183
|
+
|
|
1184
|
+
1. Copy `linreg_core_x64.dll` (and/or `linreg_core_x86.dll`) to the same folder as your `.xlsm` workbook.
|
|
1185
|
+
2. Import `LinregCore.bas` into your VBA project (ALT+F11 → File → Import File).
|
|
1186
|
+
3. Optionally import `ExampleMacros.bas` for ready-to-run demo macros. Once both files are imported, run `SetupWorkbook()` from the Immediate Window or a button to automatically create example sheets and load sample data.
|
|
1187
|
+
|
|
1188
|
+
### Building from Source
|
|
1189
|
+
|
|
1190
|
+
```bash
|
|
1191
|
+
# 64-bit (modern Excel)
|
|
1192
|
+
cargo build --release --target x86_64-pc-windows-msvc --features ffi
|
|
1193
|
+
|
|
1194
|
+
# 32-bit (legacy Excel)
|
|
1195
|
+
cargo build --release --target i686-pc-windows-msvc --features ffi
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
The 32-bit build automatically uses `linreg_core.def` to strip stdcall decoration, so VBA `Declare` statements work without modification.
|
|
1199
|
+
|
|
1200
|
+
### High-Level Wrappers
|
|
1201
|
+
|
|
1202
|
+
`LinregCore.bas` exposes friendly wrapper functions that return 2D Excel arrays you can drop straight into cells with `Application.Transpose`:
|
|
1203
|
+
|
|
1204
|
+
```vba
|
|
1205
|
+
' OLS regression - returns (k+6)×5 summary array
|
|
1206
|
+
Dim result As Variant
|
|
1207
|
+
result = LinReg_OLS(y, X)
|
|
1208
|
+
|
|
1209
|
+
' Regularized regression
|
|
1210
|
+
result = LinReg_Ridge(y, X, lambda:=1.0, standardize:=True)
|
|
1211
|
+
result = LinReg_Lasso(y, X, lambda:=0.1)
|
|
1212
|
+
result = LinReg_ElasticNet(y, X, lambda:=0.1, alpha:=0.5)
|
|
1213
|
+
|
|
1214
|
+
' Weighted OLS
|
|
1215
|
+
result = LinReg_WLS(y, X, weights)
|
|
1216
|
+
|
|
1217
|
+
' Prediction intervals (n_new × 4: predicted, lower, upper, SE)
|
|
1218
|
+
result = LinReg_PredictionIntervals(y, X, newX, alpha:=0.05)
|
|
1219
|
+
|
|
1220
|
+
' Diagnostic tests - each returns 1×3: {statistic, p-value, df}
|
|
1221
|
+
result = LinReg_BreuschPagan(y, X)
|
|
1222
|
+
result = LinReg_White(y, X)
|
|
1223
|
+
result = LinReg_JarqueBera(y, X)
|
|
1224
|
+
result = LinReg_ShapiroWilk(y, X)
|
|
1225
|
+
result = LinReg_AndersonDarling(y, X)
|
|
1226
|
+
result = LinReg_HarveyCollier(y, X)
|
|
1227
|
+
result = LinReg_Rainbow(y, X, fraction:=0.5)
|
|
1228
|
+
result = LinReg_Reset(y, X)
|
|
1229
|
+
result = LinReg_DurbinWatson(y, X) ' {DW statistic, ρ, ""}
|
|
1230
|
+
result = LinReg_BreuschGodfrey(y, X, lagOrder:=1)
|
|
1231
|
+
|
|
1232
|
+
' Influence diagnostics
|
|
1233
|
+
result = LinReg_VIF(y, X) ' p×1
|
|
1234
|
+
result = LinReg_CooksDistance(y, X) ' n×1
|
|
1235
|
+
result = LinReg_DFFITS(y, X) ' n×1
|
|
1236
|
+
result = LinReg_DFBETAS(y, X) ' (n+1)×(p+1) with header row/col
|
|
1237
|
+
|
|
1238
|
+
' Regularization path and cross-validation
|
|
1239
|
+
result = LinReg_LambdaPath(y, X, nLambda:=100, lmr:=0.01, alpha:=1.0)
|
|
1240
|
+
result = LinReg_KFoldOLS(y, X, nFolds:=5) ' 1×6 CV metrics
|
|
1241
|
+
result = LinReg_KFoldRidge(y, X, lambda:=1.0)
|
|
1242
|
+
result = LinReg_KFoldLasso(y, X, lambda:=0.1)
|
|
1243
|
+
result = LinReg_KFoldElasticNet(y, X, lambda:=0.1, alpha:=0.5)
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
All wrappers return a 1-element array containing an error string on failure:
|
|
1247
|
+
|
|
1248
|
+
```vba
|
|
1249
|
+
If IsArray(result) And UBound(result, 1) = 0 Then
|
|
1250
|
+
MsgBox "Error: " & result(0)
|
|
1251
|
+
Exit Sub
|
|
1252
|
+
End If
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
### Low-Level Handle API
|
|
1256
|
+
|
|
1257
|
+
The DLL uses an opaque handle pattern. All `LR_*` functions return a `usize` handle (0 = error); call `LR_Free` when done:
|
|
1258
|
+
|
|
1259
|
+
```vba
|
|
1260
|
+
' --- declarations already in LinregCore.bas ---
|
|
1261
|
+
' Private Declare PtrSafe Function LR_OLS Lib "linreg_core_x64.dll" ...
|
|
1262
|
+
' Private Declare PtrSafe Sub LR_Free Lib "linreg_core_x64.dll" ...
|
|
1263
|
+
|
|
1264
|
+
Sub LowLevelExample()
|
|
1265
|
+
Dim n As Long, p As Long
|
|
1266
|
+
n = 5 : p = 1
|
|
1267
|
+
|
|
1268
|
+
Dim y(4) As Double
|
|
1269
|
+
y(0) = 2.5 : y(1) = 3.7 : y(2) = 4.2 : y(3) = 5.1 : y(4) = 6.3
|
|
1270
|
+
|
|
1271
|
+
' X is row-major, no intercept column (added automatically)
|
|
1272
|
+
Dim X(4) As Double
|
|
1273
|
+
X(0) = 1 : X(1) = 2 : X(2) = 3 : X(3) = 4 : X(4) = 5
|
|
1274
|
+
|
|
1275
|
+
Dim h As LongPtr
|
|
1276
|
+
h = LR_OLS(VarPtr(y(0)), n, VarPtr(X(0)), n, p)
|
|
1277
|
+
|
|
1278
|
+
If h = 0 Then
|
|
1279
|
+
MsgBox "Regression failed"
|
|
1280
|
+
Exit Sub
|
|
1281
|
+
End If
|
|
1282
|
+
|
|
1283
|
+
Dim r2 As Double, mse As Double
|
|
1284
|
+
r2 = LR_GetRSquared(h)
|
|
1285
|
+
mse = LR_GetMSE(h)
|
|
1286
|
+
|
|
1287
|
+
' Retrieve coefficient vector (intercept + slopes = p+1 values)
|
|
1288
|
+
Dim coefs(1) As Double
|
|
1289
|
+
LR_GetCoefficients h, VarPtr(coefs(0)), p + 1
|
|
1290
|
+
|
|
1291
|
+
Debug.Print "R²=" & r2 & " MSE=" & mse
|
|
1292
|
+
Debug.Print "Intercept=" & coefs(0) & " Slope=" & coefs(1)
|
|
1293
|
+
|
|
1294
|
+
LR_Free h
|
|
1295
|
+
End Sub
|
|
1296
|
+
```
|
|
1297
|
+
|
|
1298
|
+
### Key FFI Functions
|
|
1299
|
+
|
|
1300
|
+
| Category | Functions |
|
|
1301
|
+
|----------|-----------|
|
|
1302
|
+
| **Lifecycle** | `LR_Init`, `LR_Free`, `LR_GetLastError`, `LR_Version` |
|
|
1303
|
+
| **Regression** | `LR_OLS`, `LR_Ridge`, `LR_Lasso`, `LR_ElasticNet`, `LR_WLS` |
|
|
1304
|
+
| **Predictions** | `LR_PredictionIntervals` |
|
|
1305
|
+
| **Diagnostics** | `LR_BreuschPagan`, `LR_White`, `LR_JarqueBera`, `LR_ShapiroWilk`, `LR_AndersonDarling`, `LR_HarveyCollier`, `LR_Rainbow`, `LR_Reset`, `LR_DurbinWatson`, `LR_BreuschGodfrey` |
|
|
1306
|
+
| **Influence** | `LR_CooksDistance`, `LR_DFFITS`, `LR_DFBETAS`, `LR_VIF` |
|
|
1307
|
+
| **Path / CV** | `LR_LambdaPath`, `LR_KFoldOLS`, `LR_KFoldRidge`, `LR_KFoldLasso`, `LR_KFoldElasticNet` |
|
|
1308
|
+
| **Scalar getters** | `LR_GetRSquared`, `LR_GetAdjRSquared`, `LR_GetFStatistic`, `LR_GetFPValue`, `LR_GetMSE`, `LR_GetIntercept`, `LR_GetDF`, `LR_GetNNonzero`, `LR_GetStatistic`, `LR_GetPValue`, `LR_GetTestDF`, `LR_GetAutocorrelation` |
|
|
1309
|
+
| **Vector getters** | `LR_GetCoefficients`, `LR_GetStdErrors`, `LR_GetTStats`, `LR_GetPValues`, `LR_GetResiduals`, `LR_GetFittedValues`, `LR_GetVector`, `LR_GetMatrix`, `LR_GetPredicted`, `LR_GetLowerBound`, `LR_GetUpperBound`, `LR_GetSEPred` |
|
|
1310
|
+
|
|
1311
|
+
### Running FFI Tests
|
|
1312
|
+
|
|
1313
|
+
```bash
|
|
1314
|
+
cargo test --features ffi --test ffi_tests
|
|
1315
|
+
cargo test --features ffi --test ffi_vba_tests
|
|
1316
|
+
```
|
|
1317
|
+
|
|
1318
|
+
---
|
|
1319
|
+
|
|
1107
1320
|
## Feature Flags
|
|
1108
1321
|
|
|
1109
1322
|
| Feature | Default | Description |
|
|
1110
1323
|
|---------|---------|-------------|
|
|
1111
1324
|
| `wasm` | Yes | Enables WASM bindings and browser support |
|
|
1112
1325
|
| `python` | No | Enables Python bindings via PyO3 |
|
|
1326
|
+
| `ffi` | No | Enables Windows DLL bindings for VBA/Excel use |
|
|
1113
1327
|
| `validation` | No | Includes test data for validation tests |
|
|
1114
1328
|
|
|
1115
1329
|
For native Rust without WASM overhead:
|
|
1116
1330
|
|
|
1117
1331
|
```toml
|
|
1118
|
-
linreg-core = { version = "0.
|
|
1332
|
+
linreg-core = { version = "0.8", default-features = false }
|
|
1119
1333
|
```
|
|
1120
1334
|
|
|
1121
1335
|
For Python bindings (built with maturin):
|
|
@@ -1139,6 +1353,12 @@ cargo test
|
|
|
1139
1353
|
# WASM tests
|
|
1140
1354
|
wasm-pack test --node
|
|
1141
1355
|
|
|
1356
|
+
# FFI tests (VBA/C bindings)
|
|
1357
|
+
cargo test --features ffi
|
|
1358
|
+
|
|
1359
|
+
# Python tests (requires maturin build)
|
|
1360
|
+
pytest tests/python/
|
|
1361
|
+
|
|
1142
1362
|
# All tests including doctests
|
|
1143
1363
|
cargo test --all-features
|
|
1144
1364
|
```
|