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