Skip to main content

regression_diagnostics/glm/
mod.rs

1//! Diagnostics for **generalized linear models** beyond binary logistic — the
2//! exponential-dispersion families that share one iteratively-reweighted
3//! least-squares (IRLS) engine but differ in link, variance, and residual scale.
4//!
5//! Where [`logistic`](crate::logistic) is a single hand-written model, this
6//! module factors the GLM machinery into a [`Family`] trait and one generic
7//! solver [`GlmFit`], so each family is just its link / variance / deviance
8//! formulas. Three families are provided:
9//!
10//! * [`Poisson`] — counts, `V(μ) = μ`, fixed dispersion.
11//! * [`NegativeBinomial`] — over-dispersed counts, `V(μ) = μ + μ²/θ` for a fixed
12//!   `θ`.
13//! * [`Gamma`] — positive skewed continuous responses, `V(μ) = μ²`, with an
14//!   **estimated** dispersion.
15//!
16//! All three use the **log link** (see [`family`] for why), and each carries the
17//! same diagnostic surface:
18//!
19//! * [`GlmFit`] — the maximum-likelihood fit via IRLS, with coefficient standard
20//!   errors, Wald statistics, and p-values (normal or Student's *t* depending on
21//!   whether the dispersion is estimated).
22//! * [`deviance_residuals`] / [`pearson_residuals`] — the two GLM residual
23//!   scales; deviance residuals square to the residual deviance, Pearson
24//!   residuals to the χ² that defines the dispersion estimate.
25//! * [`GoodnessOfFit`] via [`GlmFit::goodness_of_fit`] — null/residual deviance,
26//!   dispersion, McFadden's pseudo-R², and AIC/BIC.
27//! * [`leverage`] and [`cooks_distance`] — the weighted-hat-matrix influence
28//!   measures.
29//!
30//! # Response conventions
31//!
32//! As with OLS and logistic, the **caller owns the design matrix**, intercept
33//! column included. The response must lie in the family's support: non-negative
34//! counts for [`Poisson`] and [`NegativeBinomial`], strictly positive reals for
35//! [`Gamma`]. Out-of-support values are rejected with
36//! [`RegressionError::InvalidResponse`](crate::RegressionError::InvalidResponse).
37//!
38//! # Relationship to the `logistic` module
39//!
40//! Binary logistic regression is itself a GLM (binomial family, logit link) and
41//! could be expressed here, but it keeps its own module: its separation
42//! diagnostics and the Hosmer–Lemeshow test are specific to the binary case, and
43//! rewriting a shipped, tested API in terms of this trait would buy nothing. This
44//! module covers the families logistic does *not*.
45//!
46//! # What is still out of scope
47//!
48//! **Multinomial and ordinal** logistic have a vector-valued linear predictor
49//! and block covariance — a different solver shape, not another [`Family`] — and
50//! live in [`categorical`](crate::categorical), not here. The negative-binomial
51//! `θ` can be supplied fixed to [`NegativeBinomial`] or estimated jointly with
52//! [`fit_negative_binomial`] (a profile-likelihood search over `θ`).
53//!
54//! # Quick start
55//!
56//! ```
57//! use ndarray::array;
58//! use regression_diagnostics::glm::{GlmFit, Poisson, deviance_residuals};
59//!
60//! // Caller supplies the intercept column (first column of ones).
61//! let x = array![
62//!     [1.0, 0.0],
63//!     [1.0, 1.0],
64//!     [1.0, 2.0],
65//!     [1.0, 3.0],
66//!     [1.0, 4.0],
67//!     [1.0, 5.0],
68//! ];
69//! let y = array![1.0, 2.0, 3.0, 5.0, 8.0, 13.0]; // roughly exponential growth
70//! let fit = GlmFit::new(Poisson, x, y).unwrap();
71//!
72//! let gof = fit.goodness_of_fit();
73//! assert!(gof.residual_deviance <= gof.null_deviance + 1e-9);
74//! let dr = deviance_residuals(&fit);
75//! let sum_sq: f64 = dr.iter().map(|d| d * d).sum();
76//! assert!((sum_sq - gof.residual_deviance).abs() < 1e-8);
77//! ```
78
79pub mod family;
80
81mod fit;
82mod goodness;
83mod influence;
84mod negbin_theta;
85mod residuals;
86
87pub use family::{Family, Gamma, NegativeBinomial, Poisson};
88pub use fit::GlmFit;
89pub use negbin_theta::fit_negative_binomial;
90pub use goodness::GoodnessOfFit;
91pub use influence::{cooks_distance, leverage};
92pub use residuals::{deviance_residuals, pearson_residuals};