Center tick labels and add labels beside colorbar extensions in Matplotlib

Viewed 1237

I am looking for solutions to:

  1. Align vertically the tick labels along the colorbar with the center of each color/class.
  2. Place some tick labels beside both colorbar triangular extensions.
  3. Change the figure size to see the tick labels completely while keeping the same colorbar width.

I already tried various solutions proposed on stack overflow to center the tick labels, but none seems to work properly with my current code. For exemple, using ax.set_yticks(number_of_classes + 0.5) or other similiar methods put all the tick labels at the bottom of the colorbar instead of placing them aligned with the center of each color.

This is the colorbar I get with my current code.

Here's the code I used:

import matplotlib as mpl
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.figure import Figure
import numpy as np

# data and labels
bounds = [44, 51, 53, 56]
color_palette = [[43, 131, 186],
                 [86, 104, 146],
                 [129, 78, 107],
                 [172, 51, 67],
                 [215, 25, 28]]
tick_labels = ['Convection not likely',
               'Likely thunderstorms',
               'Isolated severe storms',
               'Widely scattered severe storms',
               'Scattered severe storms']

# Array of colors with rgb value in 0-1 range
all_vals = np.array([[color_palette[0][0] / 255,
                      color_palette[0][1] / 255,
                      color_palette[0][2] / 255,
                      1]])

for i in range(1, len(color_palette)):
    vals = [[color_palette[i][0] / 255,
             color_palette[i][1] / 255,
             color_palette[i][2] / 255,
             1]]
    all_vals = np.concatenate((all_vals, vals))

# create a colormap
cmap = mpl.colors.ListedColormap(all_vals[1:-1])
cmap.set_over(all_vals[-1])
cmap.set_under(all_vals[0])

# normalize bound values
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

# create a figure
fig = Figure(figsize=(2, 6))
canvas = FigureCanvasAgg(fig)
ax = fig.add_subplot(121)

# create the colorbar
cb = mpl.colorbar.ColorbarBase(ax, cmap=cmap,
                               norm=norm,
                               extend='both',
                               ticks=bounds,
                               spacing='uniform')

# add the tick labels
cb.ax.set_yticklabels(tick_labels)

fig.savefig('colorbar')
1 Answers

When setting ticklabels, it is important to also set the tick positions. For the colorbar, these positions need to be set with cb.set_ticks (this is confusing, as the labels need to be set via cb.ax.set_yticklabels). Tick positions can only be set at the extremes of the main colorbar, not exactly near the extensions.

So, logical positions are: one at the very bottom, one at the very top, and in the middle of the subsequent bounds. The label at the bottom can be top-aligned to set it closer to the downward triangle (and similar for the upward triangle).

As the labels now indicate a region, the ticks marks could be removed.

cb.set_ticks([bounds[0]] + [(b0 + b1) / 2 for b0, b1 in zip(bounds[:-1], bounds[1:])] + [bounds[-1]])
tick_texts = cb.ax.set_yticklabels(tick_labels)
tick_texts[0].set_verticalalignment('top')
tick_texts[-1].set_verticalalignment('bottom')
cb.ax.tick_params(length=0) # optionally remove the tick marks

resulting plot

PS: To create a figure and axes, usually the simplest way is leaving out FigureCanvasAgg and creating figure and axes via plt.subplots(). The 'left' and 'right' padding (white space left and right of the axes' rectangle) can be set via gridspec_kw. You can still save an image via fig.savefig. With plt.show() you get a window where these paddings can be changed interactively.

from matplotlib import pyplot as plt

fig, ax = plt.subplots(figsize=(2, 5), gridspec_kw={'left': .4, 'right': .5})
# ...
fig.savefig(...)
plt.show()
Related