Skip to main content

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(PlRefPath::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(
363        x_vals.len(),
364        vec![
365            Column::new("x".into(), x_vals),
366            Column::new("y".into(), y_vals),
367            Column::new("z".into(), z_vals),
368            Column::new("surface_type".into(), surface_type),
369        ],
370    )
371    .unwrap();
372
373    let config = FacetConfig::new().cols(3).rows(1);
374
375    let lighting = Lighting::new().ambient(0.6).diffuse(0.8).specular(0.4);
376
377    Mesh3D::builder()
378        .data(&dataset)
379        .x("x")
380        .y("y")
381        .z("z")
382        .facet("surface_type")
383        .facet_config(&config)
384        .color(Rgb(100, 150, 200))
385        .lighting(&lighting)
386        .plot_title(
387            Text::from("Mathematical Surfaces Comparison")
388                .font("Arial")
389                .size(20),
390        )
391        .build()
392        .plot();
393}
394
395fn piechart_example() {
396    let dataset = CsvReadOptions::default()
397        .with_has_header(true)
398        .try_into_reader_with_file_path(Some("data/industry_region.csv".into()))
399        .unwrap()
400        .finish()
401        .unwrap();
402
403    let facet_config = FacetConfig::new()
404        .cols(3)
405        .scales(FacetScales::Free)
406        .h_gap(0.08)
407        .v_gap(0.12)
408        .title_style(Text::from("").size(13).color(Rgb(60, 60, 60)));
409
410    PieChart::builder()
411        .data(&dataset)
412        .labels("category")
413        .facet("region")
414        .facet_config(&facet_config)
415        .colors(vec![
416            Rgb(192, 57, 43),
417            Rgb(39, 174, 96),
418            Rgb(41, 128, 185),
419            Rgb(243, 156, 18),
420        ])
421        .rotation(25.0)
422        .pull(0.02)
423        .plot_title(Text::from("Industry Analysis").size(18))
424        .build()
425        .plot();
426}
427
428fn sankeydiagram_example() {
429    let dataset = CsvReadOptions::default()
430        .with_has_header(true)
431        .try_into_reader_with_file_path(Some("data/energy_transition.csv".into()))
432        .unwrap()
433        .finish()
434        .unwrap();
435
436    let facet_config = FacetConfig::new()
437        .cols(4)
438        .h_gap(0.06)
439        .title_style(Text::from("").size(11).color(Rgb(50, 50, 50)));
440
441    let node_colors = vec![
442        Rgb(64, 64, 64),
443        Rgb(100, 149, 237),
444        Rgb(139, 69, 19),
445        Rgb(255, 195, 0),
446        Rgb(135, 206, 250),
447        Rgb(65, 105, 225),
448        Rgb(220, 20, 60),
449        Rgb(34, 139, 34),
450    ];
451
452    let link_colors = vec![
453        Rgb(220, 220, 220),
454        Rgb(200, 220, 245),
455        Rgb(220, 200, 180),
456        Rgb(255, 240, 200),
457        Rgb(220, 240, 255),
458        Rgb(200, 220, 240),
459    ];
460
461    SankeyDiagram::builder()
462        .data(&dataset)
463        .sources("source")
464        .targets("target")
465        .values("value")
466        .facet("year")
467        .facet_config(&facet_config)
468        .node_colors(node_colors)
469        .link_colors(link_colors)
470        .arrangement(Arrangement::Perpendicular)
471        .plot_title(
472            Text::from("Energy Transition Timeline (2020-2023)")
473                .font("Arial")
474                .size(16),
475        )
476        .pad(18)
477        .thickness(22)
478        .build()
479        .plot();
480}
481
482fn scatterplot_example() {
483    let dataset = LazyCsvReader::new(PlRefPath::new("data/penguins.csv"))
484        .finish()
485        .unwrap()
486        .select([
487            col("species"),
488            col("sex").alias("gender"),
489            col("bill_length_mm"),
490            col("bill_depth_mm"),
491        ])
492        .collect()
493        .unwrap();
494
495    let facet_config = FacetConfig::new()
496        .highlight_facet(true)
497        .unhighlighted_color(Rgb(220, 220, 220));
498
499    ScatterPlot::builder()
500        .data(&dataset)
501        .x("bill_length_mm")
502        .y("bill_depth_mm")
503        .group("gender")
504        .facet("species")
505        .facet_config(&facet_config)
506        .plot_title(Text::from("Penguin Bill Morphology with Gender Comparison"))
507        .x_title("bill length (mm)")
508        .y_title("bill depth (mm)")
509        .opacity(0.6)
510        .size(8)
511        .colors(vec![Rgb(128, 128, 128), Rgb(255, 0, 255), Rgb(0, 255, 255)])
512        .shapes(vec![Shape::Diamond, Shape::Circle, Shape::Square])
513        .legend_title("gender")
514        .build()
515        .plot();
516}
517
518fn scatter3d_example() {
519    let dataset = LazyCsvReader::new(PlRefPath::new("data/penguins.csv"))
520        .finish()
521        .unwrap()
522        .select([
523            col("species"),
524            col("sex").alias("gender"),
525            col("bill_length_mm").cast(DataType::Float32),
526            col("flipper_length_mm").cast(DataType::Int16),
527            col("body_mass_g").cast(DataType::Int16),
528        ])
529        .collect()
530        .unwrap();
531
532    let facet_config = FacetConfig::new()
533        .cols(3)
534        .highlight_facet(true)
535        .unhighlighted_color(Rgb(220, 220, 220));
536
537    Scatter3dPlot::builder()
538        .data(&dataset)
539        .x("body_mass_g")
540        .y("flipper_length_mm")
541        .z("bill_length_mm")
542        .facet("species")
543        .facet_config(&facet_config)
544        .opacity(0.6)
545        .size(6)
546        .colors(vec![Rgb(178, 34, 34), Rgb(65, 105, 225), Rgb(255, 140, 0)])
547        .plot_title("Penguin Morphological Traits - 3D Faceted Analysis")
548        .build()
549        .plot();
550}
551
552fn scatterpolar_example() {
553    let dataset = CsvReadOptions::default()
554        .with_has_header(true)
555        .try_into_reader_with_file_path(Some("data/wind_patterns.csv".into()))
556        .unwrap()
557        .finish()
558        .unwrap();
559
560    let facet_config = FacetConfig::new()
561        .highlight_facet(true)
562        .unhighlighted_color(Rgb(220, 220, 220))
563        .cols(3);
564
565    ScatterPolar::builder()
566        .data(&dataset)
567        .theta("angle")
568        .r("speed")
569        .group("time")
570        .facet("season")
571        .facet_config(&facet_config)
572        .plot_title(Text::from("Wind Patterns by Season and Time of Day"))
573        .mode(Mode::LinesMarkers)
574        .opacity(0.7)
575        .size(7)
576        .width(2.5)
577        .colors(vec![Rgb(255, 105, 180), Rgb(30, 144, 255)])
578        .shapes(vec![Shape::Circle, Shape::Diamond])
579        .lines(vec![Line::Solid, Line::DashDot])
580        .legend_title("time of day")
581        .build()
582        .plot();
583}
584
585fn timeseriesplot_example() {
586    let dataset = CsvReadOptions::default()
587        .with_has_header(true)
588        .try_into_reader_with_file_path(Some("data/financial_timeseries.csv".into()))
589        .unwrap()
590        .finish()
591        .unwrap();
592
593    let facet_config = FacetConfig::new()
594        .highlight_facet(true)
595        .unhighlighted_color(Rgb(220, 220, 220));
596
597    TimeSeriesPlot::builder()
598        .data(&dataset)
599        .x("date")
600        .y("revenue")
601        .additional_series(vec!["costs"])
602        .facet("region")
603        .facet_config(&facet_config)
604        .plot_title(Text::from("Regional Financial Metrics"))
605        .x_title("Month")
606        .y_title("Amount ($)")
607        .legend_title("Metric")
608        .width(2.0)
609        .with_shape(false)
610        .colors(vec![Rgb(255, 105, 180), Rgb(30, 144, 255)])
611        .lines(vec![Line::Solid, Line::Dash])
612        .build()
613        .plot();
614}
615
616fn surfaceplot_example() {
617    let n: usize = 50;
618    let x_base: Vec<f64> = (0..n)
619        .map(|i| {
620            let step = (5.0 - (-5.0)) / (n - 1) as f64;
621            -5.0 + step * i as f64
622        })
623        .collect();
624    let y_base: Vec<f64> = (0..n)
625        .map(|i| {
626            let step = (5.0 - (-5.0)) / (n - 1) as f64;
627            -5.0 + step * i as f64
628        })
629        .collect();
630
631    let mut x_all = Vec::new();
632    let mut y_all = Vec::new();
633    let mut z_all = Vec::new();
634    let mut category_all = Vec::new();
635
636    type SurfaceFunction = Box<dyn Fn(f64, f64) -> f64>;
637    let functions: Vec<(&str, SurfaceFunction)> = vec![
638        (
639            "Sine Wave",
640            Box::new(|xi: f64, yj: f64| (xi * xi + yj * yj).sqrt().sin()),
641        ),
642        ("Saddle", Box::new(|xi: f64, yj: f64| xi * xi - yj * yj)),
643        (
644            "Gaussian",
645            Box::new(|xi: f64, yj: f64| (-0.5 * (xi * xi + yj * yj)).exp()),
646        ),
647    ];
648
649    for (name, func) in &functions {
650        for &xi in x_base.iter() {
651            for &yj in y_base.iter() {
652                x_all.push(xi);
653                y_all.push(yj);
654                z_all.push(func(xi, yj));
655                category_all.push(*name);
656            }
657        }
658    }
659
660    let dataset = df![
661        "x" => &x_all,
662        "y" => &y_all,
663        "z" => &z_all,
664        "function" => &category_all,
665    ]
666    .unwrap();
667
668    SurfacePlot::builder()
669        .data(&dataset)
670        .x("x")
671        .y("y")
672        .z("z")
673        .facet("function")
674        .facet_config(&FacetConfig::new().cols(3).rows(1).h_gap(0.08).v_gap(0.12))
675        .plot_title(
676            Text::from("3D Mathematical Functions")
677                .font("Arial")
678                .size(20),
679        )
680        .color_scale(Palette::Viridis)
681        .opacity(0.9)
682        .build()
683        .plot();
684}