regression-diagnostics 0.2.0

Statistical diagnostics for OLS regression in Rust: VIF, condition number, adjusted R2, F/AIC/BIC, residual tests (Durbin-Watson, Breusch-Pagan, White, Jarque-Bera), influence measures (leverage, Cook's distance, DFFITS), QQ-plot data, and an R/statsmodels-style summary().
Documentation
use ndarray::Array1;

use crate::residuals::externally_studentized_residuals;
use crate::OlsFit;

/// DFFITS `ᵢ` for each observation.
///
/// `DFFITSᵢ = tᵢ · √(hᵢ / (1 − hᵢ))`
///
/// where `tᵢ` is the **externally** studentized residual and `hᵢ` the leverage.
/// It measures, in standard-error units, how much observation `i`'s own fitted
/// value changes when that observation is deleted from the fit — a leave-one-out
/// influence measure.
///
/// A commonly cited flag is `|DFFITSᵢ| > 2·√(p/n)` (convention, not a hard rule).
///
/// DFFITS uses the external (leave-one-out) residual scale, whereas
/// [`cooks_distance`](super::cooks_distance) uses the internal one; the two
/// normally agree, but DFFITS reacts more sharply to a lone extreme outlier since
/// that point is excluded from the scale it is judged against. Entries are `NaN`
/// where the external studentized residual is undefined (`n − p − 1 < 1`).
pub fn dffits(fit: &OlsFit) -> Array1<f64> {
    let t = externally_studentized_residuals(fit);
    let lev = fit.leverage();

    Array1::from_shape_fn(fit.n_observations(), |i| {
        let h = lev[i];
        let one_minus_h = 1.0 - h;
        if one_minus_h <= 0.0 {
            return f64::NAN;
        }
        t[i] * (h / one_minus_h).sqrt()
    })
}