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
//! Ridge diagnostics on collinear data: VIF *before and after* regularization,
//! effective degrees of freedom, and GCV-based λ selection.
//!
//! Run with: `cargo run --example ridge_diagnostics`

use ndarray::{Array1, Array2};
use regression_diagnostics::multicollinearity::vif;
use regression_diagnostics::regularized::{select_lambda_gcv, RidgeFit};
use regression_diagnostics::OlsFit;

fn main() {
    // Intercept + two strongly collinear predictors (x2 ≈ 2·x1).
    let rows = [
        [1.0, 2.05],
        [2.0, 3.98],
        [3.0, 6.02],
        [4.0, 8.01],
        [5.0, 9.97],
        [6.0, 12.03],
        [7.0, 13.98],
        [8.0, 16.02],
        [9.0, 17.99],
        [10.0, 20.05],
    ];
    let yv = [2.1, 3.9, 6.2, 7.8, 10.1, 11.9, 14.2, 15.8, 18.1, 19.9];

    let n = rows.len();
    let mut x = Array2::<f64>::ones((n, 3));
    for i in 0..n {
        x[(i, 1)] = rows[i][0];
        x[(i, 2)] = rows[i][1];
    }
    let y = Array1::from(yv.to_vec());

    // --- Before: ordinary least squares ---
    let ols = OlsFit::new(x.clone(), y.clone()).unwrap();
    let ols_vif = vif(&ols);
    println!("OLS (no regularization):");
    println!("  coefficients: {:?}", ols.coefficients().to_vec());
    println!("  VIF  x1 = {:.1},  x2 = {:.1}\n", ols_vif[1], ols_vif[2]);

    // --- After: ridge at a few penalties ---
    println!("Ridge — VIF and effective df shrink as λ grows:");
    println!("     λ        VIF x1     VIF x2    eff.df      GCV");
    for &lam in &[0.0, 0.1, 1.0, 10.0, 100.0] {
        let r = RidgeFit::new(x.clone(), y.clone(), lam).unwrap();
        let rv = r.ridge_vif();
        println!(
            "  {:>7.2}   {:>8.2}   {:>8.2}   {:>7.3}   {:>8.4}",
            lam,
            rv[1],
            rv[2],
            r.effective_df(),
            r.gcv()
        );
    }

    // --- GCV-selected penalty ---
    let grid: Vec<f64> = (0..=8).map(|k| 10f64.powf(k as f64 / 2.0 - 2.0)).collect();
    let best = select_lambda_gcv(x, y, &grid).unwrap();
    println!(
        "\nGCV-selected λ = {:.4}  (effective df {:.3}, GCV {:.4})",
        best.lambda(),
        best.effective_df(),
        best.gcv()
    );
    println!(
        "\nRegularization trades a little bias for a large drop in coefficient\n\
         variance: the VIFs collapse from the OLS values above toward ~1, which is\n\
         the concrete, measurable effect of ridge on multicollinearity."
    );
}