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
impl SubplotGrid
Sourcepub fn regular<'f1, 'f2>() -> SubplotGridRegularBuilder<'f1, 'f2>
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).

Examples found in repository?
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
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}Sourcepub fn irregular<'f1, 'f2>() -> SubplotGridIrregularBuilder<'f1, 'f2>
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 plotrow: 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).

Examples found in repository?
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
impl SubplotGrid
Sourcepub fn plot(&self)
pub fn plot(&self)
Display the subplot grid in the default browser or Jupyter notebook.
Examples found in repository?
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
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}Sourcepub fn write_html(&self, path: impl Into<String>)
pub fn write_html(&self, path: impl Into<String>)
Write the subplot grid to an HTML file.
Sourcepub fn to_inline_html(&self, plot_div_id: Option<&str>) -> String
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
impl Clone for SubplotGrid
Source§fn clone(&self) -> SubplotGrid
fn clone(&self) -> SubplotGrid
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Serialize for SubplotGrid
impl Serialize for SubplotGrid
Source§fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
Auto Trait Implementations§
impl Freeze for SubplotGrid
impl !RefUnwindSafe for SubplotGrid
impl !Send for SubplotGrid
impl !Sync for SubplotGrid
impl Unpin for SubplotGrid
impl UnsafeUnpin for SubplotGrid
impl !UnwindSafe for SubplotGrid
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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