1use plotlars::polars::prelude::*;
2use plotlars::{
3 Arrangement, BarPlot, BoxPlot, ContourPlot, CsvReader, FacetConfig, FacetScales, HeatMap,
4 Histogram, Lighting, Line, LinePlot, Mesh3D, Mode, Palette, PieChart, Plot, Rgb, SankeyDiagram,
5 Scatter3dPlot, ScatterPlot, ScatterPolar, Shape, SurfacePlot, Text, TimeSeriesPlot,
6};
7
8fn main() {
9 barplot_example();
10 boxplot_example();
11 contourplot_example();
12 heatmap_example();
13 histogram_example();
14 lineplot_example();
15 mesh3d_example();
16 piechart_example();
17 sankeydiagram_example();
18 scatter3d_example();
19 scatterplot_example();
20 scatterpolar_example();
21 surfaceplot_example();
22 timeseriesplot_example();
23}
24
25fn barplot_example() {
26 let regional_data = CsvReader::new("data/regional_sales.csv").finish().unwrap();
27
28 let facet_config = FacetConfig::new().cols(4).rows(2).h_gap(0.05).v_gap(0.30);
29
30 BarPlot::builder()
31 .data(®ional_data)
32 .labels("product")
33 .values("sales")
34 .facet("region")
35 .facet_config(&facet_config)
36 .color(Rgb(70, 130, 180))
37 .plot_title(Text::from("8-Region Sales Facet Grid"))
38 .x_title("Product")
39 .y_title("Sales")
40 .build()
41 .plot();
42}
43
44fn boxplot_example() {
45 let dataset = CsvReader::new("data/penguins.csv")
46 .finish()
47 .unwrap()
48 .lazy()
49 .select([
50 col("species"),
51 col("island"),
52 col("sex"),
53 col("body_mass_g").cast(DataType::Int16),
54 ])
55 .collect()
56 .unwrap();
57
58 BoxPlot::builder()
59 .data(&dataset)
60 .labels("island")
61 .values("body_mass_g")
62 .group("sex")
63 .colors(vec![Rgb(0, 119, 182), Rgb(0, 180, 216), Rgb(144, 224, 239)])
64 .facet("species")
65 .facet_config(&FacetConfig::new().cols(3))
66 .plot_title(Text::from("Body Mass by Island, Sex and Species").size(16))
67 .x_title(Text::from("Island"))
68 .y_title(Text::from("Body Mass (g)"))
69 .legend_title(Text::from("Sex"))
70 .build()
71 .plot();
72}
73
74fn contourplot_example() {
75 let mut x_vals = Vec::new();
76 let mut y_vals = Vec::new();
77 let mut z_vals = Vec::new();
78 let mut patterns = Vec::new();
79
80 let grid_size = 25;
81
82 type SurfaceFn = Box<dyn Fn(f64, f64) -> f64>;
83 let functions: Vec<(&str, SurfaceFn)> = vec![
84 ("Gaussian", Box::new(|x, y| (-x * x - y * y).exp())),
85 ("Saddle", Box::new(|x, y| x * x - y * y)),
86 (
87 "Ripple",
88 Box::new(|x, y| {
89 let r = (x * x + y * y).sqrt();
90 (r * 2.0).sin() / (r + 0.1)
91 }),
92 ),
93 ("Paraboloid", Box::new(|x, y| x * x + y * y)),
94 ("Wave", Box::new(|x, y| (x * 2.0).sin() * (y * 2.0).cos())),
95 ("Diagonal", Box::new(|x, y| ((x + y) * 2.0).sin())),
96 ];
97
98 for (name, func) in &functions {
99 let mut raw_z = Vec::new();
100 let mut coords = Vec::new();
101
102 for i in 0..grid_size {
103 for j in 0..grid_size {
104 let x = (i as f64 - 12.0) / 3.0;
105 let y = (j as f64 - 12.0) / 3.0;
106 let z = func(x, y);
107 raw_z.push(z);
108 coords.push((x, y));
109 }
110 }
111
112 let z_min = raw_z.iter().cloned().fold(f64::INFINITY, f64::min);
114 let z_max = raw_z.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
115 let range = z_max - z_min;
116
117 for (idx, &(x, y)) in coords.iter().enumerate() {
118 let z = if range > 0.0 {
119 2.0 * (raw_z[idx] - z_min) / range - 1.0
120 } else {
121 0.0
122 };
123 x_vals.push(x);
124 y_vals.push(y);
125 z_vals.push(z);
126 patterns.push(*name);
127 }
128 }
129
130 let contour_data = df! {
131 "x" => x_vals,
132 "y" => y_vals,
133 "z" => z_vals,
134 "pattern" => patterns,
135 }
136 .unwrap();
137
138 ContourPlot::builder()
139 .data(&contour_data)
140 .x("x")
141 .y("y")
142 .z("z")
143 .facet("pattern")
144 .facet_config(&FacetConfig::new().rows(2).cols(3).scales(FacetScales::Free))
145 .plot_title(Text::from("Mathematical Surface Patterns").size(16))
146 .x_title(Text::from("X Axis"))
147 .y_title(Text::from("Y Axis"))
148 .build()
149 .plot();
150}
151
152fn heatmap_example() {
153 let mut regions = Vec::new();
154 let mut x_coords = Vec::new();
155 let mut y_coords = Vec::new();
156 let mut intensities = Vec::new();
157
158 let region_names = ["North", "South", "East", "West"];
159 let x_labels = ["X0", "X1", "X2", "X3", "X4"];
160 let y_labels = ["Y0", "Y1", "Y2", "Y3", "Y4"];
161
162 for (region_idx, region_name) in region_names.iter().enumerate() {
163 for (y_idx, y_label) in y_labels.iter().enumerate() {
164 for (x_idx, x_label) in x_labels.iter().enumerate() {
165 regions.push(*region_name);
166 x_coords.push(*x_label);
167 y_coords.push(*y_label);
168
169 let intensity = match region_idx {
170 0 => (x_idx + y_idx * 5) as f64 * 4.0,
171 1 => {
172 let dx = x_idx as f64 - 2.0;
173 let dy = y_idx as f64 - 2.0;
174 100.0 - (dx * dx + dy * dy) * 4.0
175 }
176 2 => ((x_idx * x_idx + y_idx * y_idx) as f64).sqrt() * 10.0,
177 3 => x_idx.max(y_idx) as f64 * 20.0,
178 _ => 0.0,
179 };
180
181 intensities.push(intensity);
182 }
183 }
184 }
185
186 let heatmap_data = df! {
187 "region" => regions,
188 "x" => x_coords,
189 "y" => y_coords,
190 "intensity" => intensities,
191 }
192 .unwrap();
193
194 HeatMap::builder()
195 .data(&heatmap_data)
196 .x("x")
197 .y("y")
198 .z("intensity")
199 .facet("region")
200 .facet_config(&FacetConfig::new().rows(2).cols(2))
201 .plot_title(Text::from("Regional Heat Intensity Patterns").size(16))
202 .x_title(Text::from("X Coordinate"))
203 .y_title(Text::from("Y Coordinate"))
204 .build()
205 .plot();
206}
207
208fn histogram_example() {
209 let df = CsvReader::new("data/temperature_seasonal.csv")
210 .finish()
211 .unwrap();
212
213 let facet_config = FacetConfig::new().rows(2).cols(3);
214
215 Histogram::builder()
216 .data(&df)
217 .x("temperature")
218 .group("season")
219 .facet("city")
220 .facet_config(&facet_config)
221 .opacity(0.5)
222 .plot_title("Seasonal Temperature Distribution by City")
223 .x_title("Temperature (°C)")
224 .y_title("Frequency")
225 .build()
226 .plot();
227}
228
229fn lineplot_example() {
230 let dataset = create_lineplot_dataset();
231
232 let facet_config = FacetConfig::new().highlight_facet(true);
233
234 LinePlot::builder()
235 .data(&dataset)
236 .x("x")
237 .y("sine")
238 .facet("category")
239 .facet_config(&facet_config)
240 .plot_title(Text::from("Sine Wave Patterns by Amplitude Level"))
241 .x_title("x")
242 .y_title("sin(x)")
243 .width(2.5)
244 .color(Rgb(255, 69, 0))
245 .build()
246 .plot();
247}
248
249fn create_lineplot_dataset() -> DataFrame {
250 let x_values: Vec<f64> = (0..200)
251 .map(|i| {
252 let step = (2.0 * std::f64::consts::PI - 0.0) / 199.0;
253 0.0 + step * i as f64
254 })
255 .collect();
256
257 let mut category = Vec::new();
258 let mut amplitude = Vec::new();
259 let mut sine_values = Vec::new();
260
261 for cat in ["Low", "Medium", "High"].iter() {
262 let amp = match *cat {
263 "Low" => 0.5,
264 "Medium" => 1.0,
265 "High" => 1.5,
266 _ => 1.0,
267 };
268
269 for &x in &x_values {
270 category.push(cat.to_string());
271 amplitude.push(amp);
272 sine_values.push(amp * x.sin());
273 }
274 }
275
276 let x_repeated: Vec<f64> = x_values
277 .iter()
278 .cycle()
279 .take(x_values.len() * 3)
280 .copied()
281 .collect();
282
283 df![
284 "x" => &x_repeated,
285 "category" => &category,
286 "amplitude" => &litude,
287 "sine" => &sine_values,
288 ]
289 .unwrap()
290}
291
292fn mesh3d_example() {
293 let mut x_vals = Vec::new();
294 let mut y_vals = Vec::new();
295 let mut z_vals = Vec::new();
296 let mut surface_type = Vec::new();
297
298 let n = 25;
299
300 for surface in ["Gaussian", "Saddle", "Ripple"].iter() {
301 for i in 0..n {
302 for j in 0..n {
303 let x = (i as f64 / (n - 1) as f64) * 4.0 - 2.0;
304 let y = (j as f64 / (n - 1) as f64) * 4.0 - 2.0;
305
306 let z = match *surface {
307 "Gaussian" => (-0.5 * (x * x + y * y)).exp(),
308 "Saddle" => 0.3 * (x * x - y * y),
309 "Ripple" => 0.4 * ((x * 3.0).sin() + (y * 3.0).cos()),
310 _ => 0.0,
311 };
312
313 x_vals.push(x);
314 y_vals.push(y);
315 z_vals.push(z);
316 surface_type.push(surface.to_string());
317 }
318 }
319 }
320
321 let dataset = DataFrame::new(
322 x_vals.len(),
323 vec![
324 Column::new("x".into(), x_vals),
325 Column::new("y".into(), y_vals),
326 Column::new("z".into(), z_vals),
327 Column::new("surface_type".into(), surface_type),
328 ],
329 )
330 .unwrap();
331
332 let config = FacetConfig::new().cols(3).rows(1);
333
334 let lighting = Lighting::new().ambient(0.6).diffuse(0.8).specular(0.4);
335
336 Mesh3D::builder()
337 .data(&dataset)
338 .x("x")
339 .y("y")
340 .z("z")
341 .facet("surface_type")
342 .facet_config(&config)
343 .color(Rgb(100, 150, 200))
344 .lighting(&lighting)
345 .plot_title(
346 Text::from("Mathematical Surfaces Comparison")
347 .font("Arial")
348 .size(20),
349 )
350 .build()
351 .plot();
352}
353
354fn piechart_example() {
355 let dataset = CsvReader::new("data/industry_region.csv").finish().unwrap();
356
357 let facet_config = FacetConfig::new()
358 .cols(3)
359 .scales(FacetScales::Free)
360 .h_gap(0.08)
361 .v_gap(0.12)
362 .title_style(Text::from("").size(13).color(Rgb(60, 60, 60)));
363
364 PieChart::builder()
365 .data(&dataset)
366 .labels("category")
367 .facet("region")
368 .facet_config(&facet_config)
369 .colors(vec![
370 Rgb(192, 57, 43),
371 Rgb(39, 174, 96),
372 Rgb(41, 128, 185),
373 Rgb(243, 156, 18),
374 ])
375 .rotation(25.0)
376 .pull(0.02)
377 .plot_title(Text::from("Industry Analysis").size(18))
378 .build()
379 .plot();
380}
381
382fn sankeydiagram_example() {
383 let dataset = CsvReader::new("data/energy_transition.csv")
384 .finish()
385 .unwrap();
386
387 let facet_config = FacetConfig::new()
388 .cols(4)
389 .h_gap(0.06)
390 .title_style(Text::from("").size(11).color(Rgb(50, 50, 50)));
391
392 let node_colors = vec![
393 Rgb(64, 64, 64),
394 Rgb(100, 149, 237),
395 Rgb(139, 69, 19),
396 Rgb(255, 195, 0),
397 Rgb(135, 206, 250),
398 Rgb(65, 105, 225),
399 Rgb(220, 20, 60),
400 Rgb(34, 139, 34),
401 ];
402
403 let link_colors = vec![
404 Rgb(220, 220, 220),
405 Rgb(200, 220, 245),
406 Rgb(220, 200, 180),
407 Rgb(255, 240, 200),
408 Rgb(220, 240, 255),
409 Rgb(200, 220, 240),
410 ];
411
412 SankeyDiagram::builder()
413 .data(&dataset)
414 .sources("source")
415 .targets("target")
416 .values("value")
417 .facet("year")
418 .facet_config(&facet_config)
419 .node_colors(node_colors)
420 .link_colors(link_colors)
421 .arrangement(Arrangement::Perpendicular)
422 .plot_title(
423 Text::from("Energy Transition Timeline (2020-2023)")
424 .font("Arial")
425 .size(16),
426 )
427 .pad(18)
428 .thickness(22)
429 .build()
430 .plot();
431}
432
433fn scatterplot_example() {
434 let dataset = CsvReader::new("data/penguins.csv")
435 .finish()
436 .unwrap()
437 .lazy()
438 .select([
439 col("species"),
440 col("sex").alias("gender"),
441 col("bill_length_mm"),
442 col("bill_depth_mm"),
443 ])
444 .collect()
445 .unwrap();
446
447 let facet_config = FacetConfig::new()
448 .highlight_facet(true)
449 .unhighlighted_color(Rgb(220, 220, 220));
450
451 ScatterPlot::builder()
452 .data(&dataset)
453 .x("bill_length_mm")
454 .y("bill_depth_mm")
455 .group("gender")
456 .facet("species")
457 .facet_config(&facet_config)
458 .plot_title(Text::from("Penguin Bill Morphology with Gender Comparison"))
459 .x_title("bill length (mm)")
460 .y_title("bill depth (mm)")
461 .opacity(0.6)
462 .size(8)
463 .colors(vec![Rgb(128, 128, 128), Rgb(255, 0, 255), Rgb(0, 255, 255)])
464 .shapes(vec![Shape::Diamond, Shape::Circle, Shape::Square])
465 .legend_title("gender")
466 .build()
467 .plot();
468}
469
470fn scatter3d_example() {
471 let dataset = CsvReader::new("data/penguins.csv")
472 .finish()
473 .unwrap()
474 .lazy()
475 .select([
476 col("species"),
477 col("sex").alias("gender"),
478 col("bill_length_mm").cast(DataType::Float32),
479 col("flipper_length_mm").cast(DataType::Int16),
480 col("body_mass_g").cast(DataType::Int16),
481 ])
482 .collect()
483 .unwrap();
484
485 let facet_config = FacetConfig::new()
486 .cols(3)
487 .highlight_facet(true)
488 .unhighlighted_color(Rgb(220, 220, 220));
489
490 Scatter3dPlot::builder()
491 .data(&dataset)
492 .x("body_mass_g")
493 .y("flipper_length_mm")
494 .z("bill_length_mm")
495 .facet("species")
496 .facet_config(&facet_config)
497 .opacity(0.6)
498 .size(6)
499 .colors(vec![Rgb(178, 34, 34), Rgb(65, 105, 225), Rgb(255, 140, 0)])
500 .plot_title("Penguin Morphological Traits - 3D Faceted Analysis")
501 .build()
502 .plot();
503}
504
505fn scatterpolar_example() {
506 let dataset = CsvReader::new("data/wind_patterns.csv").finish().unwrap();
507
508 let facet_config = FacetConfig::new()
509 .highlight_facet(true)
510 .unhighlighted_color(Rgb(220, 220, 220))
511 .cols(3);
512
513 ScatterPolar::builder()
514 .data(&dataset)
515 .theta("angle")
516 .r("speed")
517 .group("time")
518 .facet("season")
519 .facet_config(&facet_config)
520 .plot_title(Text::from("Wind Patterns by Season and Time of Day"))
521 .mode(Mode::LinesMarkers)
522 .opacity(0.7)
523 .size(7)
524 .width(2.5)
525 .colors(vec![Rgb(255, 105, 180), Rgb(30, 144, 255)])
526 .shapes(vec![Shape::Circle, Shape::Diamond])
527 .lines(vec![Line::Solid, Line::DashDot])
528 .legend_title("time of day")
529 .build()
530 .plot();
531}
532
533fn timeseriesplot_example() {
534 let dataset = CsvReader::new("data/financial_timeseries.csv")
535 .finish()
536 .unwrap();
537
538 let facet_config = FacetConfig::new()
539 .highlight_facet(true)
540 .unhighlighted_color(Rgb(220, 220, 220));
541
542 TimeSeriesPlot::builder()
543 .data(&dataset)
544 .x("date")
545 .y("revenue")
546 .additional_series(vec!["costs"])
547 .facet("region")
548 .facet_config(&facet_config)
549 .plot_title(Text::from("Regional Financial Metrics"))
550 .x_title("Month")
551 .y_title("Amount ($)")
552 .legend_title("Metric")
553 .width(2.0)
554 .colors(vec![Rgb(255, 105, 180), Rgb(30, 144, 255)])
555 .lines(vec![Line::Solid, Line::Dash])
556 .build()
557 .plot();
558}
559
560fn surfaceplot_example() {
561 let n: usize = 50;
562 let x_base: Vec<f64> = (0..n)
563 .map(|i| {
564 let step = (5.0 - (-5.0)) / (n - 1) as f64;
565 -5.0 + step * i as f64
566 })
567 .collect();
568 let y_base: Vec<f64> = (0..n)
569 .map(|i| {
570 let step = (5.0 - (-5.0)) / (n - 1) as f64;
571 -5.0 + step * i as f64
572 })
573 .collect();
574
575 let mut x_all = Vec::new();
576 let mut y_all = Vec::new();
577 let mut z_all = Vec::new();
578 let mut category_all = Vec::new();
579
580 type SurfaceFunction = Box<dyn Fn(f64, f64) -> f64>;
581 let functions: Vec<(&str, SurfaceFunction)> = vec![
582 (
583 "Sine Wave",
584 Box::new(|xi: f64, yj: f64| (xi * xi + yj * yj).sqrt().sin()),
585 ),
586 ("Saddle", Box::new(|xi: f64, yj: f64| xi * xi - yj * yj)),
587 (
588 "Gaussian",
589 Box::new(|xi: f64, yj: f64| (-0.5 * (xi * xi + yj * yj)).exp()),
590 ),
591 ];
592
593 for (name, func) in &functions {
594 for &xi in x_base.iter() {
595 for &yj in y_base.iter() {
596 x_all.push(xi);
597 y_all.push(yj);
598 z_all.push(func(xi, yj));
599 category_all.push(*name);
600 }
601 }
602 }
603
604 let dataset = df![
605 "x" => &x_all,
606 "y" => &y_all,
607 "z" => &z_all,
608 "function" => &category_all,
609 ]
610 .unwrap();
611
612 SurfacePlot::builder()
613 .data(&dataset)
614 .x("x")
615 .y("y")
616 .z("z")
617 .facet("function")
618 .facet_config(&FacetConfig::new().cols(3).rows(1).h_gap(0.08).v_gap(0.12))
619 .plot_title(
620 Text::from("3D Mathematical Functions")
621 .font("Arial")
622 .size(20),
623 )
624 .color_scale(Palette::Viridis)
625 .opacity(0.9)
626 .build()
627 .plot();
628}