#![allow(clippy::disallowed_methods)]
use aprender::glm::{Family, GLM};
use aprender::primitives::{Matrix, Vector};
fn main() {
println!("=== Negative Binomial GLM for Overdispersed Count Data ===\n");
let days = Matrix::from_vec(6, 1, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).expect("Valid matrix");
let counts = Vector::from_vec(vec![5.0, 6.0, 7.0, 8.0, 9.0, 10.0]);
let mean = counts.as_slice().iter().sum::<f32>() / counts.len() as f32;
let variance = counts
.as_slice()
.iter()
.map(|x| (x - mean).powi(2))
.sum::<f32>()
/ (counts.len() - 1) as f32;
println!("Sample Statistics:");
println!(" Mean: {mean:.2}");
println!(" Variance: {variance:.2}");
println!(" Variance/Mean Ratio: {:.2}", variance / mean);
println!(
" Overdispersion? {}",
if variance > mean * 1.5 { "YES" } else { "NO" }
);
println!();
println!("Fitting Negative Binomial GLM (α = 0.1)...");
let mut nb_model = GLM::new(Family::NegativeBinomial)
.with_dispersion(0.1)
.with_max_iter(5000);
match nb_model.fit(&days, &counts) {
Ok(()) => {
println!(" ✓ Model converged successfully!");
println!(
" Intercept: {:.4}",
nb_model.intercept().expect("Model fitted")
);
println!(
" Coefficient: {:.4}",
nb_model.coefficients().expect("Model fitted")[0]
);
println!();
println!("Predictions for each day:");
let predictions = nb_model.predict(&days).expect("Predictions succeed");
for (i, (&actual, &pred)) in counts
.as_slice()
.iter()
.zip(predictions.as_slice())
.enumerate()
{
println!(
" Day {}: Actual = {:.0}, Predicted = {:.2}",
i + 1,
actual,
pred
);
}
println!();
}
Err(e) => {
println!(" ✗ Model failed to converge: {e}");
println!();
}
}
println!("=== Effect of Dispersion Parameter α ===\n");
for alpha in [0.05, 0.1, 0.2, 0.5] {
let mut model = GLM::new(Family::NegativeBinomial)
.with_dispersion(alpha)
.with_max_iter(5000);
match model.fit(&days, &counts) {
Ok(()) => {
println!("α = {alpha:.1}:");
println!(
" Intercept: {:.4}, Coefficient: {:.4}",
model.intercept().expect("Model fitted"),
model.coefficients().expect("Model fitted")[0]
);
let mean_pred = 7.5; let variance_func = mean_pred + alpha * mean_pred * mean_pred;
println!(" Variance function V(μ) = μ + α*μ² ≈ {variance_func:.2}");
}
Err(_) => {
println!("α = {alpha:.1}: Failed to converge");
}
}
}
println!();
println!("=== Why Negative Binomial? ===");
println!();
println!("Poisson Assumption:");
println!(" - Assumes variance = mean (V(μ) = μ)");
println!(" - Fails when data is overdispersed (variance >> mean)");
println!(" - Can lead to underestimated uncertainty");
println!();
println!("Negative Binomial Solution:");
println!(" - Allows variance > mean (V(μ) = μ + α*μ²)");
println!(" - Dispersion parameter α controls extra variance");
println!(" - Gamma-Poisson mixture model interpretation");
println!(" - Provides accurate credible intervals");
println!();
println!("References:");
println!(" - Cameron & Trivedi (2013): Regression Analysis of Count Data");
println!(" - Hilbe (2011): Negative Binomial Regression");
println!(" - See notes-poisson.md for detailed explanation");
}