You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 22:27:04