plotters-statistical 0.1.0

Statistical chart primitives (box, violin, ROC, PR, regularization-path, residual) as native plotters series
Documentation
//! Reliability diagram rendered to `calibration_curve.svg`, comparing a
//! well-calibrated model against an over-confident one.

use plotters::prelude::*;
use plotters_statistical::style::palette_color;
use plotters_statistical::CalibrationCurve;

fn noise(i: usize) -> f64 {
    ((i as f64 * 12.9898).sin() * 43758.5453).rem_euclid(1.0)
}

/// Generate (score, label) where the true positive probability is `f(score)`.
fn dataset(overconfident: bool) -> (Vec<f64>, Vec<bool>) {
    let n = 500;
    let mut scores = Vec::with_capacity(n);
    let mut labels = Vec::with_capacity(n);
    for i in 0..n {
        let s = (i as f64 + 0.5) / n as f64;
        // Over-confident model: true prob is milder (sqrt) than the score.
        let true_p = if overconfident { s.sqrt() } else { s };
        scores.push(s);
        labels.push(noise(i) < true_p);
    }
    (scores, labels)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let root = SVGBackend::new("calibration_curve.svg", (600, 600)).into_drawing_area();
    root.fill(&WHITE)?;

    let mut chart = ChartBuilder::on(&root)
        .caption("Calibration (reliability) curve", ("sans-serif", 22))
        .margin(20)
        .set_label_area_size(LabelAreaPosition::Left, 50)
        .set_label_area_size(LabelAreaPosition::Bottom, 45)
        .build_cartesian_2d(0f64..1f64, 0f64..1f64)?;
    chart
        .configure_mesh()
        .x_desc("mean predicted probability")
        .y_desc("observed frequency")
        .draw()?;

    for (i, (name, over)) in [("well calibrated", false), ("over-confident", true)]
        .into_iter()
        .enumerate()
    {
        let (scores, labels) = dataset(over);
        chart
            .draw_series(std::iter::once(
                CalibrationCurve::from_scores(&scores, &labels, 10)?
                    .color(palette_color(i))
                    .diagonal(i == 0),
            ))?
            .label(name)
            .legend(move |(x, y)| PathElement::new(vec![(x, y), (x + 18, y)], palette_color(i)));
    }

    chart
        .configure_series_labels()
        .position(SeriesLabelPosition::UpperLeft)
        .border_style(BLACK)
        .background_style(WHITE.mix(0.85))
        .draw()?;
    root.present()?;
    println!("wrote calibration_curve.svg");
    Ok(())
}