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

如何添加正负值对数缩放的双颜色条(Matplotlib)

解决Matplotlib正负值对数刻度热力图的颜色条重叠问题及优化方案

一、修正原代码的颜色条重叠问题

原代码使用twinx()创建重复轴是导致颜色条布局混乱的核心原因,实际上无需额外轴,直接在同一轴上叠加正负值热力图,再通过工具精确控制颜色条位置即可解决:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
from mpl_toolkits.axes_grid1 import make_axes_locatable

# 生成示例数据
data = np.random.randn(10, 10)
# 用nan替代0,避免对数刻度下的0值报错,同时让非目标区域自动透明
datap = np.where(data > 0, data, np.nan)
datan = np.where(data < 0, np.abs(data), np.nan)

# 计算有效绝对值的极值(排除0)
abs_data = np.abs(data[np.abs(data) > 0])
vmin = abs_data.min()
vmax = abs_data.max()

# 创建画布和主轴
fig, ax = plt.subplots(figsize=(8, 6))

# 叠加绘制正负值热力图
heatmap_p = ax.imshow(datap, cmap='Reds', norm=matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax))
heatmap_n = ax.imshow(datan, cmap='Blues_r', norm=matplotlib.colors.LogNorm(vmin=vmin, vmax=vmax))

# 使用make_axes_locatable工具在主轴右侧创建独立的颜色条轴,避免重叠
divider = make_axes_locatable(ax)
# 第一个颜色条(正值)
cax_p = divider.append_axes("right", size="5%", pad=0.1)
cbar_p = fig.colorbar(heatmap_p, cax=cax_p, label='正值(对数刻度)')
# 第二个颜色条(负值绝对值)
cax_n = divider.append_axes("right", size="5%", pad=0.2)
cbar_n = fig.colorbar(heatmap_n, cax=cax_n, label='负值绝对值(对数刻度)')

ax.set_title('正负值对数热力图')
plt.show()

二、更优的可视化方案:自定义双色对数颜色映射

如果不想用两个颜色条,可以创建统一的双色对数映射,将负值映射到蓝色系、正值映射到红色系,中间0值用中性色填充,仅需一个颜色条即可清晰区分正负与数值大小:

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

# 生成示例数据
data = np.random.randn(10, 10)
abs_max = np.abs(data).max()

# 自定义归一化类:对正负值分别做对数缩放后映射到颜色区间
class SymLogNormCustom(matplotlib.colors.Normalize):
    def __init__(self, vmin=None, vmax=None, clip=False):
        self.vmin = vmin if vmin is not None else -abs_max
        self.vmax = vmax if vmax is not None else abs_max
        self.abs_max = max(abs(self.vmin), self.vmax)
        super().__init__(vmin=self.vmin, vmax=self.vmax, clip=clip)
    
    def __call__(self, value, clip=None):
        # 正值:对数缩放后映射到0.5-1区间
        pos = np.where(value > 0, np.log10(value + 1e-10)/np.log10(self.abs_max + 1e-10), 0)
        pos = 0.5 + 0.5 * pos
        # 负值:取绝对值对数缩放后映射到0-0.5区间
        neg = np.where(value < 0, np.log10(np.abs(value) + 1e-10)/np.log10(self.abs_max + 1e-10), 0)
        neg = 0.5 - 0.5 * neg
        # 0值设为中间色(0.5)
        zero = np.where(value == 0, 0.5, 0)
        return np.ma.masked_array(pos + neg + zero)

# 创建蓝色→白色→红色的双色映射
cmap = matplotlib.colors.LinearSegmentedColormap.from_list(
    'BlueRed', ['darkblue', 'white', 'darkred'], N=256
)

# 绘图
fig, ax = plt.subplots(figsize=(8, 6))
im = ax.imshow(data, cmap=cmap, norm=SymLogNormCustom(vmin=-abs_max, vmax=abs_max))
cbar = fig.colorbar(im, ax=ax, label='数值(正负对数刻度)')

# 自定义颜色条刻度,清晰展示对数区间
ticks = [-abs_max, -abs_max/10, -abs_max/100, 0, abs_max/100, abs_max/10, abs_max]
cbar.set_ticks(ticks)
cbar.set_ticklabels([f'-{abs_max:.1f}', f'-{abs_max/10:.1f}', f'-{abs_max/100:.1f}', '0', 
                     f'{abs_max/100:.1f}', f'{abs_max/10:.1f}', f'{abs_max:.1f}'])

ax.set_title('正负值统一对数热力图')
plt.show()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 16:35:28