# Annotations, Colorbars, and Advanced Layouts

## Overview

In this section we explore methods for customizing plots. The following topics will be covered:

Adding annotations

Rendering equations

Colormap overview

Basic colorbars

Shared colorbars

Custom colorbars

Mosaic subplots

## Imports

Here, we import the `matplotlib.pyplot`

interface and `numpy`

, in addition to the `scipy`

statistics package (`scipy.stats`

) for generating 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

By using `scipy.stats`

, the Scipy statistics package described above, we can easily create a data array containing a normal distribution. We can plot these data points to confirm that the correct distribution was generated. The generated sample data will then be used later in this section. The code and sample plot for this data generation are as follows:

```
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. A rough definition of ‘annotation’ is ‘a note of explanation or comment added to text or a diagram’.

We can add an annotation to a plot using `plt.text`

. This method takes the x and y data coordinates at which to draw the annotation (as floating-point values), and the string containing the annotation text.

```
plt.plot(x, pdf)
plt.text(0, 0.05, 'here is some text!');
```

## Rendering Equations

We can also add annotations with **equation formatting**, by using LaTeX syntax. The key is to use strings in the following format:

```
r'$some_equation$'
```

Let’s run an example that renders the following equation as an annotation:

The next code block and plot demonstrate rendering this equation as an annotation.

If you are interested in learning more about LaTeX syntax, check out their official documentation.

Furthermore, if the code is being executed in a Jupyter notebook run 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}$',
);
```

As you can see, the equation was correctly rendered in the plot above. However, the equation appears quite small. We can increase the size of the text using the `fontsize`

keyword argument, and center the equation using the `ha`

(horizontal alignment) keyword argument.

The following example illustrates the use of these keyword arguments, as well as creating a legend containing LaTeX notation:

```
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

To improve readability, we can also add a box around the equation text. This is done using `bbox`

.

`bbox`

is a keyword argument in `plt.text`

that creates a box around text. It takes a dictionary that specifies options, behaving like additional keyword arguments inside of the `bbox`

argument. In this case, we use the following dictionary keys:

a rounded box style (

`boxstyle = 'round'`

)a light grey facecolor (

`fc = 'lightgrey'`

)a black edgecolor (

`ec = 'k'`

)

This example demonstrates the correct use of `bbox`

:

```
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 method of looking at visualized data in a new and different way. They associate specific values with hues, using color to ease rapid understanding of plotted data; for example, displaying hotter temperatures as red and colder temperatures as blue.

### Classes of colormaps

There are four different classes of colormaps, and many individual maps are contained in each class. To view some examples for each class, use the dropdown arrow next to the class name below.

## 1. **Sequential:** These colormaps incrementally increase or decrease in lightness and/or saturation of color. In general, they work best for ordered data.

## 2. **Diverging:** These colormaps contain two colors that change in lightness and/or saturation in proportion to distance from the middle, and an unsaturated color in the middle. They are almost always used with data containing a natural zero point, such as sea level.

## 3. **Cyclic:** These colormaps have two different colors that change in lightness and meet in the middle, and unsaturated colors at the beginning and end. They are usually best for data values that wrap around, such as longitude.

## 4. **Qualitative:** These colormaps have no pattern, and are mostly bands of miscellaneous colors. You should only use these colormaps for unordered data without 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: By using colormaps that do not contain both red and green, you can help people with the most common form of color blindness read your data plots more easily. The GeoCAT examples gallery has a section about picking better colormaps that covers this issue in greater detail.

Grayscale conversion: It is not too uncommon for a plot originally rendered in color to be converted to black-and-white (monochrome grayscale). This reduces the usefulness of specific colormaps, as shown below.

For more information on these concerns, as well as colormap choices in general, see the documentation page Choosing Colormaps in Matplotlib.

## Basic Colorbars

Before we look at a colorbar, let’s generate some fake X and Y data using `numpy.random`

, and set a number of bins for a histogram:

```
npts = 1000
nbins = 15
x = np.random.normal(size=npts)
y = np.random.normal(size=npts)
```

Now we can use our fake data to plot a 2-D histogram with the number of bins set above. We then add a colorbar to the plot, using the default colormap `viridis`

.

```
fig = plt.figure()
ax = plt.gca()
plt.hist2d(x, y, bins=nbins, density=True)
plt.colorbar();
```

We can change which colormap to use by setting the keyword argument `cmap = 'colormap_name'`

in the plotting function call. This sets the colormap not only for the plot, but for the colorbar as well. In this case, we use the `magma`

colormap:

```
fig = plt.figure()
ax = plt.gca()
plt.hist2d(x, y, bins=nbins, density=True, cmap='magma')
plt.colorbar();
```

## Custom Colorbars

Despite the availability of a large number of premade colorbar styles, it can still occasionally be helpful to create your own colorbars.

Below are 2 similar examples of using custom colorbars.

The first example uses a very discrete list of colors, simply named `colors`

, and creates a colormap from this list by using the call `ListedColormap`

.

The second example uses the function `LinearSegmentedColormap`

to create a new colormap, using interpolation and the `colors`

list defined in the first example.

```
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

Notice that both of these examples contain plotting functions that make use of the `norm`

kwarg. This keyword argument takes an object of the `Normalize`

class. A `Normalize`

object is constructed with two numeric values, representing the start and end of the data. It then linearly normalizes the data in that range into an interval of [0,1]. If this sounds familiar, it is because this functionality was used in a previous histogram example. Feel free to review any previous examples if you need a refresher on particular topics. In this example, the values of the `vmin`

and `vmax`

kwargs used in `hist2d`

are reused as arguments to the `Normalize`

class constructor. This sets the values of `vmin`

and `vmax`

as the starting and ending data values for our `Normalize`

object, which is passed to the `norm`

kwarg of `hist2d`

to normalize the data. There are many different options for normalizing data, and it is important to explicitly specify how you want your data normalized, especially when making a custom colormap.

For information on nonlinear and other complex forms of normalization, review this Colormap Normalization tutorial.

## Mosaic Subplots

One of the helpful features recently added to Matplotlib is the `subplot_mosaic`

method. This method allows you to specify the structure of your figure using specially formatted strings, and will generate subplots automatically based on that structure.

For example, if we wanted two plots on top, and one on the bottom, we can construct them by passing the following string to `subplot_mosaic`

:

```
""
AB
CC
""
```

This creates three `Axes`

objects corresponding to three subplots. The subplots `A`

and `B`

are on top of the subplot `C`

, and the `C`

subplot spans the combined width of `A`

and `B`

.

Once we create the subplots, we can access them using the dictionary returned by `subplot_mosaic`

. You can specify an `Axes`

object (in this example, `your_axis`

) in the dictionary (in this example, `axes_dict`

) by using the syntax `axes_dict['your_axis']`

. A full example of `subplot_mosaic`

is as follows:

```
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 the following:

Which plot to use for the colormapping (ex.

`histA`

)Which subplots (

`Axes`

objects) 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 text annotations to your plots, including equations in mathematical notation

There are a number of considerations to take into account when choosing your colormap

You can create your own colormaps with Matplotlib

Various subplots and corresponding

`Axes`

objects in a figure can share colorbars