Skip to main content

SubplotGrid

Struct SubplotGrid 

Source
pub struct SubplotGrid { /* private fields */ }
Expand description

A structure representing a subplot grid layout.

The SubplotGrid struct facilitates the creation of multi-plot layouts arranged in a grid configuration. Plots are automatically arranged in rows and columns in row-major order (left-to-right, top-to-bottom). Each subplot retains its own title, axis labels, and legend, providing flexibility for displaying multiple related visualizations in a single figure.

§Features

  • Automatic grid layout with configurable rows and columns
  • Individual subplot titles (extracted from plot titles)
  • Independent axis labels for each subplot
  • Configurable horizontal and vertical spacing
  • Overall figure title
  • Sparse grid support (fewer plots than grid capacity)

Implementations§

Source§

impl SubplotGrid

Source

pub fn regular<'f1, 'f2>() -> SubplotGridRegularBuilder<'f1, 'f2>

Creates a subplot grid layout.

Arranges plots in a row * column grid with automatic positioning. Plots are placed in row-major order (left-to-right, top-to-bottom). Each subplot retains its individual title (from the plot’s plot_title), axis labels, and legend.

§Arguments
  • plots - Vector of plot references to arrange in the grid. Plots are positioned in row-major order.
  • rows - Number of rows in the grid (default: 1).
  • cols - Number of columns in the grid (default: 1).
  • title - Overall title for the entire subplot figure (optional).
  • h_gap - Horizontal spacing between columns (range: 0.0 to 1.0, default: 0.1).
  • v_gap - Vertical spacing between rows (range: 0.0 to 1.0, default: 0.1).

Example

Examples found in repository?
examples/plotly_subplot_grid.rs (line 128)
15fn regular_grid_example() {
16    let dataset1 = CsvReader::new("data/animal_statistics.csv")
17        .finish()
18        .unwrap();
19
20    let plot1 = BarPlot::builder()
21        .data(&dataset1)
22        .labels("animal")
23        .values("value")
24        .orientation(Orientation::Vertical)
25        .group("gender")
26        .sort_groups_by(|a, b| a.len().cmp(&b.len()))
27        .error("error")
28        .colors(vec![Rgb(255, 127, 80), Rgb(64, 224, 208)])
29        .plot_title(Text::from("Bar Plot").x(-0.05).y(1.35).size(14))
30        .y_title(Text::from("value").x(-0.055).y(0.76))
31        .x_title(Text::from("animal").x(0.97).y(-0.2))
32        .legend(
33            &Legend::new()
34                .orientation(Orientation::Horizontal)
35                .x(0.4)
36                .y(1.2),
37        )
38        .build();
39
40    let dataset2 = CsvReader::new("data/penguins.csv")
41        .finish()
42        .unwrap()
43        .lazy()
44        .select([
45            col("species"),
46            col("sex").alias("gender"),
47            col("flipper_length_mm").cast(DataType::Int16),
48            col("body_mass_g").cast(DataType::Int16),
49        ])
50        .collect()
51        .unwrap();
52
53    let axis = Axis::new()
54        .show_line(true)
55        .tick_direction(TickDirection::OutSide)
56        .value_thousands(true);
57
58    let plot2 = ScatterPlot::builder()
59        .data(&dataset2)
60        .x("body_mass_g")
61        .y("flipper_length_mm")
62        .group("species")
63        .sort_groups_by(|a, b| {
64            if a.len() == b.len() {
65                a.cmp(b)
66            } else {
67                a.len().cmp(&b.len())
68            }
69        })
70        .opacity(0.5)
71        .size(12)
72        .colors(vec![Rgb(178, 34, 34), Rgb(65, 105, 225), Rgb(255, 140, 0)])
73        .shapes(vec![Shape::Circle, Shape::Square, Shape::Diamond])
74        .plot_title(Text::from("Scatter Plot").x(-0.075).y(1.35).size(14))
75        .x_title(Text::from("body mass (g)").y(-0.4))
76        .y_title(Text::from("flipper length (mm)").x(-0.078).y(0.5))
77        .legend_title("species")
78        .x_axis(&axis.clone().value_range(2500.0, 6500.0))
79        .y_axis(&axis.clone().value_range(170.0, 240.0))
80        .legend(&Legend::new().x(0.98).y(0.95))
81        .build();
82
83    let dataset3 = CsvReader::new("data/debilt_2023_temps.csv")
84        .has_header(true)
85        .try_parse_dates(true)
86        .finish()
87        .unwrap()
88        .lazy()
89        .with_columns(vec![
90            (col("tavg") / lit(10)).alias("avg"),
91            (col("tmin") / lit(10)).alias("min"),
92            (col("tmax") / lit(10)).alias("max"),
93        ])
94        .collect()
95        .unwrap();
96
97    let plot3 = TimeSeriesPlot::builder()
98        .data(&dataset3)
99        .x("date")
100        .y("avg")
101        .additional_series(vec!["min", "max"])
102        .colors(vec![Rgb(128, 128, 128), Rgb(0, 122, 255), Rgb(255, 128, 0)])
103        .lines(vec![Line::Solid, Line::Dot, Line::Dot])
104        .plot_title(Text::from("Time Series Plot").x(-0.05).y(1.35).size(14))
105        .y_title(Text::from("temperature (ºC)").x(-0.055).y(0.6))
106        .legend(&Legend::new().x(0.9).y(1.25))
107        .build();
108
109    let plot4 = BoxPlot::builder()
110        .data(&dataset2)
111        .labels("species")
112        .values("body_mass_g")
113        .orientation(Orientation::Vertical)
114        .group("gender")
115        .box_points(true)
116        .point_offset(-1.5)
117        .jitter(0.01)
118        .opacity(0.1)
119        .colors(vec![Rgb(0, 191, 255), Rgb(57, 255, 20), Rgb(255, 105, 180)])
120        .plot_title(Text::from("Box Plot").x(-0.075).y(1.35).size(14))
121        .x_title(Text::from("species").y(-0.3))
122        .y_title(Text::from("body mass (g)").x(-0.08).y(0.5))
123        .legend_title(Text::from("gender").size(12))
124        .y_axis(&Axis::new().value_thousands(true))
125        .legend(&Legend::new().x(1.0))
126        .build();
127
128    SubplotGrid::regular()
129        .plots(vec![&plot1, &plot2, &plot3, &plot4])
130        .rows(2)
131        .cols(2)
132        .v_gap(0.4)
133        .title(
134            Text::from("Regular Subplot Grid")
135                .size(16)
136                .font("Arial Black")
137                .y(0.95),
138        )
139        .build()
140        .plot();
141}
142
143fn irregular_grid_example() {
144    let dataset1 = CsvReader::new("data/penguins.csv")
145        .finish()
146        .unwrap()
147        .lazy()
148        .select([
149            col("species"),
150            col("sex").alias("gender"),
151            col("flipper_length_mm").cast(DataType::Int16),
152            col("body_mass_g").cast(DataType::Int16),
153        ])
154        .collect()
155        .unwrap();
156
157    let axis = Axis::new()
158        .show_line(true)
159        .show_grid(true)
160        .value_thousands(true)
161        .tick_direction(TickDirection::OutSide);
162
163    let plot1 = Histogram::builder()
164        .data(&dataset1)
165        .x("body_mass_g")
166        .group("species")
167        .opacity(0.5)
168        .colors(vec![Rgb(255, 165, 0), Rgb(147, 112, 219), Rgb(46, 139, 87)])
169        .plot_title(Text::from("Histogram").x(0.0).y(1.35).size(14))
170        .x_title(Text::from("body mass (g)").x(0.94).y(-0.35))
171        .y_title(Text::from("count").x(-0.062).y(0.83))
172        .x_axis(&axis)
173        .y_axis(&axis)
174        .legend_title(Text::from("species"))
175        .legend(&Legend::new().x(0.87).y(1.2))
176        .build();
177
178    let dataset2 = CsvReader::new("data/stock_prices.csv").finish().unwrap();
179
180    let increasing = Direction::new()
181        .line_color(Rgb(0, 200, 100))
182        .line_width(0.5);
183
184    let decreasing = Direction::new()
185        .line_color(Rgb(200, 50, 50))
186        .line_width(0.5);
187
188    let plot2 = CandlestickPlot::builder()
189        .data(&dataset2)
190        .dates("date")
191        .open("open")
192        .high("high")
193        .low("low")
194        .close("close")
195        .increasing(&increasing)
196        .decreasing(&decreasing)
197        .whisker_width(0.1)
198        .plot_title(Text::from("Candlestick").x(0.0).y(1.35).size(14))
199        .y_title(Text::from("price ($)").x(-0.06).y(0.76))
200        .y_axis(&Axis::new().show_axis(true).show_grid(true))
201        .build();
202
203    let dataset3 = CsvReader::new("data/heatmap.csv").finish().unwrap();
204
205    let plot3 = HeatMap::builder()
206        .data(&dataset3)
207        .x("x")
208        .y("y")
209        .z("z")
210        .color_bar(
211            &ColorBar::new()
212                .value_exponent(ValueExponent::None)
213                .separate_thousands(true)
214                .tick_length(5)
215                .tick_step(5000.0),
216        )
217        .plot_title(Text::from("Heat Map").x(0.0).y(1.35).size(14))
218        .color_scale(Palette::Viridis)
219        .build();
220
221    SubplotGrid::irregular()
222        .plots(vec![
223            (&plot1, 0, 0, 1, 1),
224            (&plot2, 0, 1, 1, 1),
225            (&plot3, 1, 0, 1, 2),
226        ])
227        .rows(2)
228        .cols(2)
229        .v_gap(0.35)
230        .h_gap(0.05)
231        .title(
232            Text::from("Irregular Subplot Grid")
233                .size(16)
234                .font("Arial Black")
235                .y(0.95),
236        )
237        .build()
238        .plot();
239}
240
241fn mixed_grid_example() {
242    // 2D cartesian scatter (baseline)
243    let penguins = CsvReader::new("data/penguins.csv")
244        .finish()
245        .unwrap()
246        .lazy()
247        .select([
248            col("species"),
249            col("bill_length_mm"),
250            col("flipper_length_mm"),
251            col("body_mass_g"),
252        ])
253        .collect()
254        .unwrap();
255
256    let scatter_2d = ScatterPlot::builder()
257        .data(&penguins)
258        .x("bill_length_mm")
259        .y("flipper_length_mm")
260        .group("species")
261        .opacity(0.65)
262        .size(10)
263        .plot_title(Text::from("Penguins 2D").y(1.3))
264        .build();
265
266    // 3D scene subplot
267    let scatter_3d = Scatter3dPlot::builder()
268        .data(&penguins)
269        .x("bill_length_mm")
270        .y("flipper_length_mm")
271        .z("body_mass_g")
272        .group("species")
273        .opacity(0.35)
274        .size(6)
275        .plot_title(Text::from("Penguins 3D").y(1.45))
276        .build();
277
278    // Polar subplot
279    let polar_df = CsvReader::new("data/product_comparison_polar.csv")
280        .finish()
281        .unwrap();
282
283    let polar = ScatterPolar::builder()
284        .data(&polar_df)
285        .theta("angle")
286        .r("score")
287        .group("product")
288        .mode(Mode::LinesMarkers)
289        .size(10)
290        .plot_title(Text::from("Product Comparison (Polar)").y(1.5).x(0.72))
291        .legend(&Legend::new().x(0.8))
292        .build();
293
294    // Domain-based subplot (Sankey)
295    let sankey_df = CsvReader::new("data/energy_transition.csv")
296        .finish()
297        .unwrap();
298
299    let sankey = SankeyDiagram::builder()
300        .data(&sankey_df)
301        .sources("source")
302        .targets("target")
303        .values("value")
304        .orientation(Orientation::Horizontal)
305        .arrangement(Arrangement::Freeform)
306        .plot_title(Text::from("Energy Flow").y(1.2))
307        .build();
308
309    // Mapbox subplot
310    let map_df = CsvReader::new("data/cities.csv").finish().unwrap();
311
312    let scatter_map = ScatterMap::builder()
313        .data(&map_df)
314        .latitude("latitude")
315        .longitude("longitude")
316        .group("city")
317        .zoom(4)
318        .center([50.0, 5.0])
319        .opacity(0.8)
320        .plot_title(Text::from("Cities (Mapbox)").y(1.2))
321        .build();
322
323    // Geo subplot
324    let geo_df = CsvReader::new("data/world_cities.csv").finish().unwrap();
325
326    let scatter_geo = ScatterGeo::builder()
327        .data(&geo_df)
328        .lat("lat")
329        .lon("lon")
330        .group("continent")
331        .mode(Mode::Markers)
332        .size(10)
333        .color(Rgb(255, 140, 0))
334        .shape(Shape::Circle)
335        .plot_title(Text::from("Global Cities (Geo)").x(0.65).y(1.2))
336        .legend(&Legend::new().x(0.8))
337        .build();
338
339    SubplotGrid::regular()
340        .plots(vec![
341            &scatter_2d,
342            &scatter_3d,
343            &polar,
344            &sankey,
345            &scatter_map,
346            &scatter_geo,
347        ])
348        .rows(2)
349        .cols(3)
350        .h_gap(0.12)
351        .v_gap(0.22)
352        .title(
353            Text::from("Mixed Subplot Grid")
354                .size(16)
355                .font("Arial Black")
356                .y(0.95),
357        )
358        .build()
359        .plot();
360}
More examples
Hide additional examples
examples/plotly_dimensions.rs (line 136)
7fn main() {
8    let penguins_dataset = CsvReader::new("data/penguins.csv")
9        .finish()
10        .unwrap()
11        .lazy()
12        .select([
13            col("species"),
14            col("sex").alias("gender"),
15            col("flipper_length_mm").cast(DataType::Int16),
16            col("body_mass_g").cast(DataType::Int16),
17        ])
18        .collect()
19        .unwrap();
20
21    let temperature_dataset = CsvReader::new("data/debilt_2023_temps.csv")
22        .has_header(true)
23        .try_parse_dates(true)
24        .finish()
25        .unwrap()
26        .lazy()
27        .with_columns(vec![
28            (col("tavg") / lit(10)).alias("tavg"),
29            (col("tmin") / lit(10)).alias("tmin"),
30            (col("tmax") / lit(10)).alias("tmax"),
31        ])
32        .collect()
33        .unwrap();
34
35    let animals_dataset = CsvReader::new("data/animal_statistics.csv")
36        .finish()
37        .unwrap();
38
39    let axis = Axis::new()
40        .show_line(true)
41        .tick_direction(TickDirection::OutSide)
42        .value_thousands(true);
43
44    let plot1 = TimeSeriesPlot::builder()
45        .data(&temperature_dataset)
46        .x("date")
47        .y("tavg")
48        .additional_series(vec!["tmin", "tmax"])
49        .colors(vec![Rgb(128, 128, 128), Rgb(0, 122, 255), Rgb(255, 128, 0)])
50        .lines(vec![Line::Solid, Line::Dot, Line::Dot])
51        .plot_title(
52            Text::from("De Bilt Temperature 2023")
53                .font("Arial Black")
54                .size(16),
55        )
56        .y_title(Text::from("temperature (°C)").size(13).x(-0.08))
57        // .legend_title(Text::from("Measure").size(12))
58        .legend(&Legend::new().x(0.1).y(0.9))
59        .build();
60
61    let plot2 = ScatterPlot::builder()
62        .data(&penguins_dataset)
63        .x("body_mass_g")
64        .y("flipper_length_mm")
65        .group("species")
66        .sort_groups_by(|a, b| {
67            if a.len() == b.len() {
68                a.cmp(b)
69            } else {
70                a.len().cmp(&b.len())
71            }
72        })
73        .opacity(0.6)
74        .size(10)
75        .colors(vec![Rgb(178, 34, 34), Rgb(65, 105, 225), Rgb(255, 140, 0)])
76        .shapes(vec![Shape::Circle, Shape::Square, Shape::Diamond])
77        .plot_title(
78            Text::from("Penguin Morphology")
79                .font("Arial Black")
80                .size(16),
81        )
82        .x_title(Text::from("body mass (g)").size(13))
83        .y_title(Text::from("flipper length (mm)").size(13).x(-0.11))
84        .legend_title(Text::from("Species").size(12))
85        .x_axis(&axis.clone().value_range(2500.0, 6500.0))
86        .y_axis(&axis.clone().value_range(170.0, 240.0))
87        .legend(&Legend::new().x(0.85).y(0.4))
88        .build();
89
90    let plot3 = BarPlot::builder()
91        .data(&animals_dataset)
92        .labels("animal")
93        .values("value")
94        .orientation(Orientation::Vertical)
95        .group("gender")
96        .sort_groups_by(|a, b| a.len().cmp(&b.len()))
97        .error("error")
98        .colors(vec![Rgb(255, 127, 80), Rgb(64, 224, 208)])
99        .plot_title(Text::from("Animal Statistics").font("Arial Black").size(16))
100        .x_title(Text::from("animal").size(13))
101        .y_title(Text::from("value").size(13))
102        .legend_title(Text::from("Gender").size(12))
103        .legend(
104            &Legend::new()
105                .orientation(Orientation::Horizontal)
106                .x(0.35)
107                .y(0.9),
108        )
109        .build();
110
111    let plot4 = BoxPlot::builder()
112        .data(&penguins_dataset)
113        .labels("species")
114        .values("body_mass_g")
115        .orientation(Orientation::Vertical)
116        .group("gender")
117        .box_points(true)
118        .point_offset(-1.5)
119        .jitter(0.01)
120        .opacity(0.15)
121        .colors(vec![Rgb(0, 191, 255), Rgb(57, 255, 20), Rgb(255, 105, 180)])
122        .plot_title(
123            Text::from("Body Mass Distribution")
124                .font("Arial Black")
125                .size(16),
126        )
127        .x_title(Text::from("species").size(13))
128        .y_title(Text::from("body mass (g)").size(13).x(-0.12))
129        .legend_title(Text::from("Gender").size(12))
130        .y_axis(&Axis::new().value_thousands(true))
131        .legend(&Legend::new().x(0.85).y(0.9))
132        .build();
133
134    let dimensions = Dimensions::new().width(1400).height(850).auto_size(false);
135
136    SubplotGrid::regular()
137        .plots(vec![&plot1, &plot2, &plot3, &plot4])
138        .rows(2)
139        .cols(2)
140        .v_gap(0.3)
141        .h_gap(0.2)
142        .dimensions(&dimensions)
143        .title(
144            Text::from("Scientific Data Visualization Dashboard")
145                .size(26)
146                .font("Arial Black"),
147        )
148        .build()
149        .plot();
150}
Source

pub fn irregular<'f1, 'f2>() -> SubplotGridIrregularBuilder<'f1, 'f2>

Creates an irregular grid subplot layout with custom row/column spanning.

Allows plots to span multiple rows and/or columns, enabling dashboard-style layouts and asymmetric grid arrangements. Each plot explicitly specifies its position and span.

§Arguments
  • plots - Vector of tuples (plot, row, col, rowspan, colspan) where:
    • plot: Reference to the plot
    • row: Starting row (0-indexed)
    • col: Starting column (0-indexed)
    • rowspan: Number of rows to span (minimum 1)
    • colspan: Number of columns to span (minimum 1)
  • rows - Total number of rows in the grid (default: 1).
  • cols - Total number of columns in the grid (default: 1).
  • title - Overall title for the subplot figure (optional).
  • h_gap - Horizontal spacing between columns (range: 0.0 to 1.0, default: 0.1).
  • v_gap - Vertical spacing between rows (range: 0.0 to 1.0, default: 0.1).

Example

Examples found in repository?
examples/plotly_subplot_grid.rs (line 221)
143fn irregular_grid_example() {
144    let dataset1 = CsvReader::new("data/penguins.csv")
145        .finish()
146        .unwrap()
147        .lazy()
148        .select([
149            col("species"),
150            col("sex").alias("gender"),
151            col("flipper_length_mm").cast(DataType::Int16),
152            col("body_mass_g").cast(DataType::Int16),
153        ])
154        .collect()
155        .unwrap();
156
157    let axis = Axis::new()
158        .show_line(true)
159        .show_grid(true)
160        .value_thousands(true)
161        .tick_direction(TickDirection::OutSide);
162
163    let plot1 = Histogram::builder()
164        .data(&dataset1)
165        .x("body_mass_g")
166        .group("species")
167        .opacity(0.5)
168        .colors(vec![Rgb(255, 165, 0), Rgb(147, 112, 219), Rgb(46, 139, 87)])
169        .plot_title(Text::from("Histogram").x(0.0).y(1.35).size(14))
170        .x_title(Text::from("body mass (g)").x(0.94).y(-0.35))
171        .y_title(Text::from("count").x(-0.062).y(0.83))
172        .x_axis(&axis)
173        .y_axis(&axis)
174        .legend_title(Text::from("species"))
175        .legend(&Legend::new().x(0.87).y(1.2))
176        .build();
177
178    let dataset2 = CsvReader::new("data/stock_prices.csv").finish().unwrap();
179
180    let increasing = Direction::new()
181        .line_color(Rgb(0, 200, 100))
182        .line_width(0.5);
183
184    let decreasing = Direction::new()
185        .line_color(Rgb(200, 50, 50))
186        .line_width(0.5);
187
188    let plot2 = CandlestickPlot::builder()
189        .data(&dataset2)
190        .dates("date")
191        .open("open")
192        .high("high")
193        .low("low")
194        .close("close")
195        .increasing(&increasing)
196        .decreasing(&decreasing)
197        .whisker_width(0.1)
198        .plot_title(Text::from("Candlestick").x(0.0).y(1.35).size(14))
199        .y_title(Text::from("price ($)").x(-0.06).y(0.76))
200        .y_axis(&Axis::new().show_axis(true).show_grid(true))
201        .build();
202
203    let dataset3 = CsvReader::new("data/heatmap.csv").finish().unwrap();
204
205    let plot3 = HeatMap::builder()
206        .data(&dataset3)
207        .x("x")
208        .y("y")
209        .z("z")
210        .color_bar(
211            &ColorBar::new()
212                .value_exponent(ValueExponent::None)
213                .separate_thousands(true)
214                .tick_length(5)
215                .tick_step(5000.0),
216        )
217        .plot_title(Text::from("Heat Map").x(0.0).y(1.35).size(14))
218        .color_scale(Palette::Viridis)
219        .build();
220
221    SubplotGrid::irregular()
222        .plots(vec![
223            (&plot1, 0, 0, 1, 1),
224            (&plot2, 0, 1, 1, 1),
225            (&plot3, 1, 0, 1, 2),
226        ])
227        .rows(2)
228        .cols(2)
229        .v_gap(0.35)
230        .h_gap(0.05)
231        .title(
232            Text::from("Irregular Subplot Grid")
233                .size(16)
234                .font("Arial Black")
235                .y(0.95),
236        )
237        .build()
238        .plot();
239}
Source§

impl SubplotGrid

Source

pub fn plot(&self)

Display the subplot grid in the default browser or Jupyter notebook.

Examples found in repository?
examples/plotly_subplot_grid.rs (line 140)
15fn regular_grid_example() {
16    let dataset1 = CsvReader::new("data/animal_statistics.csv")
17        .finish()
18        .unwrap();
19
20    let plot1 = BarPlot::builder()
21        .data(&dataset1)
22        .labels("animal")
23        .values("value")
24        .orientation(Orientation::Vertical)
25        .group("gender")
26        .sort_groups_by(|a, b| a.len().cmp(&b.len()))
27        .error("error")
28        .colors(vec![Rgb(255, 127, 80), Rgb(64, 224, 208)])
29        .plot_title(Text::from("Bar Plot").x(-0.05).y(1.35).size(14))
30        .y_title(Text::from("value").x(-0.055).y(0.76))
31        .x_title(Text::from("animal").x(0.97).y(-0.2))
32        .legend(
33            &Legend::new()
34                .orientation(Orientation::Horizontal)
35                .x(0.4)
36                .y(1.2),
37        )
38        .build();
39
40    let dataset2 = CsvReader::new("data/penguins.csv")
41        .finish()
42        .unwrap()
43        .lazy()
44        .select([
45            col("species"),
46            col("sex").alias("gender"),
47            col("flipper_length_mm").cast(DataType::Int16),
48            col("body_mass_g").cast(DataType::Int16),
49        ])
50        .collect()
51        .unwrap();
52
53    let axis = Axis::new()
54        .show_line(true)
55        .tick_direction(TickDirection::OutSide)
56        .value_thousands(true);
57
58    let plot2 = ScatterPlot::builder()
59        .data(&dataset2)
60        .x("body_mass_g")
61        .y("flipper_length_mm")
62        .group("species")
63        .sort_groups_by(|a, b| {
64            if a.len() == b.len() {
65                a.cmp(b)
66            } else {
67                a.len().cmp(&b.len())
68            }
69        })
70        .opacity(0.5)
71        .size(12)
72        .colors(vec![Rgb(178, 34, 34), Rgb(65, 105, 225), Rgb(255, 140, 0)])
73        .shapes(vec![Shape::Circle, Shape::Square, Shape::Diamond])
74        .plot_title(Text::from("Scatter Plot").x(-0.075).y(1.35).size(14))
75        .x_title(Text::from("body mass (g)").y(-0.4))
76        .y_title(Text::from("flipper length (mm)").x(-0.078).y(0.5))
77        .legend_title("species")
78        .x_axis(&axis.clone().value_range(2500.0, 6500.0))
79        .y_axis(&axis.clone().value_range(170.0, 240.0))
80        .legend(&Legend::new().x(0.98).y(0.95))
81        .build();
82
83    let dataset3 = CsvReader::new("data/debilt_2023_temps.csv")
84        .has_header(true)
85        .try_parse_dates(true)
86        .finish()
87        .unwrap()
88        .lazy()
89        .with_columns(vec![
90            (col("tavg") / lit(10)).alias("avg"),
91            (col("tmin") / lit(10)).alias("min"),
92            (col("tmax") / lit(10)).alias("max"),
93        ])
94        .collect()
95        .unwrap();
96
97    let plot3 = TimeSeriesPlot::builder()
98        .data(&dataset3)
99        .x("date")
100        .y("avg")
101        .additional_series(vec!["min", "max"])
102        .colors(vec![Rgb(128, 128, 128), Rgb(0, 122, 255), Rgb(255, 128, 0)])
103        .lines(vec![Line::Solid, Line::Dot, Line::Dot])
104        .plot_title(Text::from("Time Series Plot").x(-0.05).y(1.35).size(14))
105        .y_title(Text::from("temperature (ºC)").x(-0.055).y(0.6))
106        .legend(&Legend::new().x(0.9).y(1.25))
107        .build();
108
109    let plot4 = BoxPlot::builder()
110        .data(&dataset2)
111        .labels("species")
112        .values("body_mass_g")
113        .orientation(Orientation::Vertical)
114        .group("gender")
115        .box_points(true)
116        .point_offset(-1.5)
117        .jitter(0.01)
118        .opacity(0.1)
119        .colors(vec![Rgb(0, 191, 255), Rgb(57, 255, 20), Rgb(255, 105, 180)])
120        .plot_title(Text::from("Box Plot").x(-0.075).y(1.35).size(14))
121        .x_title(Text::from("species").y(-0.3))
122        .y_title(Text::from("body mass (g)").x(-0.08).y(0.5))
123        .legend_title(Text::from("gender").size(12))
124        .y_axis(&Axis::new().value_thousands(true))
125        .legend(&Legend::new().x(1.0))
126        .build();
127
128    SubplotGrid::regular()
129        .plots(vec![&plot1, &plot2, &plot3, &plot4])
130        .rows(2)
131        .cols(2)
132        .v_gap(0.4)
133        .title(
134            Text::from("Regular Subplot Grid")
135                .size(16)
136                .font("Arial Black")
137                .y(0.95),
138        )
139        .build()
140        .plot();
141}
142
143fn irregular_grid_example() {
144    let dataset1 = CsvReader::new("data/penguins.csv")
145        .finish()
146        .unwrap()
147        .lazy()
148        .select([
149            col("species"),
150            col("sex").alias("gender"),
151            col("flipper_length_mm").cast(DataType::Int16),
152            col("body_mass_g").cast(DataType::Int16),
153        ])
154        .collect()
155        .unwrap();
156
157    let axis = Axis::new()
158        .show_line(true)
159        .show_grid(true)
160        .value_thousands(true)
161        .tick_direction(TickDirection::OutSide);
162
163    let plot1 = Histogram::builder()
164        .data(&dataset1)
165        .x("body_mass_g")
166        .group("species")
167        .opacity(0.5)
168        .colors(vec![Rgb(255, 165, 0), Rgb(147, 112, 219), Rgb(46, 139, 87)])
169        .plot_title(Text::from("Histogram").x(0.0).y(1.35).size(14))
170        .x_title(Text::from("body mass (g)").x(0.94).y(-0.35))
171        .y_title(Text::from("count").x(-0.062).y(0.83))
172        .x_axis(&axis)
173        .y_axis(&axis)
174        .legend_title(Text::from("species"))
175        .legend(&Legend::new().x(0.87).y(1.2))
176        .build();
177
178    let dataset2 = CsvReader::new("data/stock_prices.csv").finish().unwrap();
179
180    let increasing = Direction::new()
181        .line_color(Rgb(0, 200, 100))
182        .line_width(0.5);
183
184    let decreasing = Direction::new()
185        .line_color(Rgb(200, 50, 50))
186        .line_width(0.5);
187
188    let plot2 = CandlestickPlot::builder()
189        .data(&dataset2)
190        .dates("date")
191        .open("open")
192        .high("high")
193        .low("low")
194        .close("close")
195        .increasing(&increasing)
196        .decreasing(&decreasing)
197        .whisker_width(0.1)
198        .plot_title(Text::from("Candlestick").x(0.0).y(1.35).size(14))
199        .y_title(Text::from("price ($)").x(-0.06).y(0.76))
200        .y_axis(&Axis::new().show_axis(true).show_grid(true))
201        .build();
202
203    let dataset3 = CsvReader::new("data/heatmap.csv").finish().unwrap();
204
205    let plot3 = HeatMap::builder()
206        .data(&dataset3)
207        .x("x")
208        .y("y")
209        .z("z")
210        .color_bar(
211            &ColorBar::new()
212                .value_exponent(ValueExponent::None)
213                .separate_thousands(true)
214                .tick_length(5)
215                .tick_step(5000.0),
216        )
217        .plot_title(Text::from("Heat Map").x(0.0).y(1.35).size(14))
218        .color_scale(Palette::Viridis)
219        .build();
220
221    SubplotGrid::irregular()
222        .plots(vec![
223            (&plot1, 0, 0, 1, 1),
224            (&plot2, 0, 1, 1, 1),
225            (&plot3, 1, 0, 1, 2),
226        ])
227        .rows(2)
228        .cols(2)
229        .v_gap(0.35)
230        .h_gap(0.05)
231        .title(
232            Text::from("Irregular Subplot Grid")
233                .size(16)
234                .font("Arial Black")
235                .y(0.95),
236        )
237        .build()
238        .plot();
239}
240
241fn mixed_grid_example() {
242    // 2D cartesian scatter (baseline)
243    let penguins = CsvReader::new("data/penguins.csv")
244        .finish()
245        .unwrap()
246        .lazy()
247        .select([
248            col("species"),
249            col("bill_length_mm"),
250            col("flipper_length_mm"),
251            col("body_mass_g"),
252        ])
253        .collect()
254        .unwrap();
255
256    let scatter_2d = ScatterPlot::builder()
257        .data(&penguins)
258        .x("bill_length_mm")
259        .y("flipper_length_mm")
260        .group("species")
261        .opacity(0.65)
262        .size(10)
263        .plot_title(Text::from("Penguins 2D").y(1.3))
264        .build();
265
266    // 3D scene subplot
267    let scatter_3d = Scatter3dPlot::builder()
268        .data(&penguins)
269        .x("bill_length_mm")
270        .y("flipper_length_mm")
271        .z("body_mass_g")
272        .group("species")
273        .opacity(0.35)
274        .size(6)
275        .plot_title(Text::from("Penguins 3D").y(1.45))
276        .build();
277
278    // Polar subplot
279    let polar_df = CsvReader::new("data/product_comparison_polar.csv")
280        .finish()
281        .unwrap();
282
283    let polar = ScatterPolar::builder()
284        .data(&polar_df)
285        .theta("angle")
286        .r("score")
287        .group("product")
288        .mode(Mode::LinesMarkers)
289        .size(10)
290        .plot_title(Text::from("Product Comparison (Polar)").y(1.5).x(0.72))
291        .legend(&Legend::new().x(0.8))
292        .build();
293
294    // Domain-based subplot (Sankey)
295    let sankey_df = CsvReader::new("data/energy_transition.csv")
296        .finish()
297        .unwrap();
298
299    let sankey = SankeyDiagram::builder()
300        .data(&sankey_df)
301        .sources("source")
302        .targets("target")
303        .values("value")
304        .orientation(Orientation::Horizontal)
305        .arrangement(Arrangement::Freeform)
306        .plot_title(Text::from("Energy Flow").y(1.2))
307        .build();
308
309    // Mapbox subplot
310    let map_df = CsvReader::new("data/cities.csv").finish().unwrap();
311
312    let scatter_map = ScatterMap::builder()
313        .data(&map_df)
314        .latitude("latitude")
315        .longitude("longitude")
316        .group("city")
317        .zoom(4)
318        .center([50.0, 5.0])
319        .opacity(0.8)
320        .plot_title(Text::from("Cities (Mapbox)").y(1.2))
321        .build();
322
323    // Geo subplot
324    let geo_df = CsvReader::new("data/world_cities.csv").finish().unwrap();
325
326    let scatter_geo = ScatterGeo::builder()
327        .data(&geo_df)
328        .lat("lat")
329        .lon("lon")
330        .group("continent")
331        .mode(Mode::Markers)
332        .size(10)
333        .color(Rgb(255, 140, 0))
334        .shape(Shape::Circle)
335        .plot_title(Text::from("Global Cities (Geo)").x(0.65).y(1.2))
336        .legend(&Legend::new().x(0.8))
337        .build();
338
339    SubplotGrid::regular()
340        .plots(vec![
341            &scatter_2d,
342            &scatter_3d,
343            &polar,
344            &sankey,
345            &scatter_map,
346            &scatter_geo,
347        ])
348        .rows(2)
349        .cols(3)
350        .h_gap(0.12)
351        .v_gap(0.22)
352        .title(
353            Text::from("Mixed Subplot Grid")
354                .size(16)
355                .font("Arial Black")
356                .y(0.95),
357        )
358        .build()
359        .plot();
360}
More examples
Hide additional examples
examples/plotly_dimensions.rs (line 149)
7fn main() {
8    let penguins_dataset = CsvReader::new("data/penguins.csv")
9        .finish()
10        .unwrap()
11        .lazy()
12        .select([
13            col("species"),
14            col("sex").alias("gender"),
15            col("flipper_length_mm").cast(DataType::Int16),
16            col("body_mass_g").cast(DataType::Int16),
17        ])
18        .collect()
19        .unwrap();
20
21    let temperature_dataset = CsvReader::new("data/debilt_2023_temps.csv")
22        .has_header(true)
23        .try_parse_dates(true)
24        .finish()
25        .unwrap()
26        .lazy()
27        .with_columns(vec![
28            (col("tavg") / lit(10)).alias("tavg"),
29            (col("tmin") / lit(10)).alias("tmin"),
30            (col("tmax") / lit(10)).alias("tmax"),
31        ])
32        .collect()
33        .unwrap();
34
35    let animals_dataset = CsvReader::new("data/animal_statistics.csv")
36        .finish()
37        .unwrap();
38
39    let axis = Axis::new()
40        .show_line(true)
41        .tick_direction(TickDirection::OutSide)
42        .value_thousands(true);
43
44    let plot1 = TimeSeriesPlot::builder()
45        .data(&temperature_dataset)
46        .x("date")
47        .y("tavg")
48        .additional_series(vec!["tmin", "tmax"])
49        .colors(vec![Rgb(128, 128, 128), Rgb(0, 122, 255), Rgb(255, 128, 0)])
50        .lines(vec![Line::Solid, Line::Dot, Line::Dot])
51        .plot_title(
52            Text::from("De Bilt Temperature 2023")
53                .font("Arial Black")
54                .size(16),
55        )
56        .y_title(Text::from("temperature (°C)").size(13).x(-0.08))
57        // .legend_title(Text::from("Measure").size(12))
58        .legend(&Legend::new().x(0.1).y(0.9))
59        .build();
60
61    let plot2 = ScatterPlot::builder()
62        .data(&penguins_dataset)
63        .x("body_mass_g")
64        .y("flipper_length_mm")
65        .group("species")
66        .sort_groups_by(|a, b| {
67            if a.len() == b.len() {
68                a.cmp(b)
69            } else {
70                a.len().cmp(&b.len())
71            }
72        })
73        .opacity(0.6)
74        .size(10)
75        .colors(vec![Rgb(178, 34, 34), Rgb(65, 105, 225), Rgb(255, 140, 0)])
76        .shapes(vec![Shape::Circle, Shape::Square, Shape::Diamond])
77        .plot_title(
78            Text::from("Penguin Morphology")
79                .font("Arial Black")
80                .size(16),
81        )
82        .x_title(Text::from("body mass (g)").size(13))
83        .y_title(Text::from("flipper length (mm)").size(13).x(-0.11))
84        .legend_title(Text::from("Species").size(12))
85        .x_axis(&axis.clone().value_range(2500.0, 6500.0))
86        .y_axis(&axis.clone().value_range(170.0, 240.0))
87        .legend(&Legend::new().x(0.85).y(0.4))
88        .build();
89
90    let plot3 = BarPlot::builder()
91        .data(&animals_dataset)
92        .labels("animal")
93        .values("value")
94        .orientation(Orientation::Vertical)
95        .group("gender")
96        .sort_groups_by(|a, b| a.len().cmp(&b.len()))
97        .error("error")
98        .colors(vec![Rgb(255, 127, 80), Rgb(64, 224, 208)])
99        .plot_title(Text::from("Animal Statistics").font("Arial Black").size(16))
100        .x_title(Text::from("animal").size(13))
101        .y_title(Text::from("value").size(13))
102        .legend_title(Text::from("Gender").size(12))
103        .legend(
104            &Legend::new()
105                .orientation(Orientation::Horizontal)
106                .x(0.35)
107                .y(0.9),
108        )
109        .build();
110
111    let plot4 = BoxPlot::builder()
112        .data(&penguins_dataset)
113        .labels("species")
114        .values("body_mass_g")
115        .orientation(Orientation::Vertical)
116        .group("gender")
117        .box_points(true)
118        .point_offset(-1.5)
119        .jitter(0.01)
120        .opacity(0.15)
121        .colors(vec![Rgb(0, 191, 255), Rgb(57, 255, 20), Rgb(255, 105, 180)])
122        .plot_title(
123            Text::from("Body Mass Distribution")
124                .font("Arial Black")
125                .size(16),
126        )
127        .x_title(Text::from("species").size(13))
128        .y_title(Text::from("body mass (g)").size(13).x(-0.12))
129        .legend_title(Text::from("Gender").size(12))
130        .y_axis(&Axis::new().value_thousands(true))
131        .legend(&Legend::new().x(0.85).y(0.9))
132        .build();
133
134    let dimensions = Dimensions::new().width(1400).height(850).auto_size(false);
135
136    SubplotGrid::regular()
137        .plots(vec![&plot1, &plot2, &plot3, &plot4])
138        .rows(2)
139        .cols(2)
140        .v_gap(0.3)
141        .h_gap(0.2)
142        .dimensions(&dimensions)
143        .title(
144            Text::from("Scientific Data Visualization Dashboard")
145                .size(26)
146                .font("Arial Black"),
147        )
148        .build()
149        .plot();
150}
Source

pub fn write_html(&self, path: impl Into<String>)

Write the subplot grid to an HTML file.

Source

pub fn to_json(&self) -> Result<String, Error>

Serialize the subplot grid to a JSON string.

Source

pub fn to_html(&self) -> String

Render the subplot grid as a standalone HTML string.

Source

pub fn to_inline_html(&self, plot_div_id: Option<&str>) -> String

Render the subplot grid as an inline HTML snippet (no DOCTYPE/head).

Trait Implementations§

Source§

impl Clone for SubplotGrid

Source§

fn clone(&self) -> SubplotGrid

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Serialize for SubplotGrid

Source§

fn serialize<S>( &self, serializer: S, ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Key for T
where T: Clone,

Source§

fn align() -> usize

The alignment necessary for the key. Must return a power of two.
Source§

fn size(&self) -> usize

The size of the key in bytes.
Source§

unsafe fn init(&self, ptr: *mut u8)

Initialize the key in the given memory location. Read more
Source§

unsafe fn get<'a>(ptr: *const u8) -> &'a T

Get a reference to the key from the given memory location. Read more
Source§

unsafe fn drop_in_place(ptr: *mut u8)

Drop the key in place. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Serialize for T
where T: Serialize + ?Sized,

Source§

fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>

Source§

fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> PlanCallbackArgs for T

Source§

impl<T> PlanCallbackOut for T