Rustic Data: Data Visualization with Plotters — Part 1 | by Mahmoud Harmouch | Jul, 2023


Grid, Axes, and Labels

Plotters Grid (Image by author)

In the world of data visualization, it’s crucial to have the flexibility to present a grid in a plot. The Plotters library empowers us to achieve this by enabling the mesh feature. By simply incorporating the statement chart.configure_mesh().draw()?; into our code, we can enhance the visual appeal and clarity of our plots.

evcxr_figure((640, 240), |root| {
let mut chart = ChartBuilder::on(&root)
.build_cartesian_2d(0f32..1f32, 0f32..1f32)?;

chart.configure_mesh().draw()?;

Ok(())
}).style("width:100%")

The line ChartBuilder::on(&root).build_cartesian_2d(0f32..1f32, 0f32..1f32)?; allows us to manually set the limits of the x-axis from 0 to 1 and the y-axis from 0 to 1. By specifying these ranges, we have precise control over the displayed region of our plot, ensuring that the most relevant data points are emphasized.

To enhance the clarity and understanding of our plots, it is essential to provide proper labels for the axes and a descriptive title. Let’s consider the following code snippet as an example:

evcxr_figure((640, 480), |root| {
let mut chart = ChartBuilder::on(&root)
.caption("Plot Demo", ("Arial", 20).into_font())
.x_label_area_size(50)
.y_label_area_size(50)
.build_cartesian_2d(0f32..1f32, 0f32..1f32)?;

chart.configure_mesh()
.x_desc("x = Array::range(1., 7., 0.1);")
.y_desc("y = f(x)")
.draw()?;

Ok(())
}).style("width: 60%")

Plotters labels (Image by author)

In this code, we have added the chart.configure_mesh().x_desc(“x = Array::range(1., 7., 1.);”).y_desc(“y = f(x)”).draw()?; statement to enrich our plot with meaningful annotations. By including x_desc(“x = Array::range(1., 7., 1.);”), we label the x-axis with a concise description of the data being plotted. Similarly, y_desc(“y = f(x)”) assigns a label to the y-axis, indicating the functional relationship. Furthermore, Caption(“Plot Demo”, (“Arial”, 20).into_font()) provides an informative title to give context to the plot. These elements collectively improve the interpretability of the visualization, ensuring that viewers can easily comprehend the purpose and content of the plots.

In addition to labels and titles, Plotters allows us to create a legend to distinguish between multiple curves within a plot. By passing an argument for the label parameter in the label function and subsequently calling the legend function, we can generate a legend. Consider the following code example:

evcxr_figure((640, 480), |root| {
let mut chart = ChartBuilder::on(&root)
.caption("Plot Demo", ("Arial", 20).into_font())
.x_label_area_size(50)
.y_label_area_size(50)
.build_cartesian_2d(1f32..7f32, 1f32..14f32)?;

let x = Array::range(1., 7., 0.1);

chart.configure_mesh()
.x_desc("x = Array::range(1., 7., 1.);")
.y_desc("y = f(x)")
.draw()?;

chart.draw_series(LineSeries::new(
x.iter().map(|x| (*x, *x)),
&RED
)).unwrap()
.label("y = x")
.legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &RED));

chart.draw_series(LineSeries::new(
x.iter().map(|x| (*x, *x * 2.0)),
&GREEN
)).unwrap()
.label("y = 2 * x")
.legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &GREEN));

chart.configure_series_labels()
.background_style(&WHITE)
.border_style(&BLACK)
.draw()?;

Ok(())
}).style("width: 60%")

A multiline plot with labels, legend, and grid (Image by author)

By executing this code, we create a legend that corresponds to the various curves in our plot. The legend() function automatically generates a legend based on the labels provided after calling the draw_series() function. It helps viewers identify and differentiate between the different functions being plotted. In conjunction with the grid, axis labels, and title, the legend enhances the overall readability and comprehension of the plot.

By default, the legend box is positioned at the middle right of the plot. However, if we prefer to change the location of the legend box, we can do so by specifying a SeriesLabelPosition position parameter within the position function. Let’s modify our code snippet accordingly:

evcxr_figure((640, 480), |root| {
let mut chart = ChartBuilder::on(&root)
.caption("Plot Demo", ("Arial", 20).into_font())
.x_label_area_size(50)
.y_label_area_size(50)
.build_cartesian_2d(1f32..7f32, 1f32..14f32)?;

let x = Array::range(1., 7., 0.1);

chart.configure_mesh()
.x_desc("x = Array::range(1., 7., 0.1);")
.y_desc("y = f(x)")
.draw()?;

chart.draw_series(LineSeries::new(
x.iter().map(|x| (*x, *x)),
&RED
)).unwrap()
.label("y = x")
.legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &RED));

chart.draw_series(LineSeries::new(
x.iter().map(|x| (*x, *x * 2.0)),
&GREEN
)).unwrap()
.label("y = 2 * x")
.legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &GREEN));

chart.configure_series_labels()
.position(SeriesLabelPosition::UpperMiddle)
.background_style(&WHITE)
.border_style(&BLACK)
.draw()?;

Ok(())
}).style("width: 60%")

A multiline plot with a legend positioned in the upper middle of the graph (Image by author)

By including the parameter position(SeriesLabelPosition::UpperMiddle) on the configure_series_labels function, we reposition the legend box to the upper middle of the plot. This allows us to fine-tune the placement of the legend, ensuring it does not interfere with the plotted curves or other annotations. The ability to customize the legend location adds to the versatility and aesthetics of our plot.

By understanding and utilizing these features in Plotters, we can create visually appealing and informative plots, customize axis limits, add labels and titles, incorporate legends, and save our visualizations as image files. These capabilities empower us to effectively communicate and present our data in a compelling and meaningful way.



Source link

Leave a Comment