Skip to main content

GGPlot

Struct GGPlot 

Source
pub struct GGPlot { /* private fields */ }
Expand description

The top-level plot specification — builder pattern.

Implementations§

Source§

impl GGPlot

Source

pub fn new(data: impl GGData) -> Self

Create a new plot with the given data source.

Examples found in repository?
examples/bar_chart.rs (line 10)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "fruit" => ["Apple", "Apple", "Apple", "Banana", "Banana",
7                     "Cherry", "Cherry", "Cherry", "Cherry", "Date"],
8    }?;
9
10    GGPlot::new(df)
11        .aes(Aes::new().x("fruit"))
12        .geom_bar()
13        .title("Fruit Counts")
14        .xlab("Fruit")
15        .ylab("Count")
16        .save("bar_chart.svg")?;
17
18    println!("Saved bar_chart.svg");
19    Ok(())
20}
More examples
Hide additional examples
examples/coord_flip.rs (line 11)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
examples/gallery.rs (line 46)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
examples/annotations.rs (line 16)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
examples/density.rs (line 26)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate two overlapping distributions using deterministic pseudo-random values
6    let value: Vec<f64> = (0..200_u64)
7        .map(|i| {
8            let r = ((i.wrapping_mul(1103515245).wrapping_add(12345)) % (1 << 16)) as f64
9                / (1u64 << 16) as f64;
10            if i < 100 {
11                3.0 + r * 4.0
12            } else {
13                5.0 + r * 4.0
14            }
15        })
16        .collect();
17    let group: Vec<&str> = (0..200)
18        .map(|i| if i < 100 { "Group A" } else { "Group B" })
19        .collect();
20
21    let df = df! {
22        "value" => value,
23        "group" => group,
24    }?;
25
26    GGPlot::new(df)
27        .aes(Aes::new().x("value").color("group"))
28        .geom_density()
29        .title("Density Plot by Group")
30        .xlab("Value")
31        .ylab("Density")
32        .save("density.svg")?;
33
34    println!("Saved density.svg");
35    Ok(())
36}
examples/scatter.rs (line 23)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let sepal_length: Vec<f64> = (0..50).map(|i| 4.5 + i as f64 * 0.05).collect();
6    let sepal_width: Vec<f64> = (0..50)
7        .map(|i| 2.0 + (i as f64 * 0.3).sin() + i as f64 * 0.02)
8        .collect();
9    let species: Vec<&str> = (0..50)
10        .map(|i| match i % 3 {
11            0 => "setosa",
12            1 => "versicolor",
13            _ => "virginica",
14        })
15        .collect();
16
17    let df = df! {
18        "sepal_length" => sepal_length,
19        "sepal_width" => sepal_width,
20        "species" => species,
21    }?;
22
23    GGPlot::new(df)
24        .aes(
25            Aes::new()
26                .x("sepal_length")
27                .y("sepal_width")
28                .color("species"),
29        )
30        .geom_point()
31        .title("Iris Scatter Plot")
32        .xlab("Sepal Length")
33        .ylab("Sepal Width")
34        .save("scatter.svg")?;
35
36    println!("Saved scatter.svg");
37    Ok(())
38}
Source

pub fn aes(self, mapping: Aes) -> Self

Set the plot-level aesthetic mapping.

Examples found in repository?
examples/bar_chart.rs (line 11)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "fruit" => ["Apple", "Apple", "Apple", "Banana", "Banana",
7                     "Cherry", "Cherry", "Cherry", "Cherry", "Date"],
8    }?;
9
10    GGPlot::new(df)
11        .aes(Aes::new().x("fruit"))
12        .geom_bar()
13        .title("Fruit Counts")
14        .xlab("Fruit")
15        .ylab("Count")
16        .save("bar_chart.svg")?;
17
18    println!("Saved bar_chart.svg");
19    Ok(())
20}
More examples
Hide additional examples
examples/coord_flip.rs (line 12)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
examples/gallery.rs (line 47)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
examples/annotations.rs (line 17)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
examples/density.rs (line 27)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate two overlapping distributions using deterministic pseudo-random values
6    let value: Vec<f64> = (0..200_u64)
7        .map(|i| {
8            let r = ((i.wrapping_mul(1103515245).wrapping_add(12345)) % (1 << 16)) as f64
9                / (1u64 << 16) as f64;
10            if i < 100 {
11                3.0 + r * 4.0
12            } else {
13                5.0 + r * 4.0
14            }
15        })
16        .collect();
17    let group: Vec<&str> = (0..200)
18        .map(|i| if i < 100 { "Group A" } else { "Group B" })
19        .collect();
20
21    let df = df! {
22        "value" => value,
23        "group" => group,
24    }?;
25
26    GGPlot::new(df)
27        .aes(Aes::new().x("value").color("group"))
28        .geom_density()
29        .title("Density Plot by Group")
30        .xlab("Value")
31        .ylab("Density")
32        .save("density.svg")?;
33
34    println!("Saved density.svg");
35    Ok(())
36}
examples/scatter.rs (lines 24-29)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let sepal_length: Vec<f64> = (0..50).map(|i| 4.5 + i as f64 * 0.05).collect();
6    let sepal_width: Vec<f64> = (0..50)
7        .map(|i| 2.0 + (i as f64 * 0.3).sin() + i as f64 * 0.02)
8        .collect();
9    let species: Vec<&str> = (0..50)
10        .map(|i| match i % 3 {
11            0 => "setosa",
12            1 => "versicolor",
13            _ => "virginica",
14        })
15        .collect();
16
17    let df = df! {
18        "sepal_length" => sepal_length,
19        "sepal_width" => sepal_width,
20        "species" => species,
21    }?;
22
23    GGPlot::new(df)
24        .aes(
25            Aes::new()
26                .x("sepal_length")
27                .y("sepal_width")
28                .color("species"),
29        )
30        .geom_point()
31        .title("Iris Scatter Plot")
32        .xlab("Sepal Length")
33        .ylab("Sepal Width")
34        .save("scatter.svg")?;
35
36    println!("Saved scatter.svg");
37    Ok(())
38}
Source

pub fn geom_point(self) -> Self

Examples found in repository?
examples/gallery.rs (line 48)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
More examples
Hide additional examples
examples/annotations.rs (line 19)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
examples/scatter.rs (line 30)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let sepal_length: Vec<f64> = (0..50).map(|i| 4.5 + i as f64 * 0.05).collect();
6    let sepal_width: Vec<f64> = (0..50)
7        .map(|i| 2.0 + (i as f64 * 0.3).sin() + i as f64 * 0.02)
8        .collect();
9    let species: Vec<&str> = (0..50)
10        .map(|i| match i % 3 {
11            0 => "setosa",
12            1 => "versicolor",
13            _ => "virginica",
14        })
15        .collect();
16
17    let df = df! {
18        "sepal_length" => sepal_length,
19        "sepal_width" => sepal_width,
20        "species" => species,
21    }?;
22
23    GGPlot::new(df)
24        .aes(
25            Aes::new()
26                .x("sepal_length")
27                .y("sepal_width")
28                .color("species"),
29        )
30        .geom_point()
31        .title("Iris Scatter Plot")
32        .xlab("Sepal Length")
33        .ylab("Sepal Width")
34        .save("scatter.svg")?;
35
36    println!("Saved scatter.svg");
37    Ok(())
38}
examples/loess_smooth.rs (line 23)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate noisy sine wave data
6    let x: Vec<f64> = (0..80).map(|i| (i as f64) * 0.1).collect();
7    let y: Vec<f64> = (0..80)
8        .map(|i| {
9            let xv = (i as f64) * 0.1;
10            let noise = ((i * 17 + 3) % 11) as f64 / 11.0 - 0.5; // deterministic pseudo-noise
11            (xv * 0.8).sin() * 2.0 + noise * 1.5
12        })
13        .collect();
14
15    let df = df! {
16        "x" => &x,
17        "y" => &y,
18    }?;
19
20    // Linear smooth (default)
21    GGPlot::new(df.clone())
22        .aes(Aes::new().x("x").y("y"))
23        .geom_point()
24        .geom_smooth()
25        .title("Linear Smooth (method = lm)")
26        .save("smooth_lm.svg")?;
27
28    println!("Saved smooth_lm.svg");
29
30    // LOESS smooth
31    GGPlot::new(df)
32        .aes(Aes::new().x("x").y("y"))
33        .geom_point()
34        .geom_smooth_with(GeomSmooth::default().loess(0.3))
35        .title("LOESS Smooth (span = 0.3)")
36        .save("smooth_loess.svg")?;
37
38    println!("Saved smooth_loess.svg");
39    Ok(())
40}
examples/log_scale.rs (line 23)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Exponential growth data (e.g., population, bacteria count)
6    let year: Vec<f64> = (0..30).map(|i| 1990.0 + i as f64).collect();
7    let count: Vec<f64> = (0..30)
8        .map(|i| {
9            let base = 100.0 * (1.15_f64).powi(i); // 15% growth per year
10            let noise = ((i * 37 + 7) % 13) as f64 / 13.0 * 0.2 + 0.9;
11            base * noise
12        })
13        .collect();
14
15    let df = df! {
16        "year" => &year,
17        "count" => &count,
18    }?;
19
20    // Linear scale — exponential curve
21    GGPlot::new(df.clone())
22        .aes(Aes::new().x("year").y("count"))
23        .geom_point()
24        .geom_line()
25        .title("Exponential Growth (Linear Scale)")
26        .xlab("Year")
27        .ylab("Count")
28        .save("log_scale_linear.svg")?;
29
30    println!("Saved log_scale_linear.svg");
31
32    // Log10 y-axis — should appear roughly linear
33    GGPlot::new(df)
34        .aes(Aes::new().x("year").y("count"))
35        .geom_point()
36        .geom_line()
37        .scale_y_log10()
38        .title("Exponential Growth (Log10 Y Scale)")
39        .xlab("Year")
40        .ylab("Count (log10)")
41        .save("log_scale_log10.svg")?;
42
43    println!("Saved log_scale_log10.svg");
44    Ok(())
45}
examples/continuous_color.rs (line 29)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate scatter data with a continuous variable for color
6    let x: Vec<f64> = (0..200)
7        .map(|i| {
8            let t = i as f64 * 0.05;
9            t.cos() * (1.0 + t * 0.3)
10        })
11        .collect();
12    let y: Vec<f64> = (0..200)
13        .map(|i| {
14            let t = i as f64 * 0.05;
15            t.sin() * (1.0 + t * 0.3)
16        })
17        .collect();
18    let z: Vec<f64> = (0..200).map(|i| i as f64 * 0.05).collect();
19
20    let df = df! {
21        "x" => &x,
22        "y" => &y,
23        "z" => &z,
24    }?;
25
26    // Default blue-to-red gradient
27    GGPlot::new(df.clone())
28        .aes(Aes::new().x("x").y("y").color("z"))
29        .geom_point()
30        .title("Continuous Color (default gradient)")
31        .xlab("X")
32        .ylab("Y")
33        .save("continuous_color.svg")?;
34
35    println!("Saved continuous_color.svg");
36
37    // Custom gradient: dark blue to yellow
38    GGPlot::new(df)
39        .aes(Aes::new().x("x").y("y").color("z"))
40        .geom_point()
41        .scale_color_gradient(RGBAColor::new(10, 30, 100), RGBAColor::new(255, 230, 50))
42        .title("Continuous Color (custom gradient)")
43        .xlab("X")
44        .ylab("Y")
45        .save("continuous_color_custom.svg")?;
46
47    println!("Saved continuous_color_custom.svg");
48    Ok(())
49}
Source

pub fn geom_point_with(self, geom: GeomPoint) -> Self

Source

pub fn geom_line(self) -> Self

Examples found in repository?
examples/annotations.rs (line 18)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
More examples
Hide additional examples
examples/log_scale.rs (line 24)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Exponential growth data (e.g., population, bacteria count)
6    let year: Vec<f64> = (0..30).map(|i| 1990.0 + i as f64).collect();
7    let count: Vec<f64> = (0..30)
8        .map(|i| {
9            let base = 100.0 * (1.15_f64).powi(i); // 15% growth per year
10            let noise = ((i * 37 + 7) % 13) as f64 / 13.0 * 0.2 + 0.9;
11            base * noise
12        })
13        .collect();
14
15    let df = df! {
16        "year" => &year,
17        "count" => &count,
18    }?;
19
20    // Linear scale — exponential curve
21    GGPlot::new(df.clone())
22        .aes(Aes::new().x("year").y("count"))
23        .geom_point()
24        .geom_line()
25        .title("Exponential Growth (Linear Scale)")
26        .xlab("Year")
27        .ylab("Count")
28        .save("log_scale_linear.svg")?;
29
30    println!("Saved log_scale_linear.svg");
31
32    // Log10 y-axis — should appear roughly linear
33    GGPlot::new(df)
34        .aes(Aes::new().x("year").y("count"))
35        .geom_point()
36        .geom_line()
37        .scale_y_log10()
38        .title("Exponential Growth (Log10 Y Scale)")
39        .xlab("Year")
40        .ylab("Count (log10)")
41        .save("log_scale_log10.svg")?;
42
43    println!("Saved log_scale_log10.svg");
44    Ok(())
45}
Source

pub fn geom_line_with(self, geom: GeomLine) -> Self

Source

pub fn geom_bar(self) -> Self

Examples found in repository?
examples/bar_chart.rs (line 12)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "fruit" => ["Apple", "Apple", "Apple", "Banana", "Banana",
7                     "Cherry", "Cherry", "Cherry", "Cherry", "Date"],
8    }?;
9
10    GGPlot::new(df)
11        .aes(Aes::new().x("fruit"))
12        .geom_bar()
13        .title("Fruit Counts")
14        .xlab("Fruit")
15        .ylab("Count")
16        .save("bar_chart.svg")?;
17
18    println!("Saved bar_chart.svg");
19    Ok(())
20}
More examples
Hide additional examples
examples/gallery.rs (line 128)
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
Source

pub fn geom_bar_with(self, geom: GeomBar) -> Self

Source

pub fn geom_histogram(self) -> Self

Source

pub fn geom_histogram_with(self, geom: GeomHistogram) -> Self

Examples found in repository?
examples/gallery.rs (lines 99-102)
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
More examples
Hide additional examples
examples/histogram.rs (lines 26-29)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate approximate normal distribution using central limit theorem
6    let values: Vec<f64> = (0..1000)
7        .map(|i: i32| {
8            // Sum of pseudo-random uniform values → approx normal by CLT
9            let r1 = ((i * 1237 + 5678) % 1000) as f64 / 1000.0;
10            let r2 = ((i * 8731 + 4321) % 1000) as f64 / 1000.0;
11            let r3 = ((i * 4567 + 8901) % 1000) as f64 / 1000.0;
12            let r4 = ((i * 6543 + 2109) % 1000) as f64 / 1000.0;
13            let r5 = ((i * 3571 + 7654) % 1000) as f64 / 1000.0;
14            let r6 = ((i * 9137 + 3456) % 1000) as f64 / 1000.0;
15            // Sum of 6 uniforms: mean=3, std≈0.745; normalize to mean=0, std≈1.5
16            (r1 + r2 + r3 + r4 + r5 + r6 - 3.0) * 2.0
17        })
18        .collect();
19
20    let df = df! {
21        "measurement" => values,
22    }?;
23
24    GGPlot::new(df)
25        .aes(Aes::new().x("measurement"))
26        .geom_histogram_with(GeomHistogram {
27            bins: 25,
28            ..Default::default()
29        })
30        .title("Distribution of Measurements")
31        .xlab("Value")
32        .ylab("Frequency")
33        .save("histogram.svg")?;
34
35    println!("Saved histogram.svg");
36    Ok(())
37}
Source

pub fn geom_boxplot(self) -> Self

Source

pub fn geom_boxplot_with(self, geom: GeomBoxplot) -> Self

Examples found in repository?
examples/gallery.rs (lines 152-155)
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
Source

pub fn geom_smooth(self) -> Self

Examples found in repository?
examples/loess_smooth.rs (line 24)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate noisy sine wave data
6    let x: Vec<f64> = (0..80).map(|i| (i as f64) * 0.1).collect();
7    let y: Vec<f64> = (0..80)
8        .map(|i| {
9            let xv = (i as f64) * 0.1;
10            let noise = ((i * 17 + 3) % 11) as f64 / 11.0 - 0.5; // deterministic pseudo-noise
11            (xv * 0.8).sin() * 2.0 + noise * 1.5
12        })
13        .collect();
14
15    let df = df! {
16        "x" => &x,
17        "y" => &y,
18    }?;
19
20    // Linear smooth (default)
21    GGPlot::new(df.clone())
22        .aes(Aes::new().x("x").y("y"))
23        .geom_point()
24        .geom_smooth()
25        .title("Linear Smooth (method = lm)")
26        .save("smooth_lm.svg")?;
27
28    println!("Saved smooth_lm.svg");
29
30    // LOESS smooth
31    GGPlot::new(df)
32        .aes(Aes::new().x("x").y("y"))
33        .geom_point()
34        .geom_smooth_with(GeomSmooth::default().loess(0.3))
35        .title("LOESS Smooth (span = 0.3)")
36        .save("smooth_loess.svg")?;
37
38    println!("Saved smooth_loess.svg");
39    Ok(())
40}
Source

pub fn geom_smooth_with(self, geom: GeomSmooth) -> Self

Examples found in repository?
examples/gallery.rs (lines 73-76)
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
More examples
Hide additional examples
examples/loess_smooth.rs (line 34)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate noisy sine wave data
6    let x: Vec<f64> = (0..80).map(|i| (i as f64) * 0.1).collect();
7    let y: Vec<f64> = (0..80)
8        .map(|i| {
9            let xv = (i as f64) * 0.1;
10            let noise = ((i * 17 + 3) % 11) as f64 / 11.0 - 0.5; // deterministic pseudo-noise
11            (xv * 0.8).sin() * 2.0 + noise * 1.5
12        })
13        .collect();
14
15    let df = df! {
16        "x" => &x,
17        "y" => &y,
18    }?;
19
20    // Linear smooth (default)
21    GGPlot::new(df.clone())
22        .aes(Aes::new().x("x").y("y"))
23        .geom_point()
24        .geom_smooth()
25        .title("Linear Smooth (method = lm)")
26        .save("smooth_lm.svg")?;
27
28    println!("Saved smooth_lm.svg");
29
30    // LOESS smooth
31    GGPlot::new(df)
32        .aes(Aes::new().x("x").y("y"))
33        .geom_point()
34        .geom_smooth_with(GeomSmooth::default().loess(0.3))
35        .title("LOESS Smooth (span = 0.3)")
36        .save("smooth_loess.svg")?;
37
38    println!("Saved smooth_loess.svg");
39    Ok(())
40}
Source

pub fn geom_col(self) -> Self

Examples found in repository?
examples/coord_flip.rs (line 13)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
Source

pub fn geom_col_with(self, geom: GeomCol) -> Self

Source

pub fn geom_hline(self, yintercept: f64) -> Self

Source

pub fn geom_hline_with(self, geom: GeomHline) -> Self

Add a horizontal reference line with custom styling (color/linetype/width).

Source

pub fn geom_vline(self, xintercept: f64) -> Self

Source

pub fn geom_vline_with(self, geom: GeomVline) -> Self

Add a vertical reference line with custom styling (color/linetype/width).

Examples found in repository?
examples/supplier_leadtime.rs (lines 169-175)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
204
205/// ECDF for one supplier — P(lead <= contract) reads straight off the curve.
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
244
245/// Compare suppliers: overlaid per-supplier densities against the contract line.
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
Source

pub fn geom_abline(self, slope: f64, intercept: f64) -> Self

Source

pub fn geom_abline_with(self, geom: GeomAbline) -> Self

Add a slope/intercept reference line with custom styling.

Source

pub fn geom_text(self) -> Self

Source

pub fn geom_text_with(self, geom: GeomText) -> Self

Source

pub fn geom_label(self) -> Self

Source

pub fn geom_label_with(self, geom: GeomLabel) -> Self

Source

pub fn geom_area(self) -> Self

Source

pub fn geom_area_with(self, geom: GeomArea) -> Self

Source

pub fn geom_ribbon(self) -> Self

Source

pub fn geom_ribbon_with(self, geom: GeomRibbon) -> Self

Source

pub fn geom_errorbar(self) -> Self

Source

pub fn geom_errorbar_with(self, geom: GeomErrorbar) -> Self

Source

pub fn geom_segment(self) -> Self

Source

pub fn geom_segment_with(self, geom: GeomSegment) -> Self

Source

pub fn geom_density(self) -> Self

Examples found in repository?
examples/gallery.rs (line 258)
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
More examples
Hide additional examples
examples/density.rs (line 28)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate two overlapping distributions using deterministic pseudo-random values
6    let value: Vec<f64> = (0..200_u64)
7        .map(|i| {
8            let r = ((i.wrapping_mul(1103515245).wrapping_add(12345)) % (1 << 16)) as f64
9                / (1u64 << 16) as f64;
10            if i < 100 {
11                3.0 + r * 4.0
12            } else {
13                5.0 + r * 4.0
14            }
15        })
16        .collect();
17    let group: Vec<&str> = (0..200)
18        .map(|i| if i < 100 { "Group A" } else { "Group B" })
19        .collect();
20
21    let df = df! {
22        "value" => value,
23        "group" => group,
24    }?;
25
26    GGPlot::new(df)
27        .aes(Aes::new().x("value").color("group"))
28        .geom_density()
29        .title("Density Plot by Group")
30        .xlab("Value")
31        .ylab("Density")
32        .save("density.svg")?;
33
34    println!("Saved density.svg");
35    Ok(())
36}
Source

pub fn geom_density_with(self, geom: GeomDensity) -> Self

Examples found in repository?
examples/supplier_leadtime.rs (lines 162-167)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
204
205/// ECDF for one supplier — P(lead <= contract) reads straight off the curve.
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
244
245/// Compare suppliers: overlaid per-supplier densities against the contract line.
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
Source

pub fn geom_rug(self) -> Self

Source

pub fn geom_rug_with(self, geom: GeomRug) -> Self

Examples found in repository?
examples/supplier_leadtime.rs (lines 185-190)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
Source

pub fn geom_jitter(self) -> Self

Source

pub fn geom_jitter_with(self, geom: GeomJitter) -> Self

Source

pub fn geom_path(self) -> Self

Source

pub fn geom_path_with(self, geom: GeomPath) -> Self

Source

pub fn stat_ellipse(self) -> Self

Add a confidence-ellipse layer (default 95%) as a path per group.

Source

pub fn stat_ellipse_level(self, level: f64) -> Self

Add a confidence-ellipse layer at the given level (0, 1).

Source

pub fn geom_step(self) -> Self

Examples found in repository?
examples/supplier_leadtime.rs (line 222)
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
Source

pub fn geom_step_with(self, geom: GeomStep) -> Self

Source

pub fn geom_freqpoly(self) -> Self

Source

pub fn geom_freqpoly_with(self, geom: GeomFreqpoly) -> Self

Source

pub fn geom_linerange(self) -> Self

Source

pub fn geom_linerange_with(self, geom: GeomLinerange) -> Self

Source

pub fn geom_pointrange(self) -> Self

Source

pub fn geom_pointrange_with(self, geom: GeomPointrange) -> Self

Source

pub fn geom_crossbar(self) -> Self

Source

pub fn geom_crossbar_with(self, geom: GeomCrossbar) -> Self

Source

pub fn geom_spoke(self) -> Self

Source

pub fn geom_spoke_with(self, geom: GeomSpoke) -> Self

Source

pub fn geom_rect(self) -> Self

Source

pub fn geom_rect_with(self, geom: GeomRect) -> Self

Source

pub fn geom_tile(self) -> Self

Source

pub fn geom_tile_with(self, geom: GeomTile) -> Self

Source

pub fn geom_raster(self) -> Self

Dense regular grid of filled cells (heatmap/raster) from x, y, fill.

Source

pub fn geom_raster_with(self, geom: GeomRaster) -> Self

Source

pub fn geom_polygon(self) -> Self

Source

pub fn geom_polygon_with(self, geom: GeomPolygon) -> Self

Source

pub fn geom_curve(self) -> Self

Source

pub fn geom_curve_with(self, geom: GeomCurve) -> Self

Source

pub fn geom_violin(self) -> Self

Examples found in repository?
examples/gallery.rs (line 178)
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
Source

pub fn geom_violin_with(self, geom: GeomViolin) -> Self

Source

pub fn geom_dotplot(self) -> Self

Source

pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self

Source

pub fn geom_qq(self) -> Self

Source

pub fn geom_qq_with(self, geom: GeomQQ) -> Self

Source

pub fn geom_qq_line(self) -> Self

Source

pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self

Source

pub fn geom_bin2d(self) -> Self

Source

pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self

Source

pub fn geom_hex(self) -> Self

Source

pub fn geom_hex_with(self, geom: GeomHex) -> Self

Source

pub fn geom_count(self) -> Self

Source

pub fn geom_count_with(self, geom: GeomCount) -> Self

Source

pub fn geom_contour(self) -> Self

Source

pub fn geom_contour_with(self, geom: GeomContour) -> Self

Source

pub fn geom_contour_filled(self) -> Self

Filled contour bands from gridded (x, y, z) data — draws polygons filled by band level. Pair with a continuous fill scale (e.g. scale_fill_viridis_c).

Source

pub fn geom_density2d(self) -> Self

Source

pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self

Source

pub fn geom_blank(self) -> Self

Source

pub fn stat(self, stat: impl Stat + 'static) -> Self

Override the stat for the most recently added layer.

Examples found in repository?
examples/supplier_leadtime.rs (line 223)
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
Source

pub fn position(self, pos: impl Position + 'static) -> Self

Override the position for the most recently added layer.

Source

pub fn layer_data(self, data: impl GGData) -> Self

Override the data for the most recently added layer.

Examples found in repository?
examples/supplier_leadtime.rs (line 191)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
Source

pub fn layer_aes(self, mapping: Aes) -> Self

Override the aesthetic mapping for the most recently added layer.

Examples found in repository?
examples/supplier_leadtime.rs (line 192)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
Source

pub fn show_legend(self, show: bool) -> Self

Control whether the most recently added layer contributes to the legend. true = always show, false = always hide, default (None) = auto.

Source

pub fn scale_x_continuous(self, s: ScaleContinuous) -> Self

Source

pub fn scale_y_continuous(self, s: ScaleContinuous) -> Self

Source

pub fn scale_x_discrete(self, s: ScaleDiscrete) -> Self

Source

pub fn scale_y_discrete(self, s: ScaleDiscrete) -> Self

Source

pub fn scale_color(self, s: impl Scale + 'static) -> Self

Source

pub fn scale_fill(self, s: impl Scale + 'static) -> Self

Source

pub fn scale_color_manual(self, values: Vec<(&str, RGBAColor)>) -> Self

Examples found in repository?
examples/color_palettes.rs (lines 62-68)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate grouped scatter data
6    let groups = ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"];
7    let mut x_vals = Vec::new();
8    let mut y_vals = Vec::new();
9    let mut group_vals = Vec::new();
10
11    for (gi, &group) in groups.iter().enumerate() {
12        for j in 0..20 {
13            let base_x = gi as f64 * 2.0 + 1.0;
14            let base_y = (gi as f64 + 1.0) * 3.0;
15            let r = ((gi * 20 + j) * 7 + 13) % 17;
16            x_vals.push(base_x + (r as f64 / 17.0 - 0.5) * 2.0);
17            y_vals.push(base_y + ((r * 3 + 5) % 11) as f64 / 11.0 * 4.0 - 2.0);
18            group_vals.push(group);
19        }
20    }
21
22    let df = df! {
23        "x" => &x_vals,
24        "y" => &y_vals,
25        "group" => &group_vals,
26    }?;
27
28    // Viridis palette
29    GGPlot::new(df.clone())
30        .aes(Aes::new().x("x").y("y").color("group"))
31        .geom_point()
32        .scale_color_viridis()
33        .title("Viridis Palette")
34        .save("palette_viridis.svg")?;
35
36    println!("Saved palette_viridis.svg");
37
38    // Brewer Set1 palette
39    GGPlot::new(df.clone())
40        .aes(Aes::new().x("x").y("y").color("group"))
41        .geom_point()
42        .scale_color_brewer(PaletteName::Set1)
43        .title("Brewer Set1 Palette")
44        .save("palette_brewer_set1.svg")?;
45
46    println!("Saved palette_brewer_set1.svg");
47
48    // Brewer Dark2 palette
49    GGPlot::new(df.clone())
50        .aes(Aes::new().x("x").y("y").color("group"))
51        .geom_point()
52        .scale_color_brewer(PaletteName::Dark2)
53        .title("Brewer Dark2 Palette")
54        .save("palette_brewer_dark2.svg")?;
55
56    println!("Saved palette_brewer_dark2.svg");
57
58    // Manual colors
59    GGPlot::new(df)
60        .aes(Aes::new().x("x").y("y").color("group"))
61        .geom_point()
62        .scale_color_manual(vec![
63            ("Alpha", RGBAColor::new(255, 0, 0)),
64            ("Beta", RGBAColor::new(0, 180, 0)),
65            ("Gamma", RGBAColor::new(0, 0, 255)),
66            ("Delta", RGBAColor::new(255, 165, 0)),
67            ("Epsilon", RGBAColor::new(128, 0, 128)),
68        ])
69        .title("Manual Color Scale")
70        .save("palette_manual.svg")?;
71
72    println!("Saved palette_manual.svg");
73    Ok(())
74}
Source

pub fn scale_fill_manual(self, values: Vec<(&str, RGBAColor)>) -> Self

Source

pub fn scale_color_viridis(self) -> Self

Examples found in repository?
examples/color_palettes.rs (line 32)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate grouped scatter data
6    let groups = ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"];
7    let mut x_vals = Vec::new();
8    let mut y_vals = Vec::new();
9    let mut group_vals = Vec::new();
10
11    for (gi, &group) in groups.iter().enumerate() {
12        for j in 0..20 {
13            let base_x = gi as f64 * 2.0 + 1.0;
14            let base_y = (gi as f64 + 1.0) * 3.0;
15            let r = ((gi * 20 + j) * 7 + 13) % 17;
16            x_vals.push(base_x + (r as f64 / 17.0 - 0.5) * 2.0);
17            y_vals.push(base_y + ((r * 3 + 5) % 11) as f64 / 11.0 * 4.0 - 2.0);
18            group_vals.push(group);
19        }
20    }
21
22    let df = df! {
23        "x" => &x_vals,
24        "y" => &y_vals,
25        "group" => &group_vals,
26    }?;
27
28    // Viridis palette
29    GGPlot::new(df.clone())
30        .aes(Aes::new().x("x").y("y").color("group"))
31        .geom_point()
32        .scale_color_viridis()
33        .title("Viridis Palette")
34        .save("palette_viridis.svg")?;
35
36    println!("Saved palette_viridis.svg");
37
38    // Brewer Set1 palette
39    GGPlot::new(df.clone())
40        .aes(Aes::new().x("x").y("y").color("group"))
41        .geom_point()
42        .scale_color_brewer(PaletteName::Set1)
43        .title("Brewer Set1 Palette")
44        .save("palette_brewer_set1.svg")?;
45
46    println!("Saved palette_brewer_set1.svg");
47
48    // Brewer Dark2 palette
49    GGPlot::new(df.clone())
50        .aes(Aes::new().x("x").y("y").color("group"))
51        .geom_point()
52        .scale_color_brewer(PaletteName::Dark2)
53        .title("Brewer Dark2 Palette")
54        .save("palette_brewer_dark2.svg")?;
55
56    println!("Saved palette_brewer_dark2.svg");
57
58    // Manual colors
59    GGPlot::new(df)
60        .aes(Aes::new().x("x").y("y").color("group"))
61        .geom_point()
62        .scale_color_manual(vec![
63            ("Alpha", RGBAColor::new(255, 0, 0)),
64            ("Beta", RGBAColor::new(0, 180, 0)),
65            ("Gamma", RGBAColor::new(0, 0, 255)),
66            ("Delta", RGBAColor::new(255, 165, 0)),
67            ("Epsilon", RGBAColor::new(128, 0, 128)),
68        ])
69        .title("Manual Color Scale")
70        .save("palette_manual.svg")?;
71
72    println!("Saved palette_manual.svg");
73    Ok(())
74}
Source

pub fn scale_color_brewer(self, name: PaletteName) -> Self

Examples found in repository?
examples/gallery.rs (line 49)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
More examples
Hide additional examples
examples/supplier_leadtime.rs (line 277)
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
examples/color_palettes.rs (line 42)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate grouped scatter data
6    let groups = ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"];
7    let mut x_vals = Vec::new();
8    let mut y_vals = Vec::new();
9    let mut group_vals = Vec::new();
10
11    for (gi, &group) in groups.iter().enumerate() {
12        for j in 0..20 {
13            let base_x = gi as f64 * 2.0 + 1.0;
14            let base_y = (gi as f64 + 1.0) * 3.0;
15            let r = ((gi * 20 + j) * 7 + 13) % 17;
16            x_vals.push(base_x + (r as f64 / 17.0 - 0.5) * 2.0);
17            y_vals.push(base_y + ((r * 3 + 5) % 11) as f64 / 11.0 * 4.0 - 2.0);
18            group_vals.push(group);
19        }
20    }
21
22    let df = df! {
23        "x" => &x_vals,
24        "y" => &y_vals,
25        "group" => &group_vals,
26    }?;
27
28    // Viridis palette
29    GGPlot::new(df.clone())
30        .aes(Aes::new().x("x").y("y").color("group"))
31        .geom_point()
32        .scale_color_viridis()
33        .title("Viridis Palette")
34        .save("palette_viridis.svg")?;
35
36    println!("Saved palette_viridis.svg");
37
38    // Brewer Set1 palette
39    GGPlot::new(df.clone())
40        .aes(Aes::new().x("x").y("y").color("group"))
41        .geom_point()
42        .scale_color_brewer(PaletteName::Set1)
43        .title("Brewer Set1 Palette")
44        .save("palette_brewer_set1.svg")?;
45
46    println!("Saved palette_brewer_set1.svg");
47
48    // Brewer Dark2 palette
49    GGPlot::new(df.clone())
50        .aes(Aes::new().x("x").y("y").color("group"))
51        .geom_point()
52        .scale_color_brewer(PaletteName::Dark2)
53        .title("Brewer Dark2 Palette")
54        .save("palette_brewer_dark2.svg")?;
55
56    println!("Saved palette_brewer_dark2.svg");
57
58    // Manual colors
59    GGPlot::new(df)
60        .aes(Aes::new().x("x").y("y").color("group"))
61        .geom_point()
62        .scale_color_manual(vec![
63            ("Alpha", RGBAColor::new(255, 0, 0)),
64            ("Beta", RGBAColor::new(0, 180, 0)),
65            ("Gamma", RGBAColor::new(0, 0, 255)),
66            ("Delta", RGBAColor::new(255, 165, 0)),
67            ("Epsilon", RGBAColor::new(128, 0, 128)),
68        ])
69        .title("Manual Color Scale")
70        .save("palette_manual.svg")?;
71
72    println!("Saved palette_manual.svg");
73    Ok(())
74}
Source

pub fn scale_color_gradient(self, low: RGBAColor, high: RGBAColor) -> Self

Examples found in repository?
examples/continuous_color.rs (line 41)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate scatter data with a continuous variable for color
6    let x: Vec<f64> = (0..200)
7        .map(|i| {
8            let t = i as f64 * 0.05;
9            t.cos() * (1.0 + t * 0.3)
10        })
11        .collect();
12    let y: Vec<f64> = (0..200)
13        .map(|i| {
14            let t = i as f64 * 0.05;
15            t.sin() * (1.0 + t * 0.3)
16        })
17        .collect();
18    let z: Vec<f64> = (0..200).map(|i| i as f64 * 0.05).collect();
19
20    let df = df! {
21        "x" => &x,
22        "y" => &y,
23        "z" => &z,
24    }?;
25
26    // Default blue-to-red gradient
27    GGPlot::new(df.clone())
28        .aes(Aes::new().x("x").y("y").color("z"))
29        .geom_point()
30        .title("Continuous Color (default gradient)")
31        .xlab("X")
32        .ylab("Y")
33        .save("continuous_color.svg")?;
34
35    println!("Saved continuous_color.svg");
36
37    // Custom gradient: dark blue to yellow
38    GGPlot::new(df)
39        .aes(Aes::new().x("x").y("y").color("z"))
40        .geom_point()
41        .scale_color_gradient(RGBAColor::new(10, 30, 100), RGBAColor::new(255, 230, 50))
42        .title("Continuous Color (custom gradient)")
43        .xlab("X")
44        .ylab("Y")
45        .save("continuous_color_custom.svg")?;
46
47    println!("Saved continuous_color_custom.svg");
48    Ok(())
49}
Source

pub fn scale_fill_gradient(self, low: RGBAColor, high: RGBAColor) -> Self

Source

pub fn scale_color_gradient2( self, low: RGBAColor, mid: RGBAColor, high: RGBAColor, ) -> Self

Source

pub fn scale_fill_gradient2( self, low: RGBAColor, mid: RGBAColor, high: RGBAColor, ) -> Self

Source

pub fn scale_fill_viridis(self) -> Self

Source

pub fn scale_color_viridis_c(self) -> Self

Continuous viridis color scale (for numeric data).

Examples found in repository?
examples/gallery.rs (line 209)
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
Source

pub fn scale_fill_viridis_c(self) -> Self

Continuous viridis fill scale (for numeric data).

Source

pub fn scale_color_gradientn(self, stops: Vec<(f64, RGBAColor)>) -> Self

N-stop continuous color gradient.

Source

pub fn scale_fill_gradientn(self, stops: Vec<(f64, RGBAColor)>) -> Self

N-stop continuous fill gradient.

Source

pub fn scale_color_steps( self, low: RGBAColor, high: RGBAColor, n_bins: usize, ) -> Self

Binned (stepped) two-colour continuous colour scale — buckets the mapped variable into n_bins bins, each a discrete colour, with a stepped legend.

Source

pub fn scale_color_stepsn(self, stops: Vec<RGBAColor>, n_bins: usize) -> Self

Binned N-stop continuous colour scale.

Source

pub fn scale_color_fermenter(self, name: PaletteName, n_bins: usize) -> Self

Binned ColorBrewer colour scale (R’s scale_color_fermenter).

Source

pub fn scale_fill_steps( self, low: RGBAColor, high: RGBAColor, n_bins: usize, ) -> Self

Binned (stepped) two-colour continuous fill scale.

Source

pub fn scale_fill_fermenter(self, name: PaletteName, n_bins: usize) -> Self

Binned ColorBrewer fill scale.

Source

pub fn scale_fill_brewer(self, name: PaletteName) -> Self

Examples found in repository?
examples/gallery.rs (line 129)
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
More examples
Hide additional examples
examples/supplier_leadtime.rs (line 276)
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
Source

pub fn scale_linetype_manual(self, values: Vec<(&str, Linetype)>) -> Self

Source

pub fn scale_shape_manual(self, values: Vec<(&str, PointShape)>) -> Self

Source

pub fn scale_color_grey(self) -> Self

Source

pub fn scale_fill_grey(self) -> Self

Source

pub fn scale_color_grey_with(self, s: ScaleColorGrey) -> Self

Source

pub fn scale_fill_grey_with(self, s: ScaleColorGrey) -> Self

Source

pub fn scale_x_reverse(self) -> Self

Source

pub fn scale_y_reverse(self) -> Self

Source

pub fn scale_x_datetime(self, s: ScaleDateTime) -> Self

Source

pub fn scale_y_datetime(self, s: ScaleDateTime) -> Self

Source

pub fn scale_size(self, s: ScaleSizeContinuous) -> Self

Source

pub fn scale_alpha(self, s: ScaleAlphaContinuous) -> Self

Source

pub fn xlim(self, min: f64, max: f64) -> Self

Source

pub fn ylim(self, min: f64, max: f64) -> Self

Source

pub fn scale_x_log10(self) -> Self

Source

pub fn scale_y_log10(self) -> Self

Examples found in repository?
examples/log_scale.rs (line 37)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Exponential growth data (e.g., population, bacteria count)
6    let year: Vec<f64> = (0..30).map(|i| 1990.0 + i as f64).collect();
7    let count: Vec<f64> = (0..30)
8        .map(|i| {
9            let base = 100.0 * (1.15_f64).powi(i); // 15% growth per year
10            let noise = ((i * 37 + 7) % 13) as f64 / 13.0 * 0.2 + 0.9;
11            base * noise
12        })
13        .collect();
14
15    let df = df! {
16        "year" => &year,
17        "count" => &count,
18    }?;
19
20    // Linear scale — exponential curve
21    GGPlot::new(df.clone())
22        .aes(Aes::new().x("year").y("count"))
23        .geom_point()
24        .geom_line()
25        .title("Exponential Growth (Linear Scale)")
26        .xlab("Year")
27        .ylab("Count")
28        .save("log_scale_linear.svg")?;
29
30    println!("Saved log_scale_linear.svg");
31
32    // Log10 y-axis — should appear roughly linear
33    GGPlot::new(df)
34        .aes(Aes::new().x("year").y("count"))
35        .geom_point()
36        .geom_line()
37        .scale_y_log10()
38        .title("Exponential Growth (Log10 Y Scale)")
39        .xlab("Year")
40        .ylab("Count (log10)")
41        .save("log_scale_log10.svg")?;
42
43    println!("Saved log_scale_log10.svg");
44    Ok(())
45}
Source

pub fn scale_x_sqrt(self) -> Self

Source

pub fn scale_y_sqrt(self) -> Self

Source

pub fn scale_x_log2(self) -> Self

Source

pub fn scale_y_log2(self) -> Self

Source

pub fn scale_x_ln(self) -> Self

Source

pub fn scale_y_ln(self) -> Self

Source

pub fn scale_x_logit(self) -> Self

Logit-transformed x axis (for proportions in (0, 1)).

Source

pub fn scale_y_logit(self) -> Self

Source

pub fn scale_x_probit(self) -> Self

Probit-transformed x axis (inverse normal CDF, for proportions in (0, 1)).

Source

pub fn scale_y_probit(self) -> Self

Source

pub fn scale_x_pseudo_log(self) -> Self

Sign-preserving pseudo-log x axis (handles zero and negative values).

Source

pub fn scale_y_pseudo_log(self) -> Self

Source

pub fn scale_x_reciprocal(self) -> Self

Reciprocal (1/x) x axis.

Source

pub fn scale_y_reciprocal(self) -> Self

Source

pub fn scale_x_exp(self) -> Self

Exponential x axis (labels spaced logarithmically).

Source

pub fn scale_y_exp(self) -> Self

Source

pub fn scale_x_boxcox(self, lambda: f64) -> Self

Box–Cox x axis with the given lambda (x > 0).

Source

pub fn scale_y_boxcox(self, lambda: f64) -> Self

Source

pub fn facet_wrap(self, var: &str, ncol: Option<usize>) -> Self

Examples found in repository?
examples/gallery.rs (line 233)
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
More examples
Hide additional examples
examples/faceted.rs (line 39)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate data with two grouping variables
6    let sepal_length: Vec<f64> = (0..120)
7        .map(|i| 4.0 + (i as f64) * 0.04 + (i as f64 * 0.5).sin() * 0.3)
8        .collect();
9    let sepal_width: Vec<f64> = (0..120)
10        .map(|i| 2.0 + (i as f64) * 0.015 + (i as f64 * 0.3).cos() * 0.4)
11        .collect();
12    let species: Vec<&str> = (0..120)
13        .map(|i| match i % 3 {
14            0 => "setosa",
15            1 => "versicolor",
16            _ => "virginica",
17        })
18        .collect();
19    let region: Vec<&str> = (0..120)
20        .map(|i| if i % 2 == 0 { "North" } else { "South" })
21        .collect();
22
23    let df = df! {
24        "sepal_length" => sepal_length,
25        "sepal_width" => sepal_width,
26        "species" => species,
27        "region" => region,
28    }?;
29
30    // facet_wrap: one variable, automatic grid layout
31    GGPlot::new(df.clone())
32        .aes(
33            Aes::new()
34                .x("sepal_length")
35                .y("sepal_width")
36                .color("species"),
37        )
38        .geom_point()
39        .facet_wrap("species", Some(2))
40        .title("Facet Wrap by Species")
41        .xlab("Sepal Length")
42        .ylab("Sepal Width")
43        .save("facet_wrap.svg")?;
44
45    println!("Saved facet_wrap.svg");
46
47    // facet_grid: two variables, row ~ col layout
48    GGPlot::new(df)
49        .aes(
50            Aes::new()
51                .x("sepal_length")
52                .y("sepal_width")
53                .color("species"),
54        )
55        .geom_point()
56        .facet_grid(Some("region"), Some("species"))
57        .title("Facet Grid: Region ~ Species")
58        .xlab("Sepal Length")
59        .ylab("Sepal Width")
60        .save("facet_grid.svg")?;
61
62    println!("Saved facet_grid.svg");
63    Ok(())
64}
Source

pub fn facet_wrap_free( self, var: &str, ncol: Option<usize>, scales: FacetScales, ) -> Self

Source

pub fn facet_wrap_labeller( self, var: &str, ncol: Option<usize>, labeller: FacetLabeller, ) -> Self

Source

pub fn facet_grid(self, row: Option<&str>, col: Option<&str>) -> Self

Examples found in repository?
examples/faceted.rs (line 56)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate data with two grouping variables
6    let sepal_length: Vec<f64> = (0..120)
7        .map(|i| 4.0 + (i as f64) * 0.04 + (i as f64 * 0.5).sin() * 0.3)
8        .collect();
9    let sepal_width: Vec<f64> = (0..120)
10        .map(|i| 2.0 + (i as f64) * 0.015 + (i as f64 * 0.3).cos() * 0.4)
11        .collect();
12    let species: Vec<&str> = (0..120)
13        .map(|i| match i % 3 {
14            0 => "setosa",
15            1 => "versicolor",
16            _ => "virginica",
17        })
18        .collect();
19    let region: Vec<&str> = (0..120)
20        .map(|i| if i % 2 == 0 { "North" } else { "South" })
21        .collect();
22
23    let df = df! {
24        "sepal_length" => sepal_length,
25        "sepal_width" => sepal_width,
26        "species" => species,
27        "region" => region,
28    }?;
29
30    // facet_wrap: one variable, automatic grid layout
31    GGPlot::new(df.clone())
32        .aes(
33            Aes::new()
34                .x("sepal_length")
35                .y("sepal_width")
36                .color("species"),
37        )
38        .geom_point()
39        .facet_wrap("species", Some(2))
40        .title("Facet Wrap by Species")
41        .xlab("Sepal Length")
42        .ylab("Sepal Width")
43        .save("facet_wrap.svg")?;
44
45    println!("Saved facet_wrap.svg");
46
47    // facet_grid: two variables, row ~ col layout
48    GGPlot::new(df)
49        .aes(
50            Aes::new()
51                .x("sepal_length")
52                .y("sepal_width")
53                .color("species"),
54        )
55        .geom_point()
56        .facet_grid(Some("region"), Some("species"))
57        .title("Facet Grid: Region ~ Species")
58        .xlab("Sepal Length")
59        .ylab("Sepal Width")
60        .save("facet_grid.svg")?;
61
62    println!("Saved facet_grid.svg");
63    Ok(())
64}
Source

pub fn facet_grid_free( self, row: Option<&str>, col: Option<&str>, scales: FacetScales, ) -> Self

Source

pub fn facet_grid_labeller( self, row: Option<&str>, col: Option<&str>, labeller: FacetLabeller, ) -> Self

Source

pub fn coord_flip(self) -> Self

Examples found in repository?
examples/coord_flip.rs (line 14)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
Source

pub fn coord_fixed(self, ratio: f64) -> Self

Source

pub fn coord_cartesian_zoom( self, xlim: Option<(f64, f64)>, ylim: Option<(f64, f64)>, ) -> Self

Zoom into a region without filtering data (unlike xlim/ylim which filter).

Source

pub fn coord_polar(self) -> Self

Source

pub fn coord_polar_with(self, coord: CoordPolar) -> Self

Source

pub fn theme(self, theme: Theme) -> Self

Source

pub fn primary_color(self, color: (u8, u8, u8)) -> Self

Set the brand/primary color used as the default for single-series geoms that have no color/fill aesthetic mapped. Composes with any theme — one render process can serve different tenants’ brands at render time.

Source

pub fn theme_minimal(self) -> Self

Examples found in repository?
examples/gallery.rs (line 53)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
More examples
Hide additional examples
examples/supplier_leadtime.rs (line 200)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
204
205/// ECDF for one supplier — P(lead <= contract) reads straight off the curve.
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
244
245/// Compare suppliers: overlaid per-supplier densities against the contract line.
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
Source

pub fn theme_bw(self) -> Self

Examples found in repository?
examples/gallery.rs (line 80)
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
More examples
Hide additional examples
examples/supplier_leadtime.rs (line 240)
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
Source

pub fn theme_gray(self) -> Self

Source

pub fn theme_classic(self) -> Self

Source

pub fn theme_linedraw(self) -> Self

Source

pub fn theme_light(self) -> Self

Source

pub fn theme_dark(self) -> Self

Source

pub fn theme_void(self) -> Self

Source

pub fn theme_update(self, update: ThemeUpdate) -> Self

Apply incremental theme modifications on top of the current theme. Like R’s + theme(axis.text.x = element_text(...)).

Source

pub fn guides(self, guide: GuideLegend) -> Self

Configure legend guide (title, ncol, reverse).

Source

pub fn labs(self, labels: Labels) -> Self

Source

pub fn title(self, title: &str) -> Self

Examples found in repository?
examples/bar_chart.rs (line 13)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "fruit" => ["Apple", "Apple", "Apple", "Banana", "Banana",
7                     "Cherry", "Cherry", "Cherry", "Cherry", "Date"],
8    }?;
9
10    GGPlot::new(df)
11        .aes(Aes::new().x("fruit"))
12        .geom_bar()
13        .title("Fruit Counts")
14        .xlab("Fruit")
15        .ylab("Count")
16        .save("bar_chart.svg")?;
17
18    println!("Saved bar_chart.svg");
19    Ok(())
20}
More examples
Hide additional examples
examples/coord_flip.rs (line 15)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
examples/gallery.rs (line 50)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
examples/annotations.rs (line 26)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
examples/density.rs (line 29)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate two overlapping distributions using deterministic pseudo-random values
6    let value: Vec<f64> = (0..200_u64)
7        .map(|i| {
8            let r = ((i.wrapping_mul(1103515245).wrapping_add(12345)) % (1 << 16)) as f64
9                / (1u64 << 16) as f64;
10            if i < 100 {
11                3.0 + r * 4.0
12            } else {
13                5.0 + r * 4.0
14            }
15        })
16        .collect();
17    let group: Vec<&str> = (0..200)
18        .map(|i| if i < 100 { "Group A" } else { "Group B" })
19        .collect();
20
21    let df = df! {
22        "value" => value,
23        "group" => group,
24    }?;
25
26    GGPlot::new(df)
27        .aes(Aes::new().x("value").color("group"))
28        .geom_density()
29        .title("Density Plot by Group")
30        .xlab("Value")
31        .ylab("Density")
32        .save("density.svg")?;
33
34    println!("Saved density.svg");
35    Ok(())
36}
examples/scatter.rs (line 31)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let sepal_length: Vec<f64> = (0..50).map(|i| 4.5 + i as f64 * 0.05).collect();
6    let sepal_width: Vec<f64> = (0..50)
7        .map(|i| 2.0 + (i as f64 * 0.3).sin() + i as f64 * 0.02)
8        .collect();
9    let species: Vec<&str> = (0..50)
10        .map(|i| match i % 3 {
11            0 => "setosa",
12            1 => "versicolor",
13            _ => "virginica",
14        })
15        .collect();
16
17    let df = df! {
18        "sepal_length" => sepal_length,
19        "sepal_width" => sepal_width,
20        "species" => species,
21    }?;
22
23    GGPlot::new(df)
24        .aes(
25            Aes::new()
26                .x("sepal_length")
27                .y("sepal_width")
28                .color("species"),
29        )
30        .geom_point()
31        .title("Iris Scatter Plot")
32        .xlab("Sepal Length")
33        .ylab("Sepal Width")
34        .save("scatter.svg")?;
35
36    println!("Saved scatter.svg");
37    Ok(())
38}
Source

pub fn subtitle(self, subtitle: &str) -> Self

Examples found in repository?
examples/supplier_leadtime.rs (line 197)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
204
205/// ECDF for one supplier — P(lead <= contract) reads straight off the curve.
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
244
245/// Compare suppliers: overlaid per-supplier densities against the contract line.
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
Source

pub fn xlab(self, label: &str) -> Self

Examples found in repository?
examples/bar_chart.rs (line 14)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "fruit" => ["Apple", "Apple", "Apple", "Banana", "Banana",
7                     "Cherry", "Cherry", "Cherry", "Cherry", "Date"],
8    }?;
9
10    GGPlot::new(df)
11        .aes(Aes::new().x("fruit"))
12        .geom_bar()
13        .title("Fruit Counts")
14        .xlab("Fruit")
15        .ylab("Count")
16        .save("bar_chart.svg")?;
17
18    println!("Saved bar_chart.svg");
19    Ok(())
20}
More examples
Hide additional examples
examples/coord_flip.rs (line 16)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
examples/gallery.rs (line 51)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
examples/annotations.rs (line 27)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
examples/density.rs (line 30)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate two overlapping distributions using deterministic pseudo-random values
6    let value: Vec<f64> = (0..200_u64)
7        .map(|i| {
8            let r = ((i.wrapping_mul(1103515245).wrapping_add(12345)) % (1 << 16)) as f64
9                / (1u64 << 16) as f64;
10            if i < 100 {
11                3.0 + r * 4.0
12            } else {
13                5.0 + r * 4.0
14            }
15        })
16        .collect();
17    let group: Vec<&str> = (0..200)
18        .map(|i| if i < 100 { "Group A" } else { "Group B" })
19        .collect();
20
21    let df = df! {
22        "value" => value,
23        "group" => group,
24    }?;
25
26    GGPlot::new(df)
27        .aes(Aes::new().x("value").color("group"))
28        .geom_density()
29        .title("Density Plot by Group")
30        .xlab("Value")
31        .ylab("Density")
32        .save("density.svg")?;
33
34    println!("Saved density.svg");
35    Ok(())
36}
examples/scatter.rs (line 32)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let sepal_length: Vec<f64> = (0..50).map(|i| 4.5 + i as f64 * 0.05).collect();
6    let sepal_width: Vec<f64> = (0..50)
7        .map(|i| 2.0 + (i as f64 * 0.3).sin() + i as f64 * 0.02)
8        .collect();
9    let species: Vec<&str> = (0..50)
10        .map(|i| match i % 3 {
11            0 => "setosa",
12            1 => "versicolor",
13            _ => "virginica",
14        })
15        .collect();
16
17    let df = df! {
18        "sepal_length" => sepal_length,
19        "sepal_width" => sepal_width,
20        "species" => species,
21    }?;
22
23    GGPlot::new(df)
24        .aes(
25            Aes::new()
26                .x("sepal_length")
27                .y("sepal_width")
28                .color("species"),
29        )
30        .geom_point()
31        .title("Iris Scatter Plot")
32        .xlab("Sepal Length")
33        .ylab("Sepal Width")
34        .save("scatter.svg")?;
35
36    println!("Saved scatter.svg");
37    Ok(())
38}
Source

pub fn ylab(self, label: &str) -> Self

Examples found in repository?
examples/bar_chart.rs (line 15)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "fruit" => ["Apple", "Apple", "Apple", "Banana", "Banana",
7                     "Cherry", "Cherry", "Cherry", "Cherry", "Date"],
8    }?;
9
10    GGPlot::new(df)
11        .aes(Aes::new().x("fruit"))
12        .geom_bar()
13        .title("Fruit Counts")
14        .xlab("Fruit")
15        .ylab("Count")
16        .save("bar_chart.svg")?;
17
18    println!("Saved bar_chart.svg");
19    Ok(())
20}
More examples
Hide additional examples
examples/coord_flip.rs (line 17)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
examples/gallery.rs (line 52)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
examples/annotations.rs (line 28)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
examples/density.rs (line 31)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate two overlapping distributions using deterministic pseudo-random values
6    let value: Vec<f64> = (0..200_u64)
7        .map(|i| {
8            let r = ((i.wrapping_mul(1103515245).wrapping_add(12345)) % (1 << 16)) as f64
9                / (1u64 << 16) as f64;
10            if i < 100 {
11                3.0 + r * 4.0
12            } else {
13                5.0 + r * 4.0
14            }
15        })
16        .collect();
17    let group: Vec<&str> = (0..200)
18        .map(|i| if i < 100 { "Group A" } else { "Group B" })
19        .collect();
20
21    let df = df! {
22        "value" => value,
23        "group" => group,
24    }?;
25
26    GGPlot::new(df)
27        .aes(Aes::new().x("value").color("group"))
28        .geom_density()
29        .title("Density Plot by Group")
30        .xlab("Value")
31        .ylab("Density")
32        .save("density.svg")?;
33
34    println!("Saved density.svg");
35    Ok(())
36}
examples/scatter.rs (line 33)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let sepal_length: Vec<f64> = (0..50).map(|i| 4.5 + i as f64 * 0.05).collect();
6    let sepal_width: Vec<f64> = (0..50)
7        .map(|i| 2.0 + (i as f64 * 0.3).sin() + i as f64 * 0.02)
8        .collect();
9    let species: Vec<&str> = (0..50)
10        .map(|i| match i % 3 {
11            0 => "setosa",
12            1 => "versicolor",
13            _ => "virginica",
14        })
15        .collect();
16
17    let df = df! {
18        "sepal_length" => sepal_length,
19        "sepal_width" => sepal_width,
20        "species" => species,
21    }?;
22
23    GGPlot::new(df)
24        .aes(
25            Aes::new()
26                .x("sepal_length")
27                .y("sepal_width")
28                .color("species"),
29        )
30        .geom_point()
31        .title("Iris Scatter Plot")
32        .xlab("Sepal Length")
33        .ylab("Sepal Width")
34        .save("scatter.svg")?;
35
36    println!("Saved scatter.svg");
37    Ok(())
38}
Source

pub fn caption(self, caption: &str) -> Self

Source

pub fn annotate(self, annotation: Annotation) -> Self

Add an annotation to the plot.

Source

pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self

Add a text annotation at data coordinates.

Examples found in repository?
examples/annotations.rs (line 23)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
More examples
Hide additional examples
examples/supplier_leadtime.rs (line 193)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
204
205/// ECDF for one supplier — P(lead <= contract) reads straight off the curve.
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
244
245/// Compare suppliers: overlaid per-supplier densities against the contract line.
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
Source

pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self

Add a rectangle annotation at data coordinates.

Examples found in repository?
examples/annotations.rs (line 21)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
Source

pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self

Add a segment annotation between data coordinates.

Examples found in repository?
examples/annotations.rs (line 25)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
Source

pub fn try_build(self) -> Result<BuiltPlot, GGError>

Build the plot without rendering, returning errors on validation failure.

Source

pub fn build(self) -> BuiltPlot

Build the plot without rendering (analogous to R’s ggplot_build()). Returns the fully computed BuiltPlot with layer data ready for inspection. Panics on validation errors — use try_build() for error handling.

Source

pub fn save(self, path: &str) -> Result<(), GGError>

Build and save the plot to a file. Format determined by extension.

Examples found in repository?
examples/bar_chart.rs (line 16)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "fruit" => ["Apple", "Apple", "Apple", "Banana", "Banana",
7                     "Cherry", "Cherry", "Cherry", "Cherry", "Date"],
8    }?;
9
10    GGPlot::new(df)
11        .aes(Aes::new().x("fruit"))
12        .geom_bar()
13        .title("Fruit Counts")
14        .xlab("Fruit")
15        .ylab("Count")
16        .save("bar_chart.svg")?;
17
18    println!("Saved bar_chart.svg");
19    Ok(())
20}
More examples
Hide additional examples
examples/coord_flip.rs (line 18)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let df = df! {
6        "language" => ["Rust", "Python", "JavaScript", "Go", "TypeScript", "Java", "C++"],
7        "satisfaction" => [92.0, 88.0, 85.0, 78.0, 82.0, 70.0, 75.0],
8    }?;
9
10    // Horizontal bar chart using coord_flip
11    GGPlot::new(df)
12        .aes(Aes::new().x("language").y("satisfaction"))
13        .geom_col()
14        .coord_flip()
15        .title("Developer Satisfaction by Language")
16        .xlab("Language")
17        .ylab("Satisfaction Score")
18        .save("coord_flip.svg")?;
19
20    println!("Saved coord_flip.svg");
21    Ok(())
22}
examples/annotations.rs (line 29)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Sales data with a notable spike
6    let month: Vec<f64> = (1..=12).map(|i| i as f64).collect();
7    let sales = vec![
8        120.0, 135.0, 150.0, 180.0, 210.0, 310.0, 280.0, 250.0, 190.0, 170.0, 155.0, 140.0,
9    ];
10
11    let df = df! {
12        "month" => month,
13        "sales" => sales,
14    }?;
15
16    GGPlot::new(df)
17        .aes(Aes::new().x("month").y("sales"))
18        .geom_line()
19        .geom_point()
20        // Highlight the peak region
21        .annotate_rect(4.5, 7.5, 100.0, 320.0)
22        // Label the peak
23        .annotate_text("Summer Peak", 6.0, 330.0)
24        // Draw an arrow-like segment pointing to the max
25        .annotate_segment(7.5, 330.0, 6.2, 312.0)
26        .title("Monthly Sales with Annotations")
27        .xlab("Month")
28        .ylab("Sales ($)")
29        .save("annotations.svg")?;
30
31    println!("Saved annotations.svg");
32    Ok(())
33}
examples/density.rs (line 32)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate two overlapping distributions using deterministic pseudo-random values
6    let value: Vec<f64> = (0..200_u64)
7        .map(|i| {
8            let r = ((i.wrapping_mul(1103515245).wrapping_add(12345)) % (1 << 16)) as f64
9                / (1u64 << 16) as f64;
10            if i < 100 {
11                3.0 + r * 4.0
12            } else {
13                5.0 + r * 4.0
14            }
15        })
16        .collect();
17    let group: Vec<&str> = (0..200)
18        .map(|i| if i < 100 { "Group A" } else { "Group B" })
19        .collect();
20
21    let df = df! {
22        "value" => value,
23        "group" => group,
24    }?;
25
26    GGPlot::new(df)
27        .aes(Aes::new().x("value").color("group"))
28        .geom_density()
29        .title("Density Plot by Group")
30        .xlab("Value")
31        .ylab("Density")
32        .save("density.svg")?;
33
34    println!("Saved density.svg");
35    Ok(())
36}
examples/scatter.rs (line 34)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    let sepal_length: Vec<f64> = (0..50).map(|i| 4.5 + i as f64 * 0.05).collect();
6    let sepal_width: Vec<f64> = (0..50)
7        .map(|i| 2.0 + (i as f64 * 0.3).sin() + i as f64 * 0.02)
8        .collect();
9    let species: Vec<&str> = (0..50)
10        .map(|i| match i % 3 {
11            0 => "setosa",
12            1 => "versicolor",
13            _ => "virginica",
14        })
15        .collect();
16
17    let df = df! {
18        "sepal_length" => sepal_length,
19        "sepal_width" => sepal_width,
20        "species" => species,
21    }?;
22
23    GGPlot::new(df)
24        .aes(
25            Aes::new()
26                .x("sepal_length")
27                .y("sepal_width")
28                .color("species"),
29        )
30        .geom_point()
31        .title("Iris Scatter Plot")
32        .xlab("Sepal Length")
33        .ylab("Sepal Width")
34        .save("scatter.svg")?;
35
36    println!("Saved scatter.svg");
37    Ok(())
38}
examples/loess_smooth.rs (line 26)
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Generate noisy sine wave data
6    let x: Vec<f64> = (0..80).map(|i| (i as f64) * 0.1).collect();
7    let y: Vec<f64> = (0..80)
8        .map(|i| {
9            let xv = (i as f64) * 0.1;
10            let noise = ((i * 17 + 3) % 11) as f64 / 11.0 - 0.5; // deterministic pseudo-noise
11            (xv * 0.8).sin() * 2.0 + noise * 1.5
12        })
13        .collect();
14
15    let df = df! {
16        "x" => &x,
17        "y" => &y,
18    }?;
19
20    // Linear smooth (default)
21    GGPlot::new(df.clone())
22        .aes(Aes::new().x("x").y("y"))
23        .geom_point()
24        .geom_smooth()
25        .title("Linear Smooth (method = lm)")
26        .save("smooth_lm.svg")?;
27
28    println!("Saved smooth_lm.svg");
29
30    // LOESS smooth
31    GGPlot::new(df)
32        .aes(Aes::new().x("x").y("y"))
33        .geom_point()
34        .geom_smooth_with(GeomSmooth::default().loess(0.3))
35        .title("LOESS Smooth (span = 0.3)")
36        .save("smooth_loess.svg")?;
37
38    println!("Saved smooth_loess.svg");
39    Ok(())
40}
Source

pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError>

Build and save with custom dimensions.

Examples found in repository?
examples/gallery.rs (line 54)
35fn scatter() -> Result<(), Box<dyn std::error::Error>> {
36    let n = 150;
37    let x: Vec<f64> = (0..n).map(|i| 4.5 + i as f64 * 0.02).collect();
38    let y: Vec<f64> = (0..n)
39        .map(|i| 2.5 + (i as f64 * 0.15).sin() + (i % 3) as f64 * 0.6)
40        .collect();
41    let species: Vec<&str> = (0..n)
42        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
43        .collect();
44
45    let df = df! { "x" => x, "y" => y, "species" => species }?;
46    GGPlot::new(df)
47        .aes(Aes::new().x("x").y("y").color("species"))
48        .geom_point()
49        .scale_color_brewer(PaletteName::Set1)
50        .title("Grouped Scatter")
51        .xlab("Sepal Length")
52        .ylab("Sepal Width")
53        .theme_minimal()
54        .save_with_size(&out("scatter"), W, H)?;
55    Ok(())
56}
57
58/// Points overlaid with a LOESS trend line and confidence band.
59fn smooth() -> Result<(), Box<dyn std::error::Error>> {
60    let n = 120;
61    let x: Vec<f64> = (0..n).map(|i| i as f64 * 0.1).collect();
62    let y: Vec<f64> = (0..n)
63        .map(|i| {
64            let t = i as f64 * 0.1;
65            (t * 0.6).sin() * 3.0 + t * 0.2 + ((i * 7919 % 100) as f64 / 100.0 - 0.5) * 1.5
66        })
67        .collect();
68
69    let df = df! { "x" => x, "y" => y }?;
70    GGPlot::new(df)
71        .aes(Aes::new().x("x").y("y"))
72        .geom_point()
73        .geom_smooth_with(GeomSmooth {
74            method: SmoothMethod::Loess { span: 0.5 },
75            ..Default::default()
76        })
77        .title("LOESS Smoothing")
78        .xlab("x")
79        .ylab("y")
80        .theme_bw()
81        .save_with_size(&out("smooth"), W, H)?;
82    Ok(())
83}
84
85/// Histogram of an approximately-normal sample.
86fn histogram() -> Result<(), Box<dyn std::error::Error>> {
87    let values: Vec<f64> = (0..1500)
88        .map(|i: i32| {
89            let r: f64 = (0..6)
90                .map(|k| ((i * (1237 + k * 311) + 5678) % 1000) as f64 / 1000.0)
91                .sum();
92            (r - 3.0) * 2.0
93        })
94        .collect();
95
96    let df = df! { "measurement" => values }?;
97    GGPlot::new(df)
98        .aes(Aes::new().x("measurement"))
99        .geom_histogram_with(GeomHistogram {
100            bins: 30,
101            ..Default::default()
102        })
103        .title("Histogram")
104        .xlab("Value")
105        .ylab("Count")
106        .theme_minimal()
107        .save_with_size(&out("histogram"), W, H)?;
108    Ok(())
109}
110
111/// Bar chart of category counts with a fill palette.
112fn bar() -> Result<(), Box<dyn std::error::Error>> {
113    let mut fruit: Vec<&str> = Vec::new();
114    for (f, c) in [
115        ("Apple", 8),
116        ("Banana", 5),
117        ("Cherry", 11),
118        ("Date", 3),
119        ("Elder", 7),
120    ] {
121        for _ in 0..c {
122            fruit.push(f);
123        }
124    }
125    let df = df! { "fruit" => fruit }?;
126    GGPlot::new(df)
127        .aes(Aes::new().x("fruit").fill("fruit"))
128        .geom_bar()
129        .scale_fill_brewer(PaletteName::Set2)
130        .title("Bar Chart")
131        .xlab("Fruit")
132        .ylab("Count")
133        .theme_minimal()
134        .save_with_size(&out("bar"), W, H)?;
135    Ok(())
136}
137
138/// Grouped boxplots.
139fn boxplot() -> Result<(), Box<dyn std::error::Error>> {
140    let n = 240;
141    let group: Vec<&str> = (0..n).map(|i| ["A", "B", "C", "D"][i % 4]).collect();
142    let value: Vec<f64> = (0..n)
143        .map(|i| {
144            let base = (i % 4) as f64 * 1.5;
145            base + (i as f64 * 0.4).sin() * 1.2 + ((i * 6151 % 100) as f64 / 100.0 - 0.5) * 2.0
146        })
147        .collect();
148
149    let df = df! { "group" => group, "value" => value }?;
150    GGPlot::new(df)
151        .aes(Aes::new().x("group").y("value"))
152        .geom_boxplot_with(GeomBoxplot {
153            fill: (70, 130, 180),
154            ..Default::default()
155        })
156        .title("Boxplot")
157        .xlab("Group")
158        .ylab("Value")
159        .theme_bw()
160        .save_with_size(&out("boxplot"), W, H)?;
161    Ok(())
162}
163
164/// Violin plots of grouped distributions.
165fn violin() -> Result<(), Box<dyn std::error::Error>> {
166    let n = 360;
167    let group: Vec<&str> = (0..n).map(|i| ["X", "Y", "Z"][i % 3]).collect();
168    let value: Vec<f64> = (0..n)
169        .map(|i| {
170            let g = (i % 3) as f64;
171            g * 2.0 + (i as f64 * 0.5).sin() * 1.5 + ((i * 4231 % 100) as f64 / 100.0 - 0.5) * 2.5
172        })
173        .collect();
174
175    let df = df! { "group" => group, "value" => value }?;
176    GGPlot::new(df)
177        .aes(Aes::new().x("group").y("value").fill("group"))
178        .geom_violin()
179        .scale_fill_brewer(PaletteName::Accent)
180        .title("Violin")
181        .xlab("Group")
182        .ylab("Value")
183        .theme_minimal()
184        .save_with_size(&out("violin"), W, H)?;
185    Ok(())
186}
187
188/// Spiral scatter coloured by a continuous variable (viridis).
189fn continuous_color() -> Result<(), Box<dyn std::error::Error>> {
190    let n = 400;
191    let x: Vec<f64> = (0..n)
192        .map(|i| {
193            let t = i as f64 * 0.05;
194            t.cos() * (1.0 + t * 0.12)
195        })
196        .collect();
197    let y: Vec<f64> = (0..n)
198        .map(|i| {
199            let t = i as f64 * 0.05;
200            t.sin() * (1.0 + t * 0.12)
201        })
202        .collect();
203    let z: Vec<f64> = (0..n).map(|i| i as f64 * 0.05).collect();
204
205    let df = df! { "x" => x, "y" => y, "z" => z }?;
206    GGPlot::new(df)
207        .aes(Aes::new().x("x").y("y").color("z"))
208        .geom_point()
209        .scale_color_viridis_c()
210        .title("Continuous Color (viridis)")
211        .xlab("x")
212        .ylab("y")
213        .theme_minimal()
214        .save_with_size(&out("continuous_color"), W, H)?;
215    Ok(())
216}
217
218/// Faceted scatter, one panel per group.
219fn facet() -> Result<(), Box<dyn std::error::Error>> {
220    let n = 180;
221    let x: Vec<f64> = (0..n).map(|i| (i as f64 * 0.1).cos() * 3.0).collect();
222    let y: Vec<f64> = (0..n)
223        .map(|i| (i as f64 * 0.1).sin() * 3.0 + (i % 3) as f64)
224        .collect();
225    let species: Vec<&str> = (0..n)
226        .map(|i| ["setosa", "versicolor", "virginica"][i % 3])
227        .collect();
228
229    let df = df! { "x" => x, "y" => y, "species" => species }?;
230    GGPlot::new(df)
231        .aes(Aes::new().x("x").y("y").color("species"))
232        .geom_point()
233        .facet_wrap("species", Some(3))
234        .scale_color_brewer(PaletteName::Set1)
235        .title("Facet Wrap")
236        .xlab("x")
237        .ylab("y")
238        .theme_bw()
239        .save_with_size(&out("facet"), W, H)?;
240    Ok(())
241}
242
243/// Overlapping density curves by group.
244fn density() -> Result<(), Box<dyn std::error::Error>> {
245    let n = 600;
246    let group: Vec<&str> = (0..n).map(|i| ["Group 1", "Group 2"][i % 2]).collect();
247    let value: Vec<f64> = (0..n)
248        .map(|i| {
249            let shift = (i % 2) as f64 * 2.5;
250            let t = i as f64 * 0.05;
251            shift + (t.sin() + (t * 1.7).cos()) + ((i * 3319 % 100) as f64 / 100.0 - 0.5) * PI
252        })
253        .collect();
254
255    let df = df! { "value" => value, "group" => group }?;
256    GGPlot::new(df)
257        .aes(Aes::new().x("value").fill("group").color("group"))
258        .geom_density()
259        .scale_fill_brewer(PaletteName::Set1)
260        .scale_color_brewer(PaletteName::Set1)
261        .title("Density by Group")
262        .xlab("Value")
263        .ylab("Density")
264        .theme_minimal()
265        .save_with_size(&out("density"), W, H)?;
266    Ok(())
267}
More examples
Hide additional examples
examples/supplier_leadtime.rs (line 201)
137fn detail(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
138    let name = "Meridian Components";
139    let mine: Vec<&Po> = rows.iter().filter(|p| p.supplier == name).collect();
140
141    let attributable: Vec<f64> = mine
142        .iter()
143        .filter(|p| p.attributable)
144        .map(|p| p.lead)
145        .collect();
146    let p90 = percentile(attributable.clone(), 0.90);
147
148    let dens_data: Vec<(String, Vec<Value>)> = vec![(
149        "lead".to_string(),
150        attributable.iter().map(|v| Value::Float(*v)).collect(),
151    )];
152    let excused_data: Vec<(String, Vec<Value>)> = vec![(
153        "lead".to_string(),
154        mine.iter()
155            .filter(|p| !p.attributable)
156            .map(|p| Value::Float(p.lead))
157            .collect(),
158    )];
159
160    GGPlot::new(dens_data)
161        .aes(Aes::new().x("lead"))
162        .geom_density_with(GeomDensity {
163            fill: TEAL,
164            color: (20, 110, 98),
165            alpha: 0.45,
166            line_width: 1.5,
167        })
168        // SLA threshold — mass to the left is the on-time share.
169        .geom_vline_with(GeomVline {
170            xintercept: CONTRACT,
171            color: CONTRACT_RED,
172            width: 1.5,
173            linetype: Linetype::Dashed,
174            alpha: 1.0,
175        })
176        // p90 — the number to size safety stock against.
177        .geom_vline_with(GeomVline {
178            xintercept: p90,
179            color: P90_BLUE,
180            width: 1.5,
181            linetype: Linetype::Dashed,
182            alpha: 1.0,
183        })
184        // Excused deliveries, shown distinctly and kept out of the density.
185        .geom_rug_with(GeomRug {
186            color: MUTED,
187            alpha: 0.7,
188            length: 0.04,
189            sides: "b".to_string(),
190        })
191        .layer_data(excused_data)
192        .layer_aes(Aes::new().x("lead"))
193        .annotate_text(&format!("contract {CONTRACT:.0}d"), CONTRACT + 1.0, 0.075)
194        .annotate_text(&format!("p90 {p90:.0}d"), p90 + 1.0, 0.06)
195        .annotate_text("<- excused (external)", 48.0, 0.008)
196        .title(&format!("{name} — lead-time distribution"))
197        .subtitle("attributable deliveries only; excused shown as rug")
198        .xlab("Actual lead time (days)")
199        .ylab("Density")
200        .theme_minimal()
201        .save_with_size(&out("supplier_leadtime"), W, H)?;
202    Ok(())
203}
204
205/// ECDF for one supplier — P(lead <= contract) reads straight off the curve.
206fn ecdf(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
207    let name = "Meridian Components";
208    let attributable: Vec<f64> = rows
209        .iter()
210        .filter(|p| p.supplier == name && p.attributable)
211        .map(|p| p.lead)
212        .collect();
213    let on_time =
214        attributable.iter().filter(|&&l| l <= CONTRACT).count() as f64 / attributable.len() as f64;
215    let data: Vec<(String, Vec<Value>)> = vec![(
216        "lead".to_string(),
217        attributable.iter().map(|v| Value::Float(*v)).collect(),
218    )];
219
220    GGPlot::new(data)
221        .aes(Aes::new().x("lead"))
222        .geom_step()
223        .stat(StatEcdf)
224        .geom_vline_with(GeomVline {
225            xintercept: CONTRACT,
226            color: CONTRACT_RED,
227            width: 1.5,
228            linetype: Linetype::Dashed,
229            alpha: 1.0,
230        })
231        .annotate_text(
232            &format!("on-time rate {:.0}%", on_time * 100.0),
233            CONTRACT + 1.0,
234            0.15,
235        )
236        .title(&format!("{name} — on-time reliability (ECDF)"))
237        .subtitle("cumulative share at the contract line = P(lead <= 30d)")
238        .xlab("Actual lead time (days)")
239        .ylab("Cumulative share")
240        .theme_bw()
241        .save_with_size(&out("supplier_leadtime_ecdf"), W, H)?;
242    Ok(())
243}
244
245/// Compare suppliers: overlaid per-supplier densities against the contract line.
246fn compare(rows: &[Po]) -> Result<(), Box<dyn std::error::Error>> {
247    let lead: Vec<Value> = rows
248        .iter()
249        .filter(|p| p.attributable)
250        .map(|p| Value::Float(p.lead))
251        .collect();
252    let supplier: Vec<Value> = rows
253        .iter()
254        .filter(|p| p.attributable)
255        .map(|p| Value::Str(p.supplier.to_string()))
256        .collect();
257    let data: Vec<(String, Vec<Value>)> = vec![
258        ("lead".to_string(), lead),
259        ("supplier".to_string(), supplier),
260    ];
261
262    GGPlot::new(data)
263        .aes(Aes::new().x("lead").fill("supplier").color("supplier"))
264        .geom_density_with(GeomDensity {
265            alpha: 0.35,
266            line_width: 1.2,
267            ..Default::default()
268        })
269        .geom_vline_with(GeomVline {
270            xintercept: CONTRACT,
271            color: CONTRACT_RED,
272            width: 1.2,
273            linetype: Linetype::Dashed,
274            alpha: 1.0,
275        })
276        .scale_fill_brewer(PaletteName::Dark2)
277        .scale_color_brewer(PaletteName::Dark2)
278        .annotate_text("contract", CONTRACT + 1.0, 0.005)
279        .title("Lead-time distributions by supplier")
280        .subtitle("attributable deliveries; dashed line = contracted lead time")
281        .xlab("Actual lead time (days)")
282        .ylab("Density")
283        .theme_minimal()
284        .save_with_size(&out("supplier_leadtime_compare"), W, H)?;
285    Ok(())
286}
Source

pub fn render_svg(self) -> Result<String, GGError>

Render the plot to an in-memory SVG document (default 800x600).

Unlike save, this writes nothing to disk — handy for serving charts from a web/MCP service.

Source

pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError>

Render the plot to an in-memory SVG document with custom dimensions.

Source

pub fn render_png(self) -> Result<Vec<u8>, GGError>

Render the plot to in-memory PNG bytes (default 800x600).

Returns a fully-encoded PNG, ready to write to an HTTP response or embed as a data URI — no temp files involved.

Source

pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError>

Render the plot to in-memory PNG bytes with custom dimensions.

Source

pub fn ggsave( self, path: &str, width_inches: f64, height_inches: f64, dpi: f64, ) -> Result<(), GGError>

Save with physical dimensions (inches) and DPI.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V