How can I create a colorbar in matplotlib that looks like this:
Here is what I tried:
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.cm import ScalarMappable
from matplotlib.colors import Normalize
# Define the custom colormap
colors = ['red', 'cyan', 'darkgreen']
cmap = LinearSegmentedColormap.from_list(
'custom_colormap',
[(0.0, colors[0]), (0.5 / 2.0, colors[0]),
(0.5 / 2.0, colors[1]), (1.5 / 2.0, colors[1]),
(1.5 / 2.0, colors[2]), (2.0 / 2.0, colors[2])]
)
# Create a scalar mappable object with the colormap
sm = ScalarMappable(norm=Normalize(vmin=3.5, vmax=4.5), cmap=cmap)
# Create the colorbar
plt.figure(figsize=(3, 1))
cb = plt.colorbar(sm, orientation='horizontal', ticks=[3.5, 4.5], extend='neither')
cb.set_label('')
One solution is to draw wide white edges around the color segments (using cb.solids.set
below), hide the spines (cb.outline.set_visible
) and draw vertical lines as dividers (cb.ax.axvline
). To match the desired colorbar, make sure to pass a ymin
that is greater than 0.
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, Normalize
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots(figsize=(3, 1), layout='tight')
colors = ['red', 'cyan', 'darkgreen']
ticks = [3.5, 4.5]
cmap = ListedColormap(colors)
norm = Normalize(vmin=2.5, vmax=5.5)
cb = fig.colorbar(
mappable=ScalarMappable(norm=norm, cmap=cmap),
cax=ax,
ticks=ticks,
ticklocation='top',
orientation='horizontal',
)
cb.solids.set(edgecolor='white', linewidth=5)
cb.outline.set_visible(False)
cb.ax.tick_params(width=1, length=10, color='k')
for bound in ticks:
cb.ax.axvline(bound, c='k', linewidth=1, ymin=0.3, alpha=0.6)
plt.setp(cb.ax.xaxis.get_ticklines(), alpha=0.6)
cb.set_ticklabels(ticks, alpha=0.6, color='k', fontsize=15, fontfamily='Arial')
Another solution is instead of drawing vertical lines as dividers, just use the divider lines defined on colorbar object itself (pass drawedges=True
). However, the end result will be a little different from the desired result because the divider line draws from the bottom to the top (can't pass ymin
like above).
fig, ax = plt.subplots(figsize=(3, 1), layout='tight')
colors = ['red', 'cyan', 'darkgreen']
ticks = [3.5, 4.5]
cmap = ListedColormap(colors)
norm = Normalize(vmin=2.5, vmax=5.5)
cb = fig.colorbar(
mappable=ScalarMappable(norm=norm, cmap=cmap),
cax=ax,
ticks=ticks,
ticklocation='top',
orientation='horizontal',
drawedges=True
)
cb.solids.set(edgecolor='white', linewidth=5)
cb.outline.set_visible(False)
cb.dividers.set(linewidth=1, alpha=0.6)
cb.ax.tick_params(width=1, length=10, color='k')
plt.setp(cb.ax.xaxis.get_ticklines(), alpha=0.6)
cb.set_ticklabels(ticks, alpha=0.6, color='k', fontsize=15, fontfamily='Arial')