use plotters::coord::Shift;
use plotters::prelude::*;
use plotters_statistical::{
figures::CorrelationHeatmap, figures::MissingnessHeatmap, figures::PairPlot, CalibrationCurve,
Ecdf, GainChart, Heatmap, QqPlot,
};
fn render<F>(f: F) -> String
where
F: FnOnce(&DrawingArea<SVGBackend, Shift>) -> Result<(), Box<dyn std::error::Error>>,
{
let mut buf = String::new();
{
let root = SVGBackend::with_string(&mut buf, (500, 400)).into_drawing_area();
root.fill(&WHITE).unwrap();
f(&root).unwrap();
root.present().unwrap();
}
buf
}
fn has_shape(svg: &str) -> bool {
[
"<rect",
"<circle",
"<polygon",
"<polyline",
"<path",
"<line",
"<text",
]
.iter()
.any(|t| svg.contains(t))
}
fn assert_ok(svg: &str) {
assert!(svg.contains("<svg") && svg.contains("</svg>"));
assert!(has_shape(svg), "no drawn content");
}
#[test]
fn renders_ecdf_with_band() {
let svg = render(|root| {
let mut chart = ChartBuilder::on(root).build_cartesian_2d(0f64..10f64, 0f64..1f64)?;
let data: Vec<f64> = (0..40)
.map(|i| (i as f64 * 0.25).sin() * 3.0 + 5.0)
.collect();
chart.draw_series(std::iter::once(
Ecdf::from_data(&data)?.confidence_band(0.05).markers(true),
))?;
Ok(())
});
assert_ok(&svg);
}
#[test]
fn renders_qq_plot() {
let svg = render(|root| {
let mut chart = ChartBuilder::on(root).build_cartesian_2d(-3f64..3f64, -3f64..3f64)?;
let data: Vec<f64> = (0..60)
.map(|i| ((i as f64 + 0.5) / 60.0 - 0.5) * 4.0)
.collect();
chart.draw_series(std::iter::once(QqPlot::from_data(&data)?))?;
Ok(())
});
assert_ok(&svg);
}
#[test]
fn renders_calibration_curve() {
let svg = render(|root| {
let mut chart = ChartBuilder::on(root).build_cartesian_2d(0f64..1f64, 0f64..1f64)?;
let scores: Vec<f64> = (0..100).map(|i| i as f64 / 100.0).collect();
let labels: Vec<bool> = scores.iter().map(|&s| s > 0.5).collect();
chart.draw_series(std::iter::once(CalibrationCurve::from_scores(
&scores, &labels, 10,
)?))?;
Ok(())
});
assert_ok(&svg);
}
#[test]
fn renders_gain_chart() {
use plotters_statistical::series::gain_chart::GainMode;
let svg = render(|root| {
let mut chart = ChartBuilder::on(root).build_cartesian_2d(0f64..1f64, 0f64..3f64)?;
let scores: Vec<f64> = (0..100)
.map(|i| (i % 7) as f64 + i as f64 / 100.0)
.collect();
let labels: Vec<bool> = (0..100).map(|i| i % 3 == 0).collect();
chart.draw_series(std::iter::once(
GainChart::from_scores(&scores, &labels)?.mode(GainMode::Lift),
))?;
Ok(())
});
assert_ok(&svg);
}
#[test]
fn renders_heatmap_series_annotated() {
use plotters_statistical::series::heatmap::HeatmapAnnotation;
let svg = render(|root| {
let values = vec![vec![0.1, 0.5, 0.9], vec![0.4, 0.2, 0.7]];
let mut chart =
ChartBuilder::on(root).build_cartesian_2d(-0.5f64..2.5f64, -0.5f64..1.5f64)?;
chart.draw_series(std::iter::once(
Heatmap::new(&values).annotate(HeatmapAnnotation::default()),
))?;
Ok(())
});
assert_ok(&svg);
}
#[test]
fn renders_correlation_heatmap_figure() {
let svg = render(|root| {
let cols = vec![
vec![1.0, 2.0, 3.0, 4.0, 5.0],
vec![2.0, 4.0, 5.0, 4.0, 5.0],
vec![5.0, 4.0, 3.0, 2.0, 1.0],
];
let labels = vec!["a".to_string(), "b".to_string(), "c".to_string()];
use plotters_statistical::stats::CorrelationMethod;
CorrelationHeatmap::from_columns(&cols, labels, CorrelationMethod::Pearson)?
.title("corr")
.draw(root)?;
Ok(())
});
assert_ok(&svg);
}
#[test]
fn renders_missingness_heatmap_figure() {
let svg = render(|root| {
let cols = vec![
vec![Some(1.0), None, Some(3.0), Some(4.0)],
vec![Some(1.0), Some(2.0), None, None],
];
let labels = vec!["x".to_string(), "y".to_string()];
MissingnessHeatmap::from_columns(&cols, labels)?.draw(root)?;
Ok(())
});
assert_ok(&svg);
}
#[test]
fn renders_pair_plot_figure() {
let svg = render(|root| {
let cols = vec![
(0..40).map(|i| i as f64).collect::<Vec<_>>(),
(0..40).map(|i| (i as f64).sin()).collect::<Vec<_>>(),
];
let labels = vec!["t".to_string(), "sin".to_string()];
PairPlot::new(cols, labels).draw(root)?;
Ok(())
});
assert_ok(&svg);
}