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

如何结合BoundaryNorm与DivergingNorm实现离散自定义中心色图?

解决方案:结合离散边界与自定义色图中心对齐

这确实是个很实用的需求——既要保留自定义的离散色阶边界,又要让指定值对齐发散色图的中心,不用每次都重新制作整个色图。下面给你两种可行的方法:

方法一:自定义归一化类(推荐)

我们可以继承Matplotlib的Normalize基类,自己实现一个同时支持自定义离散边界和指定中心的归一化逻辑。这样既能保留BoundaryNorm的离散层级特性,又能像DivergingNorm那样让指定值精准对齐色图中心。

import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np

class CustomDivergingBoundaryNorm(colors.Normalize):
    def __init__(self, boundaries, vcenter, clip=False):
        self.boundaries = np.array(boundaries)
        self.vcenter = vcenter
        # 定位中心值在边界列表中的位置
        self.center_idx = np.searchsorted(self.boundaries, self.vcenter)
        # 拆分边界为中心上下两部分
        self.lower_bounds = self.boundaries[:self.center_idx+1]
        self.upper_bounds = self.boundaries[self.center_idx:]
        # 计算上下区间的取值范围
        self.lower_range = self.vcenter - self.lower_bounds[0]
        self.upper_range = self.upper_bounds[-1] - self.vcenter
        super().__init__(vmin=self.boundaries[0], vmax=self.boundaries[-1], clip=clip)

    def __call__(self, value, clip=None):
        value = np.asarray(value)
        # 处理低于中心值的区域,映射到色图0-0.5区间
        lower_mask = value <= self.vcenter
        lower_vals = (value[lower_mask] - self.lower_bounds[0]) / self.lower_range * 0.5
        # 处理高于中心值的区域,映射到色图0.5-1区间
        upper_mask = value > self.vcenter
        upper_vals = 0.5 + (value[upper_mask] - self.vcenter) / self.upper_range * 0.5
        # 合并结果并处理超出边界的情况
        result = np.empty_like(value, dtype=np.float64)
        result[lower_mask] = lower_vals
        result[upper_mask] = upper_vals
        result[value < self.vmin] = 0.0
        result[value > self.vmax] = 1.0
        return result

# 测试用例
frac_levels = [0, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 5.0, 7.0, 10.0, 15.0]
vcenter = 1.0
# 初始化自定义归一化规则
custom_norm = CustomDivergingBoundaryNorm(boundaries=frac_levels, vcenter=vcenter)
# 选用发散色图(比如RdBu_r)
cmap = plt.get_cmap('RdBu_r')

# 生成测试数据
data = np.random.uniform(0, 15, (10,10))

# 绘图并添加带离散刻度的色条
fig, ax = plt.subplots()
im = ax.imshow(data, norm=custom_norm, cmap=cmap)
cbar = plt.colorbar(im, boundaries=frac_levels, ticks=frac_levels)
plt.show()

这个类的核心逻辑是:把边界拆分为中心上下两部分,分别将上下区间的值映射到色图的前半段和后半段,确保vcenter正好对应色图的中间位置,同时完全保留你自定义的离散边界。

方法二:手动分配色图颜色

如果不想写自定义类,也可以手动拆分发散色图的颜色,按区间数量分配到中心上下的离散层级中,同样能实现需求。

import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np

frac_levels = [0, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 5.0, 7.0, 10.0, 15.0]
vcenter = 1.0
cmap_name = 'RdBu_r'

# 找到中心值在边界列表中的索引
center_idx = frac_levels.index(vcenter)
# 计算中心上下的区间数量
n_lower = center_idx  # 低于中心的区间数(0→0.25、0.25→0.5、0.5→0.75,共3个)
n_upper = len(frac_levels) - center_idx - 1  # 高于中心的区间数(1→2、2→3...10→15,共6个)

# 拆分发散色图的颜色
cmap = plt.get_cmap(cmap_name)
# 下部分颜色:从色图起始到中心(0→0.5),取对应数量的颜色
lower_colors = cmap(np.linspace(0, 0.5, n_lower+1))
# 上部分颜色:从中心到色图末尾(0.5→1),取对应数量的颜色
upper_colors = cmap(np.linspace(0.5, 1, n_upper+1))
# 合并颜色(去掉重复的中心颜色)
custom_cmap = colors.ListedColormap(np.vstack([lower_colors[:-1], upper_colors]))

# 配合BoundaryNorm使用
frac_norm = colors.BoundaryNorm(boundaries=frac_levels, ncolors=custom_cmap.N)

# 测试绘图
data = np.random.uniform(0,15,(10,10))
fig, ax = plt.subplots()
im = ax.imshow(data, norm=frac_norm, cmap=custom_cmap)
cbar = plt.colorbar(im, boundaries=frac_levels, ticks=frac_levels)
plt.show()

这个方法更直观:先把发散色图拆成中心上下两段,根据中心前后的区间数量分配对应数量的颜色,最后合并成自定义色图,再搭配BoundaryNorm实现离散层级的显示。

两种方法各有优劣:方法一灵活性更高,不管边界怎么调整,只要指定vcenter就能自动对齐中心;方法二更适合快速调整颜色分配逻辑。你可以根据自己的场景选择~

内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 09:32:54