# `pls_diagnostic_t2` — Hotelling T² score _Group_: **Diagnostic** · _Registry tolerance_: `10.0` ## Description PLS Hotelling T² (§9) > **Registry note** — R `mdatools::pls` is the only widely installable external reference. Both use SIMPLS-style but differ on score normalization conventions — tolerance is wide enough to flag the R ref's presence. ### Parameters | Name | Type | Default | Notes | |------|------|---------|-------| | `n_components` | `int` | `4` | registry benchmark cell value | ## Explanations ### Bibliographic source Hotelling, H. (1931). *The generalization of Student's ratio*. Annals of Mathematical Statistics 2(3), 360–378. — applied to PLS scores by MacGregor & Kourti 1995. ### Mathematical principle Hotelling T² measures how unusual a sample is **within the latent score space**: $T_i^2 = \mathbf{t}_i^{\top} \boldsymbol{\Lambda}^{-1} \mathbf{t}_i$ where $\boldsymbol{\Lambda}$ is the diagonal matrix of score variances. Under multivariate normality of the scores, $\frac{n(n-k)}{k(n^2-1)} T^2 \sim F_{k, n-k}$, giving an exact upper control limit at any $\alpha$. T² complements the Q residual (next entry): Q measures the **distance to the model** (variation in $\mathbf{X}$ that the latent space does not explain), while T² measures the **distance within the model** (unusual score combination on otherwise well-explained samples). Joint T²/Q monitoring catches both kinds of out-of-control points. Reported per-sample as a 1-D vector aligned with the rows of the input. ### Implementation `n4m_pls_diagnostics_compute` with stat='t2'. Reference: R `mdatools 0.15.0` (Kucheryavskiy). MATLAB header (`bindings/matlab/+pls4all/pls_diagnostics.m`): ```text pls4all.pls_diagnostics Hotelling T2, Q residuals, DModX from a SIMPLS fit. res = pls4all.pls_diagnostics(X, Y, n_components) res = pls4all.pls_diagnostics(X, Y, n_components, X_reference) Fits an internal SIMPLS model (store_scores=1) and evaluates row-wise ``` ### Usage Every pls4all binding tab dispatches into the same C kernel; the external libraries listed at the bottom of the page are the parity references registered in `benchmarks.parity_timing.registry`. Switch tabs to read the same fit in your language. The R package now ships drop-in-compatible facades for the CRAN `pls` package (`plsr`, `pcr`, `mvr`) and for the `mdatools::pls(x, y, ...)` matrix idiom — those tabs appear only on the methods that have a meaningful equivalence. **pls4all bindings** ::::{tab-set} :class: pls4all-bindings :::{tab-item} C ABI · libn4m :sync: c :class-label: lang-c ```c /* C ABI — libn4m */ n4m_context_t* ctx = n4m_context_create(); n4m_config_t* cfg = n4m_config_create(); n4m_method_result_t* res = NULL; n4m_pls_diagnostics_compute(ctx, cfg, &x_view, &y_view, /* hyperparams */, &res); /* … read coefficients / mask / scores via */ /* n4m_method_result_get_double_matrix / vector / scalar … */ n4m_method_result_destroy(res); n4m_config_destroy(cfg); n4m_context_destroy(ctx); ``` ::: :::{tab-item} Python · pls4all (raw) :sync: python-raw :class-label: lang-python ```python import pls4all from pls4all._methods import pls_diagnostics_compute with pls4all.Context() as ctx, pls4all.Config() as cfg: res = pls_diagnostics_compute(ctx, cfg, X, y, n_components=4) # then: res.matrix("predictions"), res.matrix("coefficients"), # res.vector("mask"), res.scalar("intercept"), … ``` ::: :::{tab-item} Python · pls4all.sklearn :sync: python-sklearn :class-label: lang-python ```python from pls4all.sklearn import t2_score result = t2_score(X, y, n_components=4) ``` ::: :::{tab-item} R · pls4all_method() :sync: r-dispatcher :class-label: lang-r ```r library(pls4all) # Unified low-level dispatcher (May 2026 R cleanup): res <- pls4all_method("pls_diagnostic_t2", X, y, n_components = 4L) # res is a named list with MethodResult arrays/scalars. # selected_indices / top_k_intervals are 1-based. ``` ::: :::{tab-item} MATLAB · pls4all (MEX) :sync: matlab-mex :class-label: lang-matlab ```matlab res = pls4all.pls_diagnostics(X, y, 4); % see header of bindings/matlab/+pls4all/pls_diagnostics.m for full % parameter surface: % res = pls_diagnostics(X, Y, n_components, X_reference) yhat = predict(res, Xtest); ``` ::: :::{tab-item} MATLAB · pls4all (classdef) :sync: matlab-classdef :class-label: lang-matlab _No idiomatic classdef wrapper — invoke `pls4all.fit("pls_diagnostic_t2", X, y, …)` directly from the unified MEX factory._ ::: :::: **Registry parity references** 📐 :::{card} :class-card: external-refs - 📐 **`ref.r_mdatools`** (R · r) — `mdatools` 0.15.0 · qualitative (rmse_rel ≤ 1e+01) — R `mdatools::pls` with `predict()$xdecomp$T2 / $Q`. DModX is derived locally from $Q + DOF. mdatools uses different SIMPLS deflation / normalization conventions than pls4all, so cross-implementation parity is qualitative. ::: ### Benchmarks Adaptive wall-clock per cell measured against [`full_matrix.csv`](../benchmarks/overview.md). 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 = pls4all binding agrees with the C++ baseline  ·  ✗ divergent  ·  ⚠ error  ·  — not run. The fastest backend per column is marked 🏆. **Reference gate**: qualitative — shape/smoke comparison only. The external library and pls4all do not produce numerically equivalent output for this method (see the MethodSpec notes); the `rmse_rel_tol ≤ 1e+01` budget is set wide on purpose. Treat ~ shape as *“we ran both, both finished”*, not as numerical agreement. Rows tagged with **📐** are the canonical parity references for this method (declared in [`parity_timing.registry`](../benchmarks/methodology.md)). C++ and external rows show reference parity; pls4all language bindings show binding parity against the C++ backend. Hover the icon for role and tolerance band. ::::{tab-set} :class: parity-tabs :::{tab-item} 1 thread :sync: threads-1
BackendParity50×250 (ms)100×50 (ms)100×500 (ms)100×2500 (ms)200×30 (ms)250×50 (ms)500×50 (ms)500×500 (ms)500×2500 (ms)2500×50 (ms)2500×500 (ms)2500×2500 (ms)10000×50 (ms)10000×500 (ms)
C++ native · libn4m
pls4all.cpp.blas≈ +7e-152.55 ms🏆2.31 ms14.0 ms70.8 ms1.24 ms🏆2.66 ms5.74 ms69.1 ms336.0 ms🏆33.1 ms🏆333.6 ms1.7 s🏆130.6 ms1.5 s
pls4all.cpp.blas+omp≈ +7e-152.60 ms1.28 ms🏆11.9 ms🏆71.6 ms1.28 ms2.62 ms5.57 ms🏆70.4 ms345.4 ms33.3 ms320.7 ms🏆1.7 s128.5 ms1.5 s
pls4all.cpp.omp≈ +2e-142.78 ms2.51 ms14.0 ms67.1 ms🏆1.26 ms2.63 ms7.11 ms73.7 ms349.5 ms34.5 ms337.9 ms1.8 s128.4 ms🏆1.5 s🏆
pls4all.cpp.ref≈ +2e-142.72 ms1.37 ms13.9 ms73.9 ms1.27 ms2.77 ms7.35 ms67.5 ms🏆357.7 ms35.1 ms332.9 ms1.8 s134.8 ms1.5 s
Python · pls4all
pls4all.python✓ bind2.57 ms1.43 ms3.33 ms
pls4all.sklearn✓ bind2.74 ms1.68 ms2.56 ms🏆
R · pls4all
pls4all.R✓ 4e-1312.0 ms4.52 ms10.6 ms
pls4all.R.formula✓ 4e-1319.4 ms7.45 ms9.82 ms
pls4all.R.mdatools✓ 4e-1321.2 ms5.56 ms12.3 ms
pls4all.R.pls✓ 4e-1320.1 ms5.18 ms12.4 ms
MATLAB · pls4all
pls4all.matlab✗ +1e+014.28 ms2.25 ms4.81 ms
pls4all.matlab.classdef✗ +1e+015.23 ms2.72 ms4.95 ms
R · external
📐ref.r_mdatoolssource35.9 ms18.6 ms20.5 ms
::: :::{tab-item} 3 threads :sync: threads-3
BackendParity50×250 (ms)100×50 (ms)100×500 (ms)100×2500 (ms)200×30 (ms)250×50 (ms)500×50 (ms)500×500 (ms)500×2500 (ms)2500×50 (ms)2500×500 (ms)2500×2500 (ms)10000×50 (ms)10000×500 (ms)
C++ native · libn4m
pls4all.cpp.blas~ shape 3e-151.37 ms
pls4all.cpp.blas+omp~ shape 3e-151.91 ms
pls4all.cpp.omp~ shape 5e-151.37 ms
pls4all.cpp.ref~ shape 5e-151.59 ms
Python · pls4all
pls4all.python✓ 1e-131.25 ms🏆
pls4all.sklearn✓ 1e-131.72 ms
R · pls4all
pls4all.R✓ bind4.26 ms
pls4all.R.formula✓ bind5.80 ms
pls4all.R.mdatools✓ bind5.11 ms
pls4all.R.pls✓ bind5.42 ms
MATLAB · pls4all
pls4all.matlab✗ +1e+012.38 ms
pls4all.matlab.classdef✗ +1e+013.05 ms
R · external
📐ref.r_mdatoolssource18.6 ms
::: :::{tab-item} 10 threads :sync: threads-10
BackendParity50×250 (ms)100×50 (ms)100×500 (ms)100×2500 (ms)200×30 (ms)250×50 (ms)500×50 (ms)500×500 (ms)500×2500 (ms)2500×50 (ms)2500×500 (ms)2500×2500 (ms)10000×50 (ms)10000×500 (ms)
C++ native · libn4m
pls4all.cpp.blas~ shape 3e-151.17 ms🏆
pls4all.cpp.blas+omp~ shape 3e-151.19 ms
pls4all.cpp.omp~ shape 5e-151.19 ms
pls4all.cpp.ref~ shape 5e-151.18 ms
Python · pls4all
pls4all.python✓ 1e-131.19 ms
pls4all.sklearn✓ 1e-131.31 ms
R · pls4all
pls4all.R✓ bind3.07 ms
pls4all.R.formula✓ bind3.75 ms
pls4all.R.mdatools✓ bind3.70 ms
pls4all.R.pls✓ bind3.71 ms
MATLAB · pls4all
pls4all.matlab✗ +1e+011.96 ms
pls4all.matlab.classdef✗ +1e+012.18 ms
R · external
📐ref.r_mdatoolssource13.6 ms
::: :::: --- _See also_: [benchmark overview](../benchmarks/overview.md) · [methods index](index.md) · [interactive dashboard](../landing/dashboard.md)