I was looking to get a colorbar where the range 0 to 12000 is mapped using np.interp(x, [0, 100, 1000, 10000, 120000], [0, 0, 0.5, 1, 1])
.
The idea is to get the band from 0 to 100 mapped to the same color, then map the range 10 to 1000 uniformly from the lowest color to the mid color, then 1000 to 10000 from mid color to highest color.
I want this mapping because I'm using a divergent colormap RdYlGn_r
and the critical value is at 1000, and values above 10000 and 100 are kind of outliers.
So I tried to use ColorbarBase
with my own subclass of Normalizer
since LogNorm
, Normalizer
, SymLogNorm
, BoundaryNorm
and PowerNorm
doesn't seem suited for this task.
But the plot is blank (no errors)
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import traceback
import warnings
import sys
plt.close('all')
fig,ax = plt.subplots(1,1)
class MyNorm(mpl.colors.Normalize):
def __call__(self, value, clip=True):
if clip is None:
clip = self.clip
clip=False
result, is_scalar = self.process_value(value)
self.autoscale_None(result)
# Convert at least to float, without losing precision.
(vmin,), _ = self.process_value(self.vmin)
(vmax,), _ = self.process_value(self.vmax)
if vmin == vmax:
result.fill(0) # Or should it be all masked? Or 0.5?
elif vmin > vmax:
raise ValueError("minvalue must be less than or equal to maxvalue")
else:
if clip:
mask = np.ma.getmask(result)
result = np.ma.array(np.clip(result.filled(vmax), vmin, vmax),
mask=mask)
# ma division is very slow; we can take a shortcut
resdat = result.data
resdat = np.interp(resdat, [0,100, 1000, 10000, 120000], [0.00, 0.00, 0.5, 1,1])
result = np.ma.array(resdat, mask=result.mask, copy=False)
if is_scalar:
result = result[0]
print result
return result
norm = MyNorm(vmax=12000, vmin=0, clip=False)
#norm = mpl.colors.Normalize(vmin=0, vmax=12000, clip=True)
cmap = plt.get_cmap('RdYlGn_r') # From red to green
sm = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
cb1 = mpl.colorbar.ColorbarBase(ax, cmap=cmap, norm=norm, orientation='vertical',
ticks=[100, 1000, 10000])
cb1.set_clim(0,12000)
plt.show()
Not that I don't really want a colorbar made of discrete colors, otherwise BoundaryNorm
will work just fine. I need a starting region of a discrete color, followed by gradients and a discrete color band at the end