faceting/
faceting.rs

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(&regional_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    // Pattern 1: Gaussian Peak
88    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    // Pattern 2: Saddle Point
101    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    // Pattern 3: Ripple Effect
114    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    // Pattern 4: Paraboloid
128    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    // Pattern 5: Wave Interference
141    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    // Pattern 6: Diagonal Waves
154    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" => &amplitude,
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}