Skip to main content

plotly_faceting/
plotly_faceting.rs

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(&regional_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        // Normalize to [-1, 1]
113        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" => &amplitude,
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}