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)
}
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;
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(())
}