faceting/
faceting.rs

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