设置Colorbar中点值:如何让数值0对应白色
问题:Matplotlib颜色条无法让数值0对应白色的排查
以下代码用于生成指定范围的随机整数,为其分配RGBA颜色并绘制成patch:
import numpy import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.patches as patches import matplotlib.colors as mcolors import matplotlib.colorbar as cbar from matplotlib import cm import random class MidpointNormalize(mpl.colors.Normalize): def __init__(self,vmin=None,vmax=None,midpoint=None,clip=False): self.midpoint=midpoint mpl.colors.Normalize.__init__(self,vmin,vmax,clip) def __call__(self,value,clip=None): x,y=[self.vmin,self.midpoint,self.vmax],[0,0.5,1] return numpy.ma.masked_array(numpy.interp(value,x,y),numpy.isnan(value)) colors=plt.cm.RdBu(numpy.linspace(0,1,256)) colormap=mcolors.LinearSegmentedColormap.from_list('colormap',colors) normalisecolors=MidpointNormalize(vmin=-50,midpoint=0,vmax=100) scalecolors=cm.ScalarMappable(norm=normalisecolors,cmap=colormap) fig,ax=plt.subplots(figsize=(8,8)) ax.set_xlim(0,100) ax.set_ylim(0,100) for x in range(0,100): for y in range(0,100): val=random.randint(-50,100) rgbacolor=scalecolors.to_rgba(val) ax.add_patch(patches.Polygon([(x,y),(x,y+1),(x+1,y+1),(x+1,y)],fill=True,facecolor=rgbacolor)) cax,_=cbar.make_axes(ax,orientation='horizontal',aspect=50,pad=0.12) cb=cbar.ColorbarBase(cax,cmap=colormap,norm=normalisecolors,orientation='horizontal')
我希望让数值0对应颜色条的白色,本来以为第20行的MidpointNormalize设置(将midpoint设为0)能实现,但实际并未生效——当前颜色条里0对应的不是白色,而是偏蓝的色调。
恳请帮忙排查问题。另外请勿建议其他绘图方式,我实际场景需要处理数值模型输出文件,代码量很大,这里只是用随机数简化了示例。
解决方案
问题根源很简单:你用的RdBu默认色板的中点(0.5位置)不是白色,而是淡紫色。你写的MidpointNormalize确实把数值0映射到了色板的0.5位置,但这个位置本身就不是白色,所以才会出现不符合预期的结果。
要解决这个问题,你需要自己构建一个红-白-蓝的渐变色板,明确让色板的中点对应白色,再配合你的MidpointNormalize逻辑,就能让数值0准确对应白色了。
修改步骤:
- 替换原有的色板创建代码,手动定义颜色节点:红色(对应最小值-50)、白色(对应中点0)、蓝色(对应最大值100)
- 其余代码保持不变,你的
MidpointNormalize逻辑是正确的
修改后的关键代码段:
# 替换原有的colormap创建部分 color_nodes = [ (0.0, '#FF0000'), # 红色对应vmin=-50 (0.5, '#FFFFFF'), # 白色对应midpoint=0 (1.0, '#0000FF') # 蓝色对应vmax=100 ] colormap = mcolors.LinearSegmentedColormap.from_list('custom_RdBu', color_nodes, N=256)
把这段代码替换掉原来的colors=plt.cm.RdBu(...)和colormap=mcolors.LinearSegmentedColormap.from_list(...)两行即可。
完整修改后的代码如下:
import numpy import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.patches as patches import matplotlib.colors as mcolors import matplotlib.colorbar as cbar from matplotlib import cm import random class MidpointNormalize(mpl.colors.Normalize): def __init__(self,vmin=None,vmax=None,midpoint=None,clip=False): self.midpoint=midpoint mpl.colors.Normalize.__init__(self,vmin,vmax,clip) def __call__(self,value,clip=None): x,y=[self.vmin,self.midpoint,self.vmax],[0,0.5,1] return numpy.ma.masked_array(numpy.interp(value,x,y),numpy.isnan(value)) # 自定义红-白-蓝colormap color_nodes = [ (0.0, '#FF0000'), (0.5, '#FFFFFF'), (1.0, '#0000FF') ] colormap = mcolors.LinearSegmentedColormap.from_list('custom_RdBu', color_nodes, N=256) normalisecolors=MidpointNormalize(vmin=-50,midpoint=0,vmax=100) scalecolors=cm.ScalarMappable(norm=normalisecolors,cmap=colormap) fig,ax=plt.subplots(figsize=(8,8)) ax.set_xlim(0,100) ax.set_ylim(0,100) for x in range(0,100): for y in range(0,100): val=random.randint(-50,100) rgbacolor=scalecolors.to_rgba(val) ax.add_patch(patches.Polygon([(x,y),(x,y+1),(x+1,y+1),(x+1,y)],fill=True,facecolor=rgbacolor)) cax,_=cbar.make_axes(ax,orientation='horizontal',aspect=50,pad=0.12) cb=cbar.ColorbarBase(cax,cmap=colormap,norm=normalisecolors,orientation='horizontal') plt.show()
运行修改后的代码,数值0就会正确显示为白色,颜色条的渐变也会符合你的预期。
内容的提问来源于stack exchange,提问作者chrisprt
相关产品推荐
相关产品推荐

