This is a interactive tutorial for Plotters
drawing library. If you are looking at the static HTML version and want to try the interactive version. Please follow the steps:
# Install Jupyter notebook
sudo apt install libzmq3-dev jupyter-notebook
cargo install evcxr_jupyter
evcxr_jupyter --install
# Get the notebook
git clone https://github.com/38/plotters-doc-data
cd plotteres-doc-data
jupyter notebook
# Install Jupyter notebook
brew install zeromq pkg-config
cargo install evcxr_jupyter
evcxr_jupyter --install
# Get the notebook
git clone https://github.com/38/plotters-doc-data
cd plotteres-doc-data
jupyter notebook
You can also download the latest notebook from https://raw.githubusercontent.com/38/plotters-doc-data/master/evcxr-jupyter-integration.ipynb, thus you don't have to clone the entire data repo.
In order to use Plotters
in jupyter-evcxr
, you need both Jupyter and evcxr installed.
Check https://github.com/google/evcxr for the instructions.
To use Plotters with jupyter-evcxr
, you need to import it using the following code:
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
Because evcxr
uses only SVG images, so we don't need other types of backend. So we should put
default_features = false, features = ["evcxr"]
Make the compilation faster. Since evcxr
shares all the artifacts among cells, after the first time we have plotters
compiled, it should be faster after.
To use plotters, the most convenient way is importing everything defined in the prelude
module.
It will import evcxr_figure
function for evcxr
integration.
Note: Currently evcxr doesn't work with nightly rust, so please make sure you are using a stable rust
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
// Import all the plotters prelude functions
use plotters::prelude::*;
// To create a figure that can be displayed in Jupyter notebook, use evcxr_figure function.
// The first param is the resolution of the figure.
// The second param is the closure that performes the drawing.
evcxr_figure((300, 10), |root| {
// Do the drawings
root.fill(&Blue)?;
// Tell plotters that everything is ok
Ok(())
})
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((320,50), |root| {
root.fill(&Green)?;
root.draw(&Text::new("Hello World from Plotters!", (15, 15), ("Arial", 20).into_font()))?;
Ok(())
})
One of the very important features is, Plotters
allows drawing multiple charts in a single figure. And this is done by having sub-drawing-areas. The root drawing area is able to be splitted into smaller drawing areas, and you can always do more fine-grained splits as well.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
use plotters::coord::Shift;
pub fn sierpinski_carpet(
depth: u32,
drawing_area: &DrawingArea<SVGBackend, Shift>)
-> Result<(), Box<dyn std::error::Error>> {
if depth > 0 {
let sub_areas = drawing_area.split_evenly((3,3));
for (idx, sub_area) in (0..).zip(sub_areas.iter()) {
if idx == 4 {
sub_area.fill(&White)?;
} else {
sierpinski_carpet(depth - 1, sub_area)?;
}
}
}
Ok(())
}
evcxr_figure((4800,4800), |root| {
root.fill(&Black)?;
sierpinski_carpet(5, &root)
}).style("width: 200px") /* You can add CSS style to the result */
Plotters
is designed for drawing charts, plots, etc. This example demonstrate how to use Plotters
chart specific APIs to draw a chart, including, labels, axis, meshes, etc. To draw a chart on the drawin area, you need to create a chart context and do some configuration.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 240), |root| {
// The following code will create a chart context
let mut chart = ChartBuilder::on(&root)
.caption("Hello Plotters Chart Context!", ("Arial", 20).into_font())
.build_ranged(0f32..1f32, 0f32..1f32)?;
// Then we can draw a series on it!
chart.draw_series((1..10).map(|x|{
let x = x as f32/10.0;
Circle::new((x,x), 5, &Red)
}))?;
Ok(())
}).style("width:60%")
We can also make Plotters
draws common components for us, such as, meshes, axis, legend. In this section, we demonstrate how to do that.
The following code shows how we add mesh to the chart.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
// The following code will create a chart context
let mut chart = ChartBuilder::on(&root)
.caption("Chart Context with Mesh", ("Arial", 20).into_font())
.build_ranged(0f32..1f32, 0f32..1f32)?;
chart.configure_mesh().draw()?;
Ok(())
}).style("width: 60%")
Then we can add axis to the chart.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
// The following code will create a chart context
let mut chart = ChartBuilder::on(&root)
.caption("Chart Context with Mesh and Axis", ("Arial", 20).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_ranged(0f32..1f32, 0f32..1f32)?;
chart.configure_mesh()
.draw()?;
Ok(())
}).style("width: 60%")
In addition to that, we can put label text to the axis.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
// The following code will create a chart context
let mut chart = ChartBuilder::on(&root)
.caption("Chart with Axis Label", ("Arial", 20).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_ranged(0f32..1f32, 0f32..1f32)?;
chart.configure_mesh()
.x_desc("Here's the label for X")
.y_desc("Here's the label for Y")
.draw()?;
Ok(())
}).style("width: 60%")
Then let's disable mesh lines for the X axis
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
// The following code will create a chart context
let mut chart = ChartBuilder::on(&root)
.caption("Chart Context with Mesh and Axis", ("Arial", 20).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_ranged(0f32..1f32, 0f32..1f32)?;
chart.configure_mesh()
.y_labels(10)
.line_style_2(&Transparent)
.disable_x_mesh()
.draw()?;
Ok(())
}).style("width: 60%")
To create multiple charts in a single figure, you can just split the drawing area and create multiple chart context.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
let sub_areas = root.split_evenly((2,2));
for (idx, area) in (1..).zip(sub_areas.iter()) {
// The following code will create a chart context
let mut chart = ChartBuilder::on(&area)
.caption(format!("Subchart #{}", idx), ("Arial", 15).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_ranged(0f32..1f32, 0f32..1f32)?;
chart.configure_mesh()
.y_labels(10)
.line_style_2(&Transparent)
.disable_x_mesh()
.draw()?;
}
Ok(())
}).style("width: 60%")
Unlike most of the plotting libraries, Plotters
doesn't actually define any types of chart. All the chart is abstracted to a concept of series. By doing so, you can put a histgoram series and a line plot series into the same chart context.
The series is actually defined as an iterator of elements, just this.
This gives Plotters
a huge flexibility on drawing charts. You can implement you own types of series and uses the coordinate translation and chart elements.
There are few types of predefined series, just for convenience:
First of all, let's generate some random numbers.
:dep rand = { version = "0.6.5" }
extern crate rand;
use rand::distributions::Normal;
use rand::distributions::Distribution;
use rand::thread_rng;
let sd = 0.13;
let random_points:Vec<(f64,f64)> = {
let mut norm_dist = Normal::new(0.5, sd);
let (mut x_rand, mut y_rand) = (thread_rng(), thread_rng());
let x_iter = norm_dist.sample_iter(&mut x_rand);
let y_iter = norm_dist.sample_iter(&mut y_rand);
x_iter.zip(y_iter).take(1000).collect()
};
random_points.len()
It's trivial to draw a scatter plot with Plotters
. The only need is, provide a iterator of the elements as series.
The following example shows how to make a 2D normal distribution figure. The red rectangle is the two sigma area and the red cross is the mean.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
// The following code will create a chart context
let mut chart = ChartBuilder::on(&root)
.caption("Normal Distribution", ("Arial", 20).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_ranged(0f64..1f64, 0f64..1f64)?;
chart.configure_mesh()
.disable_x_mesh()
.disable_y_mesh()
.draw()?;
chart.draw_series(random_points.iter().map(|(x,y)| Circle::new((*x,*y), 3, Green.filled())));
// You can alawys freely draw on the drawing backend
let area = chart.plotting_area();
let two_sigma = sd * 2.0;
area.draw(&Rectangle::new(
[(0.5 - two_sigma, 0.5 - two_sigma), (0.5 + two_sigma, 0.5 + two_sigma)],
Red.mix(0.3).filled())
)?;
area.draw(&Cross::new((0.5, 0.5), 5, &Red))?;
Ok(())
}).style("width:60%")
We can also have histograms. For histograms, we can use the predefined histogram series struct to build the histogram easily. The following code demonstrate how to create both histogram for X and Y value of random_points
.
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
let areas = root.split_evenly((2,1));
let mut charts = vec![];
// The following code will create a chart context
for (area, name) in areas.iter().zip(["X", "Y"].into_iter()) {
let mut chart = ChartBuilder::on(&area)
.caption(format!("Histogram for {}", name), ("Arial", 20).into_font())
.x_label_area_size(40)
.y_label_area_size(40)
.build_ranged(0u32..100u32, 0f64..0.5f64)?;
chart.configure_mesh()
.disable_x_mesh()
.disable_y_mesh()
.y_labels(5)
.x_label_offset(30)
.x_label_formatter(&|x| format!("{:.1}", *x as f64 / 100.0))
.y_label_formatter(&|y| format!("{}%", (*y * 100.0) as u32))
.draw()?;
charts.push(chart);
}
let hist_x = Histogram::vertical(&charts[0])
.style(Red.filled())
.margin(0)
.data(random_points.iter().map(|(x,_)| ((x*100.0) as u32, 0.01)));
let hist_y = Histogram::vertical(&charts[0])
.style(Green.filled())
.margin(0)
.data(random_points.iter().map(|(_,y)| ((y*100.0) as u32, 0.01)));
charts[0].draw_series(hist_x);
charts[1].draw_series(hist_y);
Ok(())
}).style("width:60%")
:dep plotters = { git = "https://github.com/38/plotters", default_features = false, features = ["evcxr"] }
extern crate plotters;
use plotters::prelude::*;
evcxr_figure((640, 480), |root| {
let root = root.titled("Scatter with Histogram Example", ("Arial", 20).into_font())?;
let areas = root.split_by_breakpoints([560], [80]);
let mut x_hist_ctx = ChartBuilder::on(&areas[0])
.y_label_area_size(40)
.build_ranged(0u32..100u32, 0f64..0.5f64)?;
let mut y_hist_ctx = ChartBuilder::on(&areas[3])
.x_label_area_size(40)
.build_ranged(0f64..0.5f64, 0..100u32)?;
let mut scatter_ctx = ChartBuilder::on(&areas[2])
.x_label_area_size(40)
.y_label_area_size(40)
.build_ranged(0f64..1f64, 0f64..1f64)?;
scatter_ctx.configure_mesh()
.disable_x_mesh()
.disable_y_mesh()
.draw()?;
scatter_ctx.draw_series(random_points.iter().map(|(x,y)| Circle::new((*x,*y), 3, Green.filled())))?;
let x_hist = Histogram::vertical(&x_hist_ctx)
.style(Green.filled())
.margin(0)
.data(random_points.iter().map(|(x,_)| ((x*100.0) as u32, 0.01)));
let y_hist = Histogram::horizental(&y_hist_ctx)
.style(Green.filled())
.margin(0)
.data(random_points.iter().map(|(_,y)| ((y*100.0) as u32, 0.01)));
x_hist_ctx.draw_series(x_hist)?;
y_hist_ctx.draw_series(y_hist)?;
Ok(())
}).style("width:60%")