A colorbar in matplotlib maps number between 0 and 1 to a color. In order to map other numbers to colors you need a normalization to the range [0,1]
first. This is usually done automatically from the minimum and maximum data, or by using vmin
and vmax
arguments to the respective plotting function. Internally a normalization instance matplotlib.colors.Normalize
is used to perform the normalization and by default a linear scale between vmin
and vmax
is assumed.
Here you want a nonlinear scale, which (a) shifts the middle point to some specified value, and (b) squeezes the colors around that value.
The idea can now be to subclass matplotlib.colors.Normalize
and let it return a a mapping which fulfills the criteria (a) and (b).
An option might be the combination of two root functions as shown below.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
class SqueezedNorm(matplotlib.colors.Normalize):
def __init__(self, vmin=None, vmax=None, mid=0, s1=2, s2=2, clip=False):
self.vmin = vmin # minimum value
self.mid = mid # middle value
self.vmax = vmax # maximum value
self.s1=s1; self.s2=s2
f = lambda x, zero,vmax,s: np.abs((x-zero)/(vmax-zero))**(1./s)*0.5
self.g = lambda x, zero,vmin,vmax, s1,s2: f(x,zero,vmax,s1)*(x>=zero) - \
f(x,zero,vmin,s2)*(x<zero)+0.5
matplotlib.colors.Normalize.__init__(self, vmin, vmax, clip)
def __call__(self, value, clip=None):
r = self.g(value, self.mid,self.vmin,self.vmax, self.s1,self.s2)
return np.ma.masked_array(r)
fig, (ax, ax2, ax3) = plt.subplots(nrows=3,
gridspec_kw={"height_ratios":[3,2,1], "hspace":0.25})
x = np.linspace(-13,4, 110)
norm=SqueezedNorm(vmin=-13, vmax=4, mid=0, s1=1.7, s2=4)
line, = ax.plot(x, norm(x))
ax.margins(0)
ax.set_ylim(0,1)
im = ax2.imshow(np.atleast_2d(x).T, cmap="Spectral_r", norm=norm, aspect="auto")
cbar = fig.colorbar(im ,cax=ax3,ax=ax2, orientation="horizontal")
选择该函数,使其独立于其参数将任何范围映射到范围上[0,1]
,以便可以使用颜色图。该参数mid
确定应将哪个值映射到颜色图的中间。这将是0
在这种情况下。参数s1
和s2
确定颜色图在两个方向上的压缩程度。
设置mid = np.mean(vmin, vmax), s1=1, s2=1
将恢复原始缩放。
为了选择好的参数,可以使用一些滑块来查看实时更新的图。
from matplotlib.widgets import Slider
midax = plt.axes([0.1, 0.04, 0.2, 0.03], facecolor="lightblue")
s1ax = plt.axes([0.4, 0.04, 0.2, 0.03], facecolor="lightblue")
s2ax = plt.axes([0.7, 0.04, 0.2, 0.03], facecolor="lightblue")
mid = Slider(midax, 'Midpoint', x[0], x[-1], valinit=0)
s1 = Slider(s1ax, 'S1', 0.5, 6, valinit=1.7)
s2 = Slider(s2ax, 'S2', 0.5, 6, valinit=4)
def update(val):
norm=SqueezedNorm(vmin=-13, vmax=4, mid=mid.val, s1=s1.val, s2=s2.val)
im.set_norm(norm)
cbar.update_bruteforce(im)
line.set_ydata(norm(x))
fig.canvas.draw_idle()
mid.on_changed(update)
s1.on_changed(update)
s2.on_changed(update)
fig.subplots_adjust(bottom=0.15)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句