1use ggplot_rs::prelude::*;
7use polars::prelude::*;
8use std::f64::consts::PI;
9
10const W: u32 = 640;
11const H: u32 = 480;
12
13fn out(name: &str) -> String {
14 format!("assets/gallery/{name}.png")
15}
16
17fn main() -> Result<(), Box<dyn std::error::Error>> {
18 std::fs::create_dir_all("assets/gallery")?;
19
20 scatter()?;
21 smooth()?;
22 histogram()?;
23 bar()?;
24 boxplot()?;
25 violin()?;
26 continuous_color()?;
27 facet()?;
28 density()?;
29
30 println!("Gallery written to assets/gallery/");
31 Ok(())
32}
33
34fn 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
58fn 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
85fn 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
111fn 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
138fn 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
164fn 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
188fn 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
218fn 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
243fn 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}