如何为contourf图设置零值附近颜色更密、色阶范围逐倍扩大的双向扩展colorbar
自定义色阶零值加密的实现方案
你当前用SymLogNorm得到相反效果的原因是:SymLogNorm在linthresh区间内使用线性映射,该区间内色阶均匀分布,超出区间后才会使用对数映射。要实现零值附近色阶最密、远离零值区域色阶对应数值范围逐倍扩大的需求,可选用以下两种方案:
方案1:手动生成非均匀levels(最简易,适配contourf场景)
contourf本身支持传入自定义非均匀的levels数组,不需要修改归一化规则,就能直接实现色阶间隔的自定义需求,逻辑直观可控:
import numpy as np # 自定义配置参数 linthresh = 1e-5 # 零值附近加密区间的边界 cbar_min = -100 # 色标最小值,按你的实际范围调整 cbar_max = 100 # 色标最大值,按你的实际范围调整 small_step = 1e-6 # 零附近加密区间的步长 exp_base = 2 # 远离零值区域的色阶数值范围扩大倍数 # 生成负半轴大间隔levels neg_exp = np.arange(np.log10(linthresh), np.log10(-cbar_min), np.log10(exp_base)) neg_levels = -np.power(10, neg_exp)[::-1] # 生成零值附近负区间加密levels neg_small = np.arange(-linthresh, 0, small_step) # 生成零值附近正区间加密levels pos_small = np.arange(0, linthresh, small_step) # 生成正半轴大间隔levels pos_exp = np.arange(np.log10(linthresh), np.log10(cbar_max), np.log10(exp_base)) pos_levels = np.power(10, pos_exp) # 合并所有levels,去重后排序 levels = np.unique(np.concatenate([neg_levels, neg_small, pos_small, pos_levels]))
绘图时不需要传入norm参数,直接使用生成的levels即可,双向扩展功能正常生效:
xr.plot.contourf(darray=var_c[ncount, :, :], ax=ax, cmap=colormap_sel, levels=levels, extend="both")
方案2:自定义对称幂律归一化类(适配连续颜色映射场景)
如果你需要连续的颜色映射而非离散色阶,可继承matplotlib.colors.Normalize自定义归一化类实现需求:
from matplotlib.colors import Normalize class SymInvPowerNorm(Normalize): def __init__(self, vmin=None, vmax=None, gamma=0.5, linthresh=1e-5, clip=False): self.gamma = gamma # 控制零附近加密程度,gamma越小零附近色阶越密 self.linthresh = linthresh super().__init__(vmin, vmax, clip) def __call__(self, value, clip=None): if clip is None: clip = self.clip v_range = max(abs(self.vmin), abs(self.vmax)) x = np.asarray(value) / v_range mask_neg = x < 0 x_abs = np.abs(x) # 零附近用幂律压缩实现加密,超出区间用线性映射保证范围逐倍扩大 y = np.where(x_abs < self.linthresh, np.power(x_abs / self.linthresh, self.gamma) * self.linthresh, x_abs) y[mask_neg] *= -1 return np.ma.masked_array((y + 1) / 2, mask=np.isnan(x)) def inverse(self, value): # 实现逆映射保证colorbar显示正常 y = (np.asarray(value) * 2) - 1 mask_neg = y < 0 y_abs = np.abs(y) x = np.where(y_abs < self.linthresh, np.power(y_abs / self.linthresh, 1/self.gamma) * self.linthresh, y_abs) x[mask_neg] *= -1 v_range = max(abs(self.vmin), abs(self.vmax)) return x * v_range
使用时直接替换你原有的SymLogNorm即可,注意不要传入均匀分布的levels参数,避免归一化效果被覆盖:
divnorm = SymInvPowerNorm(vmin=cbar_min, vmax=cbar_max, gamma=0.3, linthresh=1e-5) xr.plot.contourf(darray=var_c[ncount, :, :], ax=ax, cmap=colormap_sel, norm=divnorm, extend="both")
如果colorbar刻度显示不符合预期,可以手动传入方案1中生成的levels作为ticks参数调整刻度位置。
内容的提问来源于stack exchange,提问作者random__human
相关产品推荐
相关产品推荐

