I'm trying to use a custom colorbar in matplotlib with irregular intervals. But when following the tutorial and using the colorbar, it gets used as a regular interval colorbar.
How do I construct/use a colorbar with irregular intervals?
MWE below:
I'm plotting various data with plt.matshow()
, like this:
testdf = pd.DataFrame([
(7, 7.1, 8 , 9),
(0, 1, 1.5, 2),
(2.001, 3, 3.5, 4),
(4.001, 5, 6, 6.9999),
], index=[0, 1, 2, 3], columns=('A', 'B', 'C', 'D'),)
and
plt.matshow(testdf)
However, I want only certain numbers highlighted and I want to group others, i.e. I want a discrete, custom colorbar, instead of the default continuous one.
Luckily, the matplotlib documentation has just what I need. So, lets set up this colorbar:
fig, ax = plt.subplots(figsize=(6, 1))
fig.subplots_adjust(bottom=0.5)
cmap = (mpl.colors.ListedColormap(['red', 'green', 'blue', 'cyan'])
.with_extremes(over='0.25', under='0.75'))
bounds = [1, 2, 4, 7, 8]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig.colorbar(
mpl.cm.ScalarMappable(cmap=cmap, norm=norm),
cax=ax,
boundaries=[0] + bounds + [13], # Adding values for extensions.
extend='both',
ticks=bounds,
spacing='proportional',
orientation='horizontal',
label='Discrete intervals, some other units',
)
Looks great! Numbers from 1 to 2 in red and 7 to 8 in blue and two large groups for all the uninteresting stuff between 2 and 7.
So, let's use it.
plt.matshow(testdf, cmap=cmap)
plt.colorbar()
...and that's not what I expected. The colorbar should look like the one I just constructed before, not regularly spaced and thus lines 0 and 1 should contain a black/grey box for over/under, line 2 should be all green and line 3 all blue.
How do I fix this? What am I missing?
As Jody Klymak and JohanC have pointed out in the comments, norm
also needs to be passed into matshow
, i.e. plt.matshow(testdf, cmap=cmap, norm=norm)
.
However, this doesn't work for stuff where I can't pass further arguments with my colormap (or where I can't figure out how to do so…), e.g. in sns.clustermap
.
A possible workaround is to define a colormap with regular intervals, with many following intervals being of the same color:
fig, ax = plt.subplots(figsize=(6, 1))
fig.subplots_adjust(bottom=0.5)
cmap = (mpl.colors.ListedColormap(['red',
'green', 'green',
'blue', 'blue', 'blue', 'blue',
'cyan'])
.with_extremes(over='0.25', under='0.75'))
bounds = [1, 2, 3, 4, 5, 6, 7, 8]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig.colorbar(
mpl.cm.ScalarMappable(cmap=cmap, norm=norm),
cax=ax,
boundaries=[0] + bounds + [13], # Adding values for extensions.
extend='both',
ticks=bounds,
spacing='proportional',
orientation='horizontal',
label='Discrete intervals, some other units',
)
Results in
and following that
As you can see, it's still different from what I expected in the question, but turns out that the limits are different from what I expected them to be, i.e. the interval 1, 2
means >1, <2
which is easily fixable, if you know/expect this behaviour.