1use plotters::prelude::*;
2
3use rand::SeedableRng;
4use rand_distr::{Distribution, Normal};
5use rand_xorshift::XorShiftRng;
6
7use itertools::Itertools;
8
9use num_traits::sign::Signed;
10
11const OUT_FILE_NAME: &'static str = "plotters-doc-data/errorbar.png";
12fn main() -> Result<(), Box<dyn std::error::Error>> {
13 let data = generate_random_data();
14 let down_sampled = down_sample(&data[..]);
15
16 let root = BitMapBackend::new(OUT_FILE_NAME, (1024, 768)).into_drawing_area();
17
18 root.fill(&WHITE)?;
19
20 let mut chart = ChartBuilder::on(&root)
21 .caption("Linear Function with Noise", ("sans-serif", 60))
22 .margin(10)
23 .set_label_area_size(LabelAreaPosition::Left, 40)
24 .set_label_area_size(LabelAreaPosition::Bottom, 40)
25 .build_cartesian_2d(-10f64..10f64, -10f64..10f64)?;
26
27 chart.configure_mesh().draw()?;
28
29 chart
30 .draw_series(LineSeries::new(data, &GREEN.mix(0.3)))?
31 .label("Raw Data")
32 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &GREEN));
33
34 chart.draw_series(LineSeries::new(
35 down_sampled.iter().map(|(x, _, y, _)| (*x, *y)),
36 &BLUE,
37 ))?;
38
39 chart
40 .draw_series(
41 down_sampled.iter().map(|(x, yl, ym, yh)| {
42 ErrorBar::new_vertical(*x, *yl, *ym, *yh, BLUE.filled(), 20)
43 }),
44 )?
45 .label("Down-sampled")
46 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLUE));
47
48 chart
49 .configure_series_labels()
50 .background_style(WHITE.filled())
51 .draw()?;
52
53 // To avoid the IO failure being ignored silently, we manually call the present function
54 root.present().expect("Unable to write result to file, please make sure 'plotters-doc-data' dir exists under current dir");
55 println!("Result has been saved to {}", OUT_FILE_NAME);
56
57 Ok(())
58}
59
60fn generate_random_data() -> Vec<(f64, f64)> {
61 let norm_dist = Normal::new(0.0, 1.0).unwrap();
62 let mut x_rand = XorShiftRng::from_seed(*b"MyFragileSeed123");
63 let x_iter = norm_dist.sample_iter(&mut x_rand);
64 x_iter
65 .take(20000)
66 .filter(|x| x.abs() <= 4.0)
67 .zip(-10000..10000)
68 .map(|(yn, x)| {
69 (
70 x as f64 / 1000.0,
71 x as f64 / 1000.0 + yn * x as f64 / 10000.0,
72 )
73 })
74 .collect()
75}
76
77fn down_sample(data: &[(f64, f64)]) -> Vec<(f64, f64, f64, f64)> {
78 let down_sampled: Vec<_> = data
79 .iter()
80 .group_by(|x| (x.0 * 1.0).round() / 1.0)
81 .into_iter()
82 .map(|(x, g)| {
83 let mut g: Vec<_> = g.map(|(_, y)| *y).collect();
84 g.sort_by(|a, b| a.partial_cmp(b).unwrap());
85 (
86 x,
87 g[0],
88 g.iter().sum::<f64>() / g.len() as f64,
89 g[g.len() - 1],
90 )
91 })
92 .collect();
93 down_sampled
94}
95#[test]
96fn entry_point() {
97 main().unwrap()
98}
99