Stacked plots and Facet grids#
Summary
On this page, you will see how to
use
.plot.facet_grid.line
and.plot.facet_grid.errorbands
to stack multiple lines in a single plot, usinghue
to differentiate them.plot multiple panels in a single image, using the
hue
,row
, andcol
keys.use
.plot.facet_grid.with_auto_encoding
to automatically distribute variables onto available plot dimensions.use
col_wrap: auto
to automatically make the plot more square.
Complete example: Stacked Errorbands
stacked_errorbands:
based_on:
- .creator.multiverse
- .plot.facet_grid.errorbands
# Select the infected population
select_and_combine:
fields:
infected:
path: densities
transform:
- .sel: [!dag_prev , { kind: infected }]
subspace:
immunity rate: [ 0.3 ]
# Calculate mean and std for each 'transmission rate' value
transform:
- .mean: [!dag_tag infected]
kwargs:
dim: seed
tag: mean
- .std: [!dag_tag infected]
kwargs:
dim: seed
tag: std
- xr.Dataset:
- infected density: !dag_tag mean
err: !dag_tag std
tag: data
# Distribute the variables accordingly
x: time
y: infected density
yerr: err
hue: transmission rate
# Set the helpers
helpers:
set_labels:
y: Density [1/A]
set_title:
title: Density of agent kinds
# Use some pretty colors and latex
style:
text.usetex: True
mathtext.fontset: stix
font.family: serif
axes.prop_cycle: cycler('color', ['#AFD8BC', '#FFCC66', '#006666' ])
Complete example: Facet grid
panel_errorbands:
based_on:
- .creator.multiverse
- .plot.facet_grid.errorbands
# Select the infected agents
# (three sweep dimensions: immunity rate, transmission rate, seed)
select_and_combine:
fields:
infected:
path: densities
transform:
- .sel: [!dag_prev , { kind: infected }]
# Calculate mean and std for each
transform:
- .mean: [!dag_tag infected, seed]
tag: mean
- .std: [!dag_tag infected, seed]
tag: std
- xr.Dataset:
- infected density: !dag_tag mean
err: !dag_tag std
tag: data
# Distribute the variables
x: time
y: infected density
yerr: err
row: transmission rate
col: immunity rate
# Set the same limits for all (makes it easier to compare the panels)
helpers:
set_limits:
y: [ 0, 0.2 ]
# Use some pretty colors and latex
color: '#CC3333'
style:
text.usetex: True
mathtext.fontset: stix
font.family: serif
In the previous section we plotted a single line; now let’s see how to plot multiple lines with legends, all in the same plot. Using our SEIRD model, let’s look at how the susceptible, infected, and recovered populations evolve together:
densities:
based_on:
- .creator.universe
- .plot.facet_grid.line
select:
data:
path: data/SEIRD/densities
transform:
- .sel: [!dag_prev , { kind: [susceptible, infected, recovered] }]
x: time
Now that we’ve added more than one y-value, we need to tell the line-plot what to put on the x-axis (x: time
).
Instead of specifying the x-value, we could also tell it what the color represents – the two are equivalent.
To do this, simply replace the following line:
# x: time
hue: kind
In both cases, we get something like this:
We used LaTeX and some pretty colours to spruce everything up – see Customising plot styles for more details.
Stacked line plot with one sweep dimension#
Let’s compare the infection curves for three different values of the transmission rate p_transmit
of the virus.
infection_curves:
based_on:
- .creator.multiverse
- .plot.facet_grid.line
select_and_combine:
fields:
data:
path: data/SEIRD/densities
transform:
- .sel: [!dag_prev , { kind: [infected] }]
x: time
Since this is a multiverse plot, we must use the corresponding creator
, and use the select_and_combine
key to gather the data.
In this example, transform
block only adds a data
tag to the data, without performing any actual transformation operations.
Note
For facet_grid()
plots, the data
tag must always be defined, even when not applying any sort of transformation; that tag is where the plot expects to find the data to plot.
Here, we are defining the data
tag in the select
step.
Other plot functions may have different requirements.
This produces the following output:
Unsurprisingly, we see the peak of infection increasing as the virus becomes more transmissible.
Stacked line plot with one sweep dimension and statistics#
Let’s do the same thing, but with each infection curve representing an average over a few simulation runs with different initial seeds.
This assumes that we have performed a two-dimensional multiverse run, sweeping over both the seed
and the transmission rate p_transmit
.
The only thing we need to change from the previous entry is the transform
block:
transform:
- .mean: [!dag_tag infected, seed]
tag: data
This would be much more meaningful if we could add errorbands to each of the curves, so let’s do that:
infection_curves_averaged:
# Use the errorbands function!
based_on:
- .creator.multiverse
- .plot.facet_grid.errorbands
# This is the same as above
select_and_combine:
fields:
infected:
path: densities
transform:
- .sel: [!dag_prev , { kind: [infected] }]
# Calculate mean and standard deviation along the 'seed' dimension
transform:
- .mean: [!dag_tag infected, seed]
tag: mean
- .std: [!dag_tag infected, seed]
tag: std
- xr.Dataset:
- infected density: !dag_tag mean
err: !dag_tag std
tag: data
x: time
y: infected density
yerr: err
hue: transmission rate
Because the data is two-dimensional, we need to tell the plot function what to put on the x-axis, and what to stack: that’s why need both the hue
and x
keys.
Make sure to adjust the hue
key to whatever you named your sweep dimension!
Facet grids#
The stacked line plots we have just discussed are examples of facet grids. Facet grids are a simple way of visualising the results of parameter sweeps in a single image, either by showing several plots in a single frame, or by combining several frames into single image. Showing several panels in a single image can be useful when there are simply too many variables for a single plot, or when plotting everything on a single would clutter the plot. In such situations, you may wish to produce something like this:
Here, we are showing the output of a 3-dimensional parameter sweep: we are sweeping over the transmission rate
, over the immunity rate
, and over the initial seed.
Each panel shows the density of infected agents over time (x-axis), with the transmission rate on the rows, and the immunity rate on the columns.
Within each panel, we are averaging over the seed
and producing an errorband plot, using the .plots.facet_grid.errorbands
function.
Generating this plot is a simple modification away from our previous configuration; all we need to do is to divide up our variables amongst the rows and columns, using the row
and col
keys:
infection_curves_averaged:
# Same as above
based_on:
- .creator.multiverse
- .plot.facet_grid.errorbands
select_and_combine:
# also same as above...
transform:
# same as above ...
x: time
y: infected density
yerr: err
row: transmission rate
col: immunity rate
color: crimson
helpers:
set_limits:
y: [0, 0.2]
The transformation framework takes care of everything else. Notice that we have set the y-limits to all be equal, so that we can compare the curves at a single glance.
The facet_grid()
plot allows us to simultaneously plot parameters onto rows, columns, the y-axis, and also make use of the hue; let’s additionally include the susceptible and recovered agents in our plots:
A little crowded perhaps, but we get the idea.
All this requires is to include the other two kinds
in our selection, and to set the hue
key:
infection_curves_averaged:
based_on:
- # Same as above
# Also select the other kinds:
select_and_combine:
fields:
infected:
path: densities
transform:
- .sel: [!dag_prev , { kind: [susceptible, infected, recovered] }]
transform:
- # same as above ...
x: time
y: infected density
yerr: err
row: transmission rate
col: immunity rate
hue: kind
helpers:
set_limits:
y: [0, 0.6]
Note that the legend and row and column titles are automatically plotted.
Hint
You may sometimes not want to plot all values of a parameter; for example, for the plot above, we may just be interested in immunity rate = 0, 0.1, 0.2
, and transmission rate = 0.2, 0.4
.
This is easily achieved using subspace selection.
Hint
If you have lots of columns and few rows, use col_wrap: auto
together with
.plot.facet_grid.with_auto_encoding
(see below) to create a more square plot.
Auto-encoding#
If you don’t care which variables go where, you can include the .plot.facet_grid.with_auto_encoding
modifier into your plot:
based_on:
# ...
- .plot.facet_grid.with_auto_encoding
# ...
This will automatically distribute the variables onto any available dimensions.
Variables will be distributed in a certain order, see the determine_encoding()
dantro function.
Hint
Automatically determining the encoding can be useful if you want to implement more generic plots that do not depend so much on the dimensionality of your multiverse simulation run. They are best suited for getting an overview of your simulation results.
However, if you want to be sure that a specific variable is represented in a certain way, it’s best to specify the encoding (x
, col
, …) explicitly.
For publication-ready figures, this explicit definition is more suited.
To further control in which order dimensions are populated, you can pass a dict to the auto_encoding
argument (instead of a boolean):
based_on:
# ...
- .plot.facet_grid.line
- .plot.facet_grid.with_auto_encoding
# ...
# change the order in which encodings are populated
auto_encoding:
line: [x, col, hue, frames, row] # default: [x, hue, col, row, frames]
Hint
The .plot.facet_grid.with_auto_encoding
base config also sets the col_wrap: auto
argument, which aims to make facet grid plots with many subplots more square by wrapping after sqrt(num_cols)
.
This is ignored if the row
encoding is specified.
Hint
The expected_multiverse_ndim
entry in a multiverse
plot configuration can be used to skip a plot for unsupported dimensionalities:
my_multiverse_plot:
# ...
# Only plot if the multiverse is 1-, 2-, or 3-dimensional
expected_multiverse_ndim: [1, 2, 3]
Alternatively, you can also use the .skip
base plot configs, which define a bunch of these ready-to-use; see Base Plot Configuration Pool.