# 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