Annotations, Colorbars, and Advanced Layouts
Overview
In this section we explore methods for customizing plots, including the following:
Adding annotations
Rendering equations
Colormap overview
Basic colorbars
Shared colorbars
Custom colorbars
Mosaic subplots
Imports
Here, we import matplotlib
, numpy
, and scipy
(to generate some sample data)
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
from matplotlib.colors import LinearSegmentedColormap, ListedColormap, Normalize
Create Some Sample Data
Using scipy.stats
, we can create a normal distribution! Notice how nicely centered and normal our distribution is!
mu = 0
variance = 1
sigma = np.sqrt(variance)
x = np.linspace(mu - 3 * sigma, mu + 3 * sigma, 200)
pdf = stats.norm.pdf(x, mu, sigma)
plt.plot(x, pdf);
Adding Annotations
A common part of many people’s workflows is adding annotations, or “a note of explanation or comment added to a text or diagram.”
We can do this using plt.text
which takes the inputs of the (x, y)
float text position in data coordinates and the text string.
plt.plot(x, pdf)
plt.text(0, 0.05, 'here is some text!');
Rendering Equations
We can even add math text, using Latex syntax. The key is use strings with following format:
r'$some_equation$'
Here is the example equation we use!
If you are interested in learning more about Latex syntax, check out their official documentation.
Further, if you’re running the notebook interactively (e.g. on Binder) you can double click on the cell to see the latex source for the rendered equation.
plt.plot(x, pdf)
plt.text(
-1,
0.05,
r'$f(x) = \frac{1}{\mu\sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}$',
);
We plotted our equation! But it looks pretty small; we can increase the size of the text, and center the equation by using fontsize
and ha
(horizontal alignment).
This next example also uses latex notation in the legend text.
fstr = r'$f(x) = \frac{1}{\mu\sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}$'
plt.plot(x, pdf, label=r'$\mu=0, \,\, \sigma^2 = 1$')
plt.text(0, 0.05, fstr, fontsize=15, ha='center')
plt.legend();
Add a Box Around the Text
One other thing we can add here, for readability, is a box around the text, using bbox
.
The bbox
argument in plt.text
uses a dictionary to create the box! We pass in:
a rounded box sytle (
boxstyle = 'round'
)a light grey facecolor (
fc = 'lightgrey'
)a black edgecolor (
ec = 'k'
)
fig = plt.figure(figsize=(10, 8))
plt.plot(x, pdf)
fstr = r'$f(x) = \frac{1}{\mu\sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}$'
plt.text(
0,
0.05,
fstr,
fontsize=18,
ha='center',
bbox=dict(boxstyle='round', fc='lightgrey', ec='k'),
)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.title("Normal Distribution with SciPy", fontsize=24);
Colormap Overview
Colormaps are a visually appealing way to represent another dimension to your data. They are a matrix of hues and values allowing you to, for example, display hotter temperatures as red and colder temperatures as blue.
Classes of colormaps
Click the dropdown arrow to see examples of colormaps within their respective classes.
1. Sequential: change in lightness and/or saturation of color incrementally. Good for data that has ordering.
2. Diverging: change in lightness and/or saturation of two different colors that meet in the middle at an unsaturated color. Should be used when the data has a natural zero point, such as sea level.
3. Cyclic: change in lightness of two different colors that meet in the middle and begin and end at an unsaturated color. Should be used for values that naturally wrap around.
4. Qualitative: miscellaneous color. Should not be used for data that has ordering or relationships.
Other considerations
There is a lot of info about choosing colormaps that could be its own tutorial. Two important considerations:
Color blind friendly patterns: avoiding colormaps with both red and green can account for the most common form of color blindness. The GeoCAT-examples gallery has a section devoted to picking better colormaps that covers this.
Grayscale conversion: It is not uncommon for plots rendered in color to be printed in black and white, obscuring the usefulness of a chosen colormap
See Choosing Colormaps in Matplotlib for a more in depth version of this section
Basic Colorbars
Before we look at a colorbar, let’s generate some fake data using numpy.random
npts = 1000
nbins = 15
x = np.random.normal(size=npts)
y = np.random.normal(size=npts)
Here, we plot a 2D histogram using our fake data, using the default colorbar “viridis”
fig = plt.figure()
ax = plt.gca()
plt.hist2d(x, y, bins=nbins, density=True)
plt.colorbar();
We can change which colorbar to use by passing in cmap = 'colorbar_name'
. We can use the magma
colorbar instead!
fig = plt.figure()
ax = plt.gca()
plt.hist2d(x, y, bins=nbins, density=True, cmap='magma')
plt.colorbar();
Custom Colorbars
Even with the large collection of prepackaged colorbars, you may find it useful to create your own colorbar.
Below are 2 similar examples of using custom colorbars:
The first has very discrete list of colors called colors
, and creates a colormap from this list with the call ListedColormap
.
The second used the call LinearSegmentedColormap
to create a colormap from interpolating the same list colors
.
colors = [
'white',
'pink',
'red',
'orange',
'yellow',
'green',
'blue',
'purple',
'black',
]
ccmap = ListedColormap(colors)
norm = Normalize(vmin=0, vmax=0.18)
fig, ax = plt.subplots(nrows=1, ncols=2, constrained_layout=True)
hist1 = ax[0].hist2d(x, y, bins=15, density=True, cmap=ccmap, norm=norm)
hist2 = ax[1].hist2d(x, y, bins=30, density=True, cmap=ccmap, norm=norm)
cbar = fig.colorbar(hist1[3], ax=ax, location='bottom')
cbcmap = LinearSegmentedColormap.from_list("cbcmap", colors)
fig, ax = plt.subplots(nrows=1, ncols=2, constrained_layout=True)
hist1 = ax[0].hist2d(x, y, bins=15, density=True, cmap=cbcmap, norm=norm)
hist2 = ax[1].hist2d(x, y, bins=30, density=True, cmap=cbcmap, norm=norm)
cbar = fig.colorbar(hist1[3], ax=ax, location='bottom')
The Normalize
Class
Note that both plots use the norm
kwarg. The Normalize
class linearly normalizes data into the [0, 1] interval. This is used to linearly map the colors in the colormap to the data from vmin
to vmax
. In fact, we used this functionality in the previous histogram exercise! The vmin
and vmax
kwargs for hist2d
are simply passed into the Normalize
function. When making a custom colormap, it is best to specify how you want the data normalized.
For non-linear nomalization, check out this Colormap Normalization tutorial.
Mosaic Subplots
One of the recent features added to Matplotlib is subplot_mosaic
where you can pass the structure of your figure, and it will generate your subplots automatically!
For example, if we wanted two plots on top, and one of the bottom, we can construct it using the following block of text:
""
AB
CC
""
This corresponds to three axes: A
, B
, and C
with A
and B
on top of C
.
Once we create the subplots, we can access them using the resultant axes dictionary, with the syntax axes_dict['your_axis']
. An example of this is given below!
axdict = plt.figure(constrained_layout=True).subplot_mosaic(
"""
AB
CC
"""
)
histA = axdict['A'].hist2d(x, y, bins=15, density=True, cmap=cbcmap, norm=norm)
histB = axdict['B'].hist2d(x, y, bins=10, density=True, cmap=cbcmap, norm=norm)
histC = axdict['C'].hist2d(x, y, bins=30, density=True, cmap=cbcmap, norm=norm)
You’ll notice there is not a colorbar plotted by default. When constructing the colorbar, we need to specify:
Which plot to use for the colormapping (ex.
histA
)Which axes to merge colorbars across (ex. [
histA
,histB
])Where to place the colorbar (ex.
bottom
)
axdict = plt.figure(constrained_layout=True).subplot_mosaic(
"""
AB
CC
"""
)
histA = axdict['A'].hist2d(x, y, bins=15, density=True, cmap=cbcmap, norm=norm)
histB = axdict['B'].hist2d(x, y, bins=10, density=True, cmap=cbcmap, norm=norm)
histC = axdict['C'].hist2d(x, y, bins=30, density=True, cmap=cbcmap, norm=norm)
fig.colorbar(histA[3], ax=[axdict['A'], axdict['B']], location='bottom')
fig.colorbar(histC[3], ax=[axdict['C']], location='right');
Summary
You can use features in Matplotlib to add annotations, even math, to your plots
There are a number of considerations to take into account when choosing your colormap
You can create your own colormaps with Matplotlib
Various axes in figures can share colorbars