Rustic Data: Data Visualization with Plotters — Part 1

Rustic Data: Data Visualization with Plotters — Part 1

A detailed guide on how to transform raw numbers into stunning graphs in Rust

Various Plotters Features (Image by author)

TLDR;

Plotters is a popular Rust library for creating data visualizations. It provides a wide range of tools and functions to help you create high-quality graphs, charts, and other visualizations. This article is part 1 of a series of articles that focuses on the aesthetic aspects of visualizations prepared with Plotters. From changing the color scheme to adding annotations, you will learn how to customize the cosmetic aspects of the Plotter’s visualizations.

By the end of this article, you will have a rock-solid understanding of how to use the Plotters library to create professional-looking visualizations that will captivate your audience. The Ndarray library will also come in handy throughout this article as we explore the use of various tools and methods for data manipulation. So, whether you’re an amateur or seasoned Rust programmer, reading through this article is imperative if creating informative yet aesthetically pleasing visualizations with Plotters piques your interest.

Note: This article assumes that you have a fairly basic understanding of the Rust programming language.The notebook named 6-plotters-tutorial-part-1.ipynb was developed for this article which can be found in the following repository:

GitHub – wiseaidev/rust-data-analysis: The ultimate data analysis with Rust course.

Table of Contents(TOC)

TLDR;
Table of Contents(TOC)
Who Is This Article For?
What is Plotters?
Plotters Advantages
Setting up Plotters
Single-Line Plots
Multiline Plots
Grid, Axes, and Labels
Colors and Markers
Subplots
Error Bars
Scatter Plot
Histogram
Conclusion
Closing Note
Resources

Who Is This Article For?

Photo by Myriam Jessier on Unsplash

For those interested in crafting intuitive data visualizations in Rust, this article is a must-read. Whether you’re an experienced data scientist or just starting out, the Plotters crate in Rust can help you create captivating and eye-catching visuals that are sure to impress your audience. With basic knowledge of Rust programming under your belt, getting started has never been easier.

The Plotters crate packs immense power when it comes to creating stunning and effective visualizations quickly and effortlessly — perfect for personal projects as well as professional ones. It’s a tool that will enable you to generate high-quality graphics capable of communicating complex information effectively.

If taking your visualization skills up another notch sounds appealing, then look no further than this piece! The clear explanations coupled with helpful graphs make following along simple while step-by-step instructions ensure quick progress towards producing breathtaking visuals using the Plotters crate.

What is Plotters?

Photo by Stephen Phillips – Hostreviews.co.uk on Unsplash

Plotters is a robust and adaptable Rust crate that empowers developers, like you, to create stunning visualizations with ease. Its versatility allows for the creation of various plots, including line, scatter, and histograms while offering high flexibility in styling options and customized annotation.

This all-in-one tool enables developers to define any type of visualization needed — making it an indispensable asset for data analysis tasks. One notable feature is its support for interactive interfaces which makes generating static graphics possible as well as creating web-based applications effortlessly. This capability facilitates easy exploration of datasets leading to diverse plot types suitable for machine learning or data science projects.

Additionally, Plotters integrates seamlessly into popular development environments like Jupyter Notebook while supporting advanced packages dedicated solely towards enhancing your data visualization experience- providing more reasons why this package should be part of every developer’s toolkit!

Whether you are just starting out on your journey or have been analyzing complex data sets already — Plotters offers unparalleled adaptability coupled with user-friendliness; truly deserving recognition among top-tier tools available today!

Plotters Advantages

Photo by UX Indonesia on Unsplash

Data visualization is a crucial aspect of data analysis, and the Plotters library provides several benefits to simplify the process. One significant advantage that sets it apart from other options is its user-friendly nature. The integration with common data analytics crates like Ndarray makes it effortless to use alongside familiar structures.

Another noteworthy benefit of using this open-source tool lies in its cost-effectiveness; developers and analysts can utilize the library without any charges or limitations on usage rights. Additionally, anyone interested in contributing towards improving the software may do so as part of a community effort.

Furthermore, being open source means quick online support from fellow members worldwide via various platforms such as forums such as stackoverflow— making problem-solving efficient!

Setting up Plotters

To fully utilize the capabilities of Plotters, it’s crucial to ensure that we have set up our environment correctly. The library offers a broad range of plot types such as line charts, scatter plots, histograms, and pie plots; however, without proper setup, these features remain inaccessible. Fortunately for us all, setting up Plotters is an effortless process — simply run a command in your Jupyter Notebook and you’re good to go!

:dep plotters = { version = “^0.3.5”, default_features = false, features = [“evcxr”, “all_series”, “all_elements”] }

Once imported into your project workspace or notebook session, Plotters allows you to explore its vast array of customization options tailored specifically to your needs- whether simple or complex plots are required.

Single-Line Plots

A linear single-line plot (Image by author)

Line plots are a fundamental visualization tool in the Plotters library that allows us to represent data points connected by straight lines. Throughout this section, we will explore the concept of single-line plots, which involve using the LineSeries struct to create visualizations with a single line.

The LineSeries struct in Plotters is highly utilized for visualizing data, especially in the creation of single-line plots. Such graphs are perfect for illustrating correlations between two variables or highlighting patterns within time series data.

To create a one-dimensional plot through Plotters, start by importing the library and utilizing its draw_series function along with the LineSeries struct to sketch your line chart with an assigned dataset. For instance, if we aim to illustrate sales figures per month via a straightforward graph, here’s how you can use the draw_series function:

evcxr_figure((640, 240), |root| {
let mut chart = ChartBuilder::on(&root)
.build_cartesian_2d(0f32..5f32, 0f32..5f32)?;
let x_axis = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
chart.draw_series(LineSeries::new(
x_axis.map(|x| (x, x)),
&RED,
))?;
Ok(())
}).style(“width:100%”)

In the code above, we have an array x representing both coordinates, x, and y. Moving on, let us examine a different instance where we utilize an Ndarray array to represent the data for the single-line plot:

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

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

chart.draw_series(LineSeries::new(
x_axis.into_raw_vec().into_iter().map(|x| (x, x)),
&RED,
))?;
Ok(())
}).style(“width:100%”)

Moving on, let’s visualize a quadratic graph represented by the equation y = f(x) = x³. Here’s the corresponding code:

let points_coordinates: Vec<(f32, f32)> = {
let x_axis = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let quadratic: Vec<f32> = x_axis.iter().map(|x| i32::pow(*x as i32, 3) as f32).collect::<Vec<f32>>();
x_axis.into_iter().zip(quadratic).collect()
};
points_coordinates

// Output

// [(1.0, 1.0), (2.0, 8.0), (3.0, 27.0), (4.0, 64.0), (5.0, 125.0), (6.0, 216.0)]

Now, we need to plot this vector like this:

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

chart.draw_series(LineSeries::new(
points_coordinates.iter().map(|(x, y)| (*x, *y)),
&RED,
))?;
Ok(())
}).style(“width:100%”)A cubic function plot (Image by author)

In summary, line plots in Plotters offer a commanding approach to illustrating correlations and tendencies within datasets. We can craft informative yet captivating representations of our information by utilizing the LineSeries struct while manipulating x-values and y-values arrays/vectors. Whether you’re delving into scientific research findings or analyzing business metrics, these line plots are indispensable tools for exploring your dataset further while effectively communicating its insights with others.

Multiline Plots

A Multiline plot (Image by author)

Plotters provides an exceptional capability to display multiple plots in a single output, which empowers us to present numerous curves concurrently on the same visualization. This remarkable attribute facilitates effortless comparison and analysis of data sets. To delve deeper into this notion, let’s examine an illustrative example:

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

chart.draw_series(LineSeries::new(
linear_coordinates.iter().map(|(x, y)| (*x, *y)),
&RED,
))?;

chart.draw_series(LineSeries::new(
quadratic_coordinates.iter().map(|(x, y)| (*x, *y)),
&GREEN,
))?;

chart.draw_series(LineSeries::new(
cubic_coordinates.iter().map(|(x, y)| (*x, *y)),
&BLUE,
))?;

Ok(())
}).style(“width:100%”)

By utilizing the code snippet provided, we can generate numerous curves with ease. This is achieved by invoking the draw_series function multiple times and defining x-values from an array coupled with corresponding y-values derived from different mathematical expressions. Upon executing this code, a comprehensive graph showcasing all of these plotted curves will be displayed for observation purposes.

Let us delve into yet another example that demonstrates the adaptability of multi-line graphs. Observe the following code snippet:

let points_coordinates: Vec<(f32, f32)> = {
let x_y_axes = array!([[1., 2., 3., 4.], [1., 2., 3., 4.]]);
let x_axis: Vec<f32> = x_y_axes.slice(s![0, 0, ..]).to_vec();
let y_axis: Vec<f32> = x_y_axes.slice(s![0, 1, ..]).to_vec();
x_axis.into_iter().zip(y_axis).collect()
};

// [(1.0, 1.0), (2.0, 2.0), (3.0, 3.0), (4.0, 4.0)]

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

chart.draw_series(LineSeries::new(
points_coordinates.iter().map(|(x, y)| (*x, *y)),
&RED,
))?;

Ok(())
}).style(“width:100%”)

The code snippet at hand involves an Ndarray array x with two dimensions, holding distinct data sets. Each row denotes unique values. When the draw_series function is called on the entire array, Plotters perceives it as multiple curves to be plotted concurrently. The outcome exhibits both datasets side by side for easy comparison and analysis of their patterns, trends, or any other noteworthy features in an intuitive manner that enables us to draw meaningful conclusions from them visually without much effort involved.

To showcase the adaptability of multiline graphs, we can create a visual representation using arbitrary data. Observe this code snippet as an illustration:

let random_samples: Vec<(f32, f32)> = {
let x_y_axes = Array::random((2, 5), Uniform::new(0., 1.));
let x_axis: Vec<f32> = x_y_axes.slice(s![0, ..]).to_vec();
let y_axis: Vec<f32> = x_y_axes.slice(s![0, ..]).to_vec();
x_axis.into_iter().zip(y_axis).collect()
};

random_samples

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

chart.draw_series(LineSeries::new(
random_samples.iter().map(|(x, y)| (*x, *y)),
&RED,
))?;

Ok(())
}).style(“width:100%”)

In this code snippet, we employed the Ndarray function Array::random to create a 2D array of data that is populated with arbitrary values. Every time you use this method, it generates an exclusive set of data points. By printing out the resulting array, one can examine these random numbers closely. The draw_series call exhibits both rows from our dataset as individual curves on a single graph. Since each execution results in different randomized outputs, every generated chart will be unique and introduce some unpredictability and diversity into your visualization experience.

To summarise, the ability to visualize multiple plots in a single output with Plotters is a powerful feature for data exploration and analysis. Whether plotting distinct curves, comparing datasets, or leveraging random data, multiline graphs offer a comprehensive view of the information at hand. By utilizing Plotters’s functionality and experimenting with different data sources, you can create impactful visualizations that facilitate better understanding and decision-making.

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.

Colors and Markers

Plotters offers a wide range of styles and markers to design visually captivating plots that are comprehensible. Styles enable you to modify the look of your lines, while markers assist in emphasizing particular data points on your plot. By combining diverse colors, styles, and markers with Plotters’s features, you can craft distinctive plots tailored explicitly to your requirements.

Plotters offers advanced color maps that enable the visualization of intricate data with a spectrum of colors. With Plotters’s style parameter, you can select from an array of predefined color maps or design your own personalized one using a built-in struct like RGBColor. This parameter proves to be particularly beneficial when representing data encompassing extensive value ranges or emphasizing specific plot lines or any other shape. You can refer to the full palette for different RGB color values.

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)),
&RGBColor(0,0,255) // red: 0, green: 0, blue: 255 -> the color is blue
))?;

Ok(())
}).style(“width: 60%”)A single line plot with color blue (image by author)

In this example, we have changed the color of the line to blue. You can also use other color formats, such as HSLColor, to specify custom colors using the HSL spectrum values.

To elevate the visual appeal of your line plots in Plotters, consider incorporating markers to represent distinct symbols for each plot. If you desire a personalized touch, there are several ways to customize these markers. First, we can utilize the draw_series method by plotting your data twice with a marker style such as size and color based on personal preferences or specific dataset characteristics.

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..8f32)?;

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
))?;

chart.draw_series(x.map(|x| {
EmptyElement::at((*x, *x))
+ Cross::new((0, 0), 2, GREEN) // coordinates relative to EmptyElement
}))?;

Ok(())
}).style(“width: 60%”)A linear single line plot with markers (image by author)

Alternatively, we can set the marker size using the point_size method which allows for creating a filled or open circle marker.

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..8f32)?;

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
).point_size(2))?; // open circle marker

Ok(())
}).style(“width: 60%”)A line plot with markers (image by author)

You can combine all these techniques (e.g. colors, markers, legend) to customize the visualization as follows:

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..342f32)?;

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.filled()
).point_size(2)).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).powi(3))),
BLUE
).point_size(2)).unwrap()
.label(“y = x ^ 3”)
.legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &BLUE));

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

chart.draw_series(x.map(|x| {
EmptyElement::at((*x, (*x).powi(2)))
+ Cross::new((0, 0), 2, WHITE) // coordinates relative to EmptyElement
}))?;

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

Ok(())
}).style(“width: 60%”)A multiline plot with different lines colors, markers, labels, title and legend (image by author)

Overall, Plotters offers a simple and effortless way to personalize colors and markers, enabling you to craft exceptional visualizations. By selecting the appropriate color palettes, your plots can effectively communicate valuable information with ease. Choosing the right color and marker t could make all the difference in conveying your message successfully.

Subplots

Plotters subplots (image by author)

The technique of sub-plotting is a powerful way to display multiple graphs in the same output. This method proves particularly useful when you want to compare different data sets or showcase various aspects of one dataset. With Plotters, creating subplots becomes an effortless task as it allows you to create a grid layout where each subsequent plot’s position can be specified relative to its predecessor.

Furthermore, every subplot has customizable specifications like titles and labels that make it easy for users to tailor their outputs according to specific needs. Sub-plotting comes especially handy while dealing with complex information in scientific and data analysis contexts since it helps convey crucial findings concisely yet effectively.

To generate subplots in Plotters, you can use the split_evenly method that requires one parameter: a tuple that consists of row count and column count. For instance, if you want to craft a 1×2 layout for your subplots while plotting data on the first one; then utilize this code snippet:

let linear_coordinates: Vec<(f32, f32)> = {
let x_y_axes = array!([[1., 2., 3., 4.], [1., 2., 3., 4.]]);
let x_axis: Vec<f32> = x_y_axes.slice(s![0, 0, ..]).to_vec();
let y_axis: Vec<f32> = x_y_axes.slice(s![0, 1, ..]).to_vec();
x_axis.into_iter().zip(y_axis).collect()
};
let quadratic_coordinates: Vec<(f32, f32)> = {
let x_y_axes = array!([[1., 2., 3., 4.], [1., 4., 9., 16.]]);
let x_axis: Vec<f32> = x_y_axes.slice(s![0, 0, ..]).to_vec();
let y_axis: Vec<f32> = x_y_axes.slice(s![0, 1, ..]).to_vec();
x_axis.into_iter().zip(y_axis).collect()
};

evcxr_figure((640, 480), |root| {
let sub_areas = root.split_evenly((1,2)); // 1×2 grid ( 1 row, 2 columns)

let graphs = vec![
(“y = x”, linear_coordinates.clone(), &RED),
(“y= x ^ 2”, quadratic_coordinates.clone(), &GREEN),
];

for ((idx, area), graph) in (1..).zip(sub_areas.iter()).zip(graphs.iter()) {
let mut chart = ChartBuilder::on(&area)
.caption(graph.0, (“Arial”, 15).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_cartesian_2d(0f32..5f32, 0f32..17f32)?;

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

chart.configure_mesh()
.y_labels(10)
.light_line_style(&TRANSPARENT)
.disable_x_mesh()
.x_desc(“x = Array::range(1., 7., 0.1);”)
.y_desc(graph.0)
.draw()?;
}

Ok(())
}).style(“width:100%”)

This will create a 1×2 grid of subplots and plot the data in both subplots, with a title and axis labels specified. The tuple argument passed to split_evenly represent the grid (1 row and 2 columns). There are many ways to do sub-plotting in Plotters, using split_vertically, split_horizontally, split_evenly, and split_by_breakpoints

By utilizing Plotters’s capabilities for sub-plotting, stunning visualizations become achievable which aid communication by presenting insights clearly and accurately.

Error Bars

A single plot with vertical error bars (image by author)

In order to accurately represent data, it is crucial that the potential for error is acknowledged and made transparent. This can be achieved through the use of error bars — graphical representations which display variability in measurements and indicate uncertainty levels. Plotters offers an easy solution with its ErrorBar function, allowing users to add these essential visual aids to any plot by specifying x/y coordinates, color/style preferences as well as providing relevant error values. Let’s consider the following code snippet:

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

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 as f32).powi(2))),
&GREEN
)).unwrap()
.label(“y = x ^ 2”)
.legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &GREEN));

chart.draw_series(x.map(|x| {
ErrorBar::new_vertical(*x, (*x as f32).powi(2) – 1.5, (*x as f32).powi(2), (*x as f32).powi(2) + 1.4, RED.filled(), 2)
})).unwrap();

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

Ok(())
}).style(“width: 100%”)

In this example, we have chosen to display the error on the y-axis as it is typically more prevalent. The following image is a visual representation of our data and exhibits distinct error bars surrounding each data point. These bars indicate the range of values that are likely within a certain confidence level; longer bars signify greater uncertainty in measurement.

However, there may be occasions where displaying error data on both axes would prove beneficial — particularly when dealing with time series or experimental data containing multiple independent variables. In such cases, using the ErrorBar::new_horizontal method and passing an array of x-axis errors (similarly done for y-axis errors) will suffice.

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

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 as f32).powi(2))),
&GREEN
)).unwrap()
.label(“y = x ^ 2”)
.legend(|(x,y)| PathElement::new(vec![(x,y), (x + 20,y)], &GREEN));

chart.draw_series(x.map(|x| {
ErrorBar::new_horizontal((*x as f32).powi(2), *x – 0.3, *x, *x + 0.3, RED.filled(), 2)
})).unwrap();

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

Ok(())
}).style(“width: 100%”)A single plot with horizontal error bars (image by author)

By incorporating such elements into your visualizations — whether as a scientist sharing research findings or a business analyst displaying sales figures — audiences can better understand any uncertainty associated with the presented information. Therefore, utilizing this essential feature will ensure that precise details are accurately conveyed while maintaining clarity throughout presentations without confusion caused due to errors within datasets represented graphically using Plotters’s features like Error Bars!

Scatter Plot

Scatter plots serve as a crucial tool for visualizing data and gaining insights into the relationship between two variables. Plotters makes it effortless to create scatter plots in Rust by assigning one variable to the x-axis, and another to the y-axis, and plotting each point on their corresponding coordinates. By manipulating the colors and sizes of points, you can represent additional dimensions within your dataset.

The primary advantage of using scatter plots is that they reveal patterns or clusters in data that may not be apparent from tables or charts alone. Outliers are also easily identifiable through this method.

Furthermore, these graphs have an intuitive nature that enables anyone — regardless of statistical expertise -to quickly comprehend relationships between different aspects, hence making them useful communication tools when presenting findings.

The following code snippet will generate a scatter plot of a data samples with uniform distribution:

evcxr_figure((640, 480), |root| {
_ = root.fill(&WHITE);

let mut chart = ChartBuilder::on(&root)
.caption(“Uniform Distribution Scatter Plot”, (“Arial”, 20).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_cartesian_2d(0f32..1f32, 0f32..1f32)?;

chart.configure_mesh()
.disable_x_mesh()
.disable_y_mesh()
.y_labels(5)
.x_label_formatter(&|x| format!(“{:.1}”, *x as f64 / 100.0))
.y_label_formatter(&|y| format!(“{}%”, (*y * 100.0) as u32))
.draw()?;

let _ = chart.draw_series(random_samples.iter().map(|(x,y)| Circle::new((*x,*y), 3, GREEN.filled())));

Ok(())
}).style(“width:100%”)

The scatter plot produced is as follows:

A scatter plot of uniform distribution data samples (image by author)

In summary, scatter plots offer powerful visualization capabilities allowing us better understand our datasets while providing straightforward ways for sharing information with others, thanks mainly due its ease-of-use features provided by Plotters’s library functions available within the Rust programming language environment!

Histogram

Histograms are an invaluable asset when it comes to analyzing data distribution. They offer a visual representation of how the information is spread across different categories or bins, making it easier for us to comprehend and interpret complex sets of data. Plotters simplify this process by utilizing Histogram::vertical function with linear arrays that group together data points into bars representing frequency per bin size.

For instance, if we need to plot a randomly generated uniform distribution, creating histograms would display each possible outcome’s frequency in detail while revealing any patterns or trends present within the dataset. Analyzing these graphs can help uncover valuable insights about underlying distributions such as age groups’ demographics in populations, light exposure levels captured on photographs, or monthly precipitation rates observed throughout cities.

The following code snippet is an example of plotting a randomly generated Uniform distribution data samples:

evcxr_figure((640, 480), |root| {
let mut chart = ChartBuilder::on(&root)
.caption(“Histogram”, (“Arial”, 20).into_font())
.x_label_area_size(50)
.y_label_area_size(50)
.build_cartesian_2d(0u32..100u32, 0f64..0.5f64)?;

chart.configure_mesh()
.disable_x_mesh()
.disable_y_mesh()
.y_labels(5)
.x_label_formatter(&|x| format!(“{:.1}”, *x as f64 / 100.0))
.y_label_formatter(&|y| format!(“{}%”, (*y * 100.0) as u32))
.draw()?;

let hist = Histogram::vertical(&chart)
.style(RED.filled())
.margin(0)
.data(random_samples.iter().map(|(x,_)| ((x*100.0) as u32, 0.01)));

let _ = chart.draw_series(hist);

Ok(())
}).style(“width:100%”)

The histogram produced is as follows:

A histogram plot of uniform distribution data samples (image by author)

In summary, Histograms provide powerful tools for gaining insight into various datasets and identifying critical factors affecting them over time accurately. By using Plotters’ features like customizable bin sizes tailored explicitly towards our needs allows us greater flexibility when interpreting large amounts of information quickly without sacrificing accuracy!

Conclusion

Photo by Aaron Burden on Unsplash

This article has emphasized the significance of visualizations and how Plotters can be customized to suit various needs. Plotters proved invaluable in creating diverse types of plots, such as single-line or multiline graphs, scatter plots, and histograms. Moreover, we have learned about customizing features like layout design choices for color lines, markers, legends, etc.

With newfound knowledge at hand, you can confidently navigate through Plotters’s various features with ease. Leveraging these methods effectively will enhance your understanding of the data and enable better communication of findings.

In the upcoming series of articles, particularly in part 2, we’ll explore captivating data visualizations, including but not limited to pie charts, and 3D visualizations. The goal is to empower you to become a skilled visual storyteller with your data, revealing hidden insights like never before!

Closing Note

Photo by Nick Morrison on Unsplash

As we conclude this tutorial, I would like to express my sincere appreciation to all those who have dedicated their time and energy to completing it. It has been an absolute pleasure to demonstrate the extraordinary capabilities of Rust programming language with you.

Being passionate about data science, I promise you that I am going to write at least one comprehensive article every week or so on related topics from now on. If staying updated with my work interests you, consider connecting with me on various social media platforms or reach out directly if anything else needs assistance.

Thank You!

Resources

GitHub – wiseaidev/rust-data-analysis: The ultimate data analysis with Rust course.plotters – Rustevcxr-jupyter-integration

Rustic Data: Data Visualization with Plotters — Part 1 was originally published in Towards Data Science on Medium, where people are continuing the conversation by highlighting and responding to this story.

Logo

Oh hi there 👋
It’s nice to meet you.

Sign up to receive awesome content in your inbox, every month.

We don’t spam!

Leave a Comment

Scroll to Top