recursive_pls — Recursive (moving-window) PLS¶
Group: Core PLS · Registry tolerance: 1e-08
Description¶
Recursive (moving-window) PLS
Parameters¶
Name |
Type |
Default |
Notes |
|---|---|---|---|
|
|
|
registry benchmark cell value |
|
|
|
registry benchmark cell value |
Explanations¶
Bibliographic source¶
Helland, K., Berntsen, H. E., Borgen, O. S. & Martens, H. (1992). Recursive algorithm for partial least squares regression. Chemometrics and Intelligent Laboratory Systems 14(1–3), 129–137. DOI 10.1016/0169-7439(92)80098-O.
Mathematical principle¶
Process-analytical instruments produce streams of spectra under drifting conditions (changing humidity, instrument warm-up, fouling). Recursive PLS maintains a fitted model that adapts as new samples arrive by re-fitting on a sliding window of the most recent \(w\) samples.
At time step \(t\), the model is fit on \(\{(\mathbf{x}_{t-w+1}, y_{t-w+1}), \ldots, (\mathbf{x}_t, y_t)\}\) and applied to incoming \(\mathbf{x}_{t+1}\). Computational cost is \(O(wpk)\) per step. The window width \(w\) controls a stability/adaptability trade-off: short windows track drift aggressively but are noisier; long windows are stable but lag.
More sophisticated recursive variants (Qin 1998) use exponential forgetting factors instead of a hard window. pls4all’s variant uses the hard-window form for deterministic parity with R pls rolling refits.
Appropriate uses¶
Updating a linear calibration as new batches arrive without retaining and refitting the complete historical data set.
Limits and validation¶
Forgetting-factor updates can drift or forget rare regimes; sample order and batch composition affect the result.
Implementation¶
n4m_estimators_recursive_pls_run (returns predictions only — no global coefficient export, since the model changes per step). The Python sklearn wrapper is an in-sample-only estimator.
Sources and provenance¶
Current implementation: cpp/src/core/extra_pls.cpp.
API and bindings¶
C ABI (ABI 2): n4m_estimators_recursive_pls_run. Use the linked public header for the exact signature, configuration, and result handles.
Python: no current AST-verified public n4m re-export was found for this method. The linked C ABI above is the documented surface in this checkout.
R (source-verified): recursive_pls(formula, data, ncomp = 2L, window_size = 50L, na.action = stats::na.omit).
The source signature has additional required inputs, so no example call is fabricated.
MATLAB / Octave (source-verified): recursive_pls(X, Y, n_components, window_size).
The source signature has additional required inputs, so no example call is fabricated.
Registry parity references 📐
📐
ref.python_scikit_learn(python · python) —scikit-learn1.4.2 · strict (rmse_rel ≤ 1e-08) — Moving-window refit using sklearn PLSRegression (NIPALS).📐
ref.r_pls(R · r) —pls2.8.5 · strict (rmse_rel ≤ 1e-08) — Moving-window refit using Rpls::plsr(SIMPLS by default).
Benchmarks¶
Archived measurement identity. Backend labels in this table are the raw IDs recorded when the benchmark ran (including historical pls4all.* IDs). They preserve measurement provenance and do not describe a current public Python, R, or MATLAB binding; use the source-verified API and bindings section above for current entry points.
Adaptive wall-clock per cell measured against full_matrix.csv. Only backends that implement this method are listed; libraries without the method are omitted.
Verdict · ✓ ref / ≈ ref / ~ shape mark a reference-gate pass at strict / relaxed / qualitative tolerance · ✓ bind = archived binding-harness result agrees with the C++ baseline · ⇄ cross-check = documented by-design selector/RNG/model, noncanonical API/facade convention, or secondary oracle · ✗ divergent · ⚠ error · — not run. The fastest backend per column is marked 🏆.
Reference gate: strict — numeric equivalence (rmse_rel_tol ≤ 1e-08).
Rows tagged with 📐 are the canonical parity references for this method (declared in parity_timing.registry). C++ and external rows show reference parity; archived language-harness rows show binding parity against the C++ backend. Hover the icon for role and tolerance band.
| Backend | Parity | 200×50 (ms) |
|---|---|---|
| C++ native · libn4m | ||
pls4all.cpp.blas+omp | ✓ ref 9e-16 | 2.92 ms |
| Python · archived pls4all benchmark | ||
pls4all.python | ✓ bind | 2.79 ms🏆 |
pls4all.sklearn | ✓ bind | 3.10 ms |
| R · archived pls4all benchmark | ||
pls4all.R | ✓ 4e-15 | 5.74 ms |
pls4all.R.formula | ✓ 4e-15 | 6.55 ms |
pls4all.R.mdatools | ✓ 4e-15 | 6.99 ms |
pls4all.R.pls | ✓ 4e-15 | 7.36 ms |
| Python · external | ||
📐ref.python_scikit_learn | source | 48.1 ms |
| R · external | ||
📐ref.r_pls | ⇄ +4e-15 | 141.6 ms |
| Backend | Parity | 200×50 (ms) |
|---|---|---|
| C++ native · libn4m | ||
pls4all.cpp.blas+omp | ✓ ref 9e-16 | 2.87 ms🏆 |
| Python · archived pls4all benchmark | ||
pls4all.python | ✓ bind | 2.94 ms |
pls4all.sklearn | ✓ bind | 3.11 ms |
| R · archived pls4all benchmark | ||
pls4all.R | ✓ 4e-15 | 5.97 ms |
pls4all.R.formula | ✓ 4e-15 | 6.97 ms |
pls4all.R.mdatools | ✓ 4e-15 | 7.91 ms |
pls4all.R.pls | ✓ 4e-15 | 6.92 ms |
| Python · external | ||
📐ref.python_scikit_learn | source | 46.3 ms |
| R · external | ||
📐ref.r_pls | ⇄ +4e-15 | 144.8 ms |
| Backend | Parity | 200×50 (ms) |
|---|---|---|
| C++ native · libn4m | ||
pls4all.cpp.blas+omp | ✓ ref 9e-16 | 2.95 ms🏆 |
| Python · archived pls4all benchmark | ||
pls4all.python | ✓ bind | 3.02 ms |
pls4all.sklearn | ✓ bind | 3.06 ms |
| R · archived pls4all benchmark | ||
pls4all.R | ✓ 4e-15 | 5.98 ms |
pls4all.R.formula | ✓ 4e-15 | 7.42 ms |
pls4all.R.mdatools | ✓ 4e-15 | 6.94 ms |
pls4all.R.pls | ✓ 4e-15 | 7.32 ms |
| Python · external | ||
📐ref.python_scikit_learn | source | 48.7 ms |
| R · external | ||
📐ref.r_pls | ⇄ +4e-15 | 147.3 ms |
See also: benchmark overview · methods index · interactive dashboard