semicontinuous_models 0.1.0

Reusable models for semi-continuous outcomes (two-part, Tweedie, and log-normal)
Documentation
use faer::Mat;
use semicontinuous_models::{
    CrossValidationOptions, ModelInput, cross_validate_models_input, recommend_from_cv,
    select_models_input,
};

fn main() {
    let n = 100;
    let design_matrix = Mat::from_fn(n, 2, |i, j| if j == 0 { 1.0 } else { idx_to_f64(i) / 20.0 });
    let outcome = Mat::from_fn(n, 1, |i, _| {
        if i % 5 == 0 {
            0.0
        } else {
            0.15f64.mul_add(idx_to_f64(i), 1.5)
        }
    });

    let input = ModelInput::new(design_matrix, outcome);
    let result = select_models_input(&input, &[1.2, 1.5, 1.8]);
    println!("Park slope: {:.3}", result.park_test.slope);
    println!("Tweedie candidates: {}", result.tweedie_candidates.len());
    for candidate in &result.tweedie_candidates {
        println!(
            "Tweedie p={:.2}: rmse {:.4}, mae {:.4}, rmsle {:.4}, r2 {:.4}, dev {:.4}",
            candidate.power,
            candidate.metrics.rmse,
            candidate.metrics.mae,
            candidate.metrics.rmsle,
            candidate.metrics.r2,
            candidate.metrics.deviance
        );
    }
    if let Some(lognormal) = result.lognormal_candidate {
        println!(
            "Lognormal: rmse {:.4}, mae {:.4}, rmsle {:.4}, r2 {:.4}, dev {:.4}",
            lognormal.metrics.rmse,
            lognormal.metrics.mae,
            lognormal.metrics.rmsle,
            lognormal.metrics.r2,
            lognormal.metrics.deviance
        );
    }

    if let Some(recommended) = result.recommended_by_aic {
        println!("Recommended by AIC: {recommended}");
    }
    if let Some(recommended) = result.recommended_by_bic {
        println!("Recommended by BIC: {recommended}");
    }

    let cv =
        cross_validate_models_input(&input, &[1.2, 1.5, 1.8], CrossValidationOptions::default())
            .expect("cv");
    if let Some(recommended) = recommend_from_cv(&cv) {
        println!("Recommended by CV RMSE: {recommended}");
    }
}

fn idx_to_f64(idx: usize) -> f64 {
    f64::from(u32::try_from(idx).unwrap_or(u32::MAX))
}