use malevich::stat::{BoxStats, jitter, kde};
use malevich::{Area, Frame, Plot, Points, Range, Scale};
include!("support/svg_card.rs");
fn main() {
let names = ["Adelie", "Chinstrap", "Gentoo"];
let mut groups = [Vec::new(), Vec::new(), Vec::new()];
for line in include_str!("data/penguins.csv").lines().skip(1) {
let mut parts = line.split(',');
let species = parts.next().unwrap_or_default();
let flipper: Option<f64> = parts.nth(2).and_then(|v| v.parse().ok());
if let (Some(index), Some(flipper)) = (names.iter().position(|n| *n == species), flipper) {
groups[index].push(flipper);
}
}
let mut plot = Plot::new()
.x_scale(Scale::bands(names))
.title("flipper length by species: cloud, box, and rain")
.y_label("mm");
let mut box_low = Vec::new();
let mut box_high = Vec::new();
let mut box_q1 = Vec::new();
let mut box_q3 = Vec::new();
let mut box_median = Vec::new();
for (index, group) in groups.iter().enumerate() {
let center = index as f64;
let (positions, density) = kde(group, 128).expect("finite sample");
let peak = density.iter().copied().fold(f64::MIN_POSITIVE, f64::max);
let inner = vec![center + 0.05; positions.len()];
let outer: Vec<f64> = density
.iter()
.map(|d| center + 0.05 + d / peak * 0.35)
.collect();
plot = plot.layer(Area::horizontal(positions, inner, outer));
let strip = jitter(&vec![center - 0.28; group.len()], 0.2);
plot = plot.layer(Points::xy(strip, group.clone()));
let stats = BoxStats::of(group).expect("finite sample");
box_low.push(stats.whisker_low);
box_high.push(stats.whisker_high);
box_q1.push(stats.q1);
box_q3.push(stats.q3);
box_median.push(stats.median);
}
let box_x: Vec<f64> = (0..names.len()).map(|i| i as f64 - 0.08).collect();
plot = plot.layer(
Range::xy(box_x, box_low, box_high)
.body(box_q1, box_q3)
.marker(box_median),
);
let frame = Frame::plain(66, 22);
if svg_card(&plot, &frame) {
return;
}
println!("{}", plot.render_best(&frame));
}