linreg-core 0.6.1 → 0.8.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
@@ -10,20 +10,22 @@
10
10
  [![Live Demo](https://img.shields.io/badge/demo-online-brightgreen)](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.6", default-features = false }
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.6", default-features = false }
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
  ```