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 crate::OlsFit;

/// Gaussian log-likelihood of the fitted OLS model at its maximum-likelihood
/// residual variance.
///
/// `ℓ = −(n/2)·[ln(2π) + 1 + ln(RSS / n)]`
///
/// This is the log-likelihood **under the standard OLS normality assumption**
/// on the residuals — it is a modeling assumption, not a property of arbitrary
/// data, and the value is only meaningful to the extent that assumption holds.
/// Note the divisor is `n` (the MLE of the error variance), not `n − p`.
pub fn log_likelihood(fit: &OlsFit) -> f64 {
    let n = fit.n_observations() as f64;
    let rss = fit.residual_sum_of_squares();
    -0.5 * n * ((2.0 * std::f64::consts::PI).ln() + 1.0 + (rss / n).ln())
}

/// Akaike Information Criterion, `AIC = −2ℓ + 2k`.
///
/// **Parameter-count convention:** `k` is the number of regression parameters
/// (design-matrix columns, intercept included), matching `statsmodels`'
/// `OLSResults.aic`. The error variance `σ²` is *not* counted as an extra
/// parameter here. This is a convention choice — some references use `k + 1` —
/// and it is fixed this way so values line up with the `statsmodels` output this
/// crate validates against.
pub fn aic(fit: &OlsFit) -> f64 {
    let k = fit.n_parameters() as f64;
    -2.0 * log_likelihood(fit) + 2.0 * k
}

/// Bayesian (Schwarz) Information Criterion, `BIC = −2ℓ + ln(n)·k`.
///
/// Uses the same `k` convention as [`aic`] (regression parameters only), again
/// matching `statsmodels`' `OLSResults.bic`.
pub fn bic(fit: &OlsFit) -> f64 {
    let k = fit.n_parameters() as f64;
    let n = fit.n_observations() as f64;
    -2.0 * log_likelihood(fit) + n.ln() * k
}