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
//! Fit OLS on a small realistic dataset and print the full `.summary()` report,
//! then walk the per-observation influence diagnostics.
//!
//! Run with: `cargo run --example full_diagnostics`

use ndarray::{Array1, Array2};
use regression_diagnostics::influence::{cooks_distance, dffits, leverage};
use regression_diagnostics::OlsFit;

fn main() {
    // Synthetic "house price" data: price ~ area + age + rooms, with mild noise
    // and one deliberately unusual observation (row 9: large, old, cheap).
    // Columns of predictors: area (100 sqft), age (years), rooms.
    let predictors = [
        [12.0, 5.0, 3.0],
        [15.0, 8.0, 3.0],
        [20.0, 2.0, 4.0],
        [22.0, 15.0, 4.0],
        [28.0, 6.0, 5.0],
        [30.0, 20.0, 5.0],
        [35.0, 3.0, 6.0],
        [18.0, 10.0, 3.0],
        [40.0, 45.0, 6.0], // unusual: very old, large
        [25.0, 7.0, 4.0],
        [33.0, 12.0, 5.0],
        [21.0, 4.0, 4.0],
    ];
    let price = [
        250.0, 280.0, 360.0, 340.0, 470.0, 440.0, 590.0, 300.0, 380.0, 430.0, 540.0, 390.0,
    ];

    let n = predictors.len();
    let mut x = Array2::<f64>::ones((n, 4)); // leading intercept column
    for i in 0..n {
        for j in 0..3 {
            x[(i, j + 1)] = predictors[i][j];
        }
    }
    let y = Array1::from(price.to_vec());

    let fit = OlsFit::new(x, y).expect("valid design");

    println!("{}\n", fit.summary());

    println!("Per-observation influence (x1=area, x2=age, x3=rooms):");
    println!("  obs   leverage   cooks_d     dffits");
    let lev = leverage(&fit);
    let cd = cooks_distance(&fit);
    let df = dffits(&fit);
    let cooks_flag = 4.0 / n as f64;
    for i in 0..n {
        let flag = if cd[i] > cooks_flag { "  <- high" } else { "" };
        println!(
            "  {:>3}   {:>8.4}   {:>8.4}   {:>8.4}{}",
            i, lev[i], cd[i], df[i], flag
        );
    }
    println!(
        "\n(Cook's distance flag threshold 4/n = {:.4}; convention, not a hard rule.)",
        cooks_flag
    );
}