PAYATHON 2026

如何在 Matplotlib 图例中显示 cmap 渐变色块?

数据小许

可以。使用自定义 HandlerBase,在一个 legend 条目中绘制多个相邻的小矩形,就能得到渐变色块,并只显示一个 channels 标签。对于 36 个通道,可以直接使用直方图的 36 种颜色。

为什么普通 patch 不能直接显示渐变?

普通 Patch 的 facecolor 表示一种填充颜色,设置 cmap 不会自动产生渐变。自定义 legend handler 可以控制图例符号的绘制方式,适合处理这个需求。

完整示例

示例使用模拟数据。接入现有代码时,保留自定义 handler,把 colors 换成直方图实际使用的颜色即可。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerBase
from matplotlib.patches import Rectangle


class HandlerGradient(HandlerBase):
    def __init__(self, colors, **kwargs):
        super().__init__(**kwargs)
        self.colors = colors

    def create_artists(
        self, legend, orig_handle,
        xdescent, ydescent, width, height, fontsize, trans
    ):
        n = len(self.colors)
        return [
            Rectangle(
                (-xdescent + i * width / n, -ydescent),
                width / n,
                height,
                facecolor=color,
                edgecolor="none",
                linewidth=0,
                transform=trans,
            )
            for i, color in enumerate(self.colors)
        ]


n_channels = 36
cmap = plt.get_cmap("viridis")
colors = cmap(np.linspace(0, 1, n_channels))

# 模拟数据:每个数组代表一个通道。
rng = np.random.default_rng(42)
data = [
    rng.normal(loc=i / 12, scale=0.8, size=200)
    for i in range(n_channels)
]
bins = np.histogram_bin_edges(np.concatenate(data), bins=30)

fig, ax = plt.subplots()
for values, color in zip(data, colors):
    ax.hist(values, bins=bins, histtype="step", color=color)

# 占位对象只用于匹配 handler,不需要添加到坐标轴。
gradient_handle = object()
ax.legend(
    handles=[gradient_handle],
    labels=["channels"],
    handler_map={gradient_handle: HandlerGradient(colors)},
    handlelength=3,
)

plt.show()

handler_map 指定如何绘制占位对象对应的图例符号。create_artists() 将图例预留区域分成 36 段,每段使用一个通道的颜色;trans 让这些矩形使用正确的图例坐标变换。

颜色和图例需要注意什么?

直方图和图例应共用同一组 colors,以保持颜色顺序一致。如果现有绘图使用了特定的 norm 或通道编号映射,应复用实际映射后的颜色,不要另行均匀采样。

示例中的阶梯渐变由 36 个色块组成,与通道配色一一对应。想让视觉效果更平滑,可以给 handler 传入更多采样颜色,例如 cmap(np.linspace(0, 1, 256))。可以用 handlelength 调整色块长度。

如果读者需要根据颜色读取具体通道编号或数值范围,带刻度的 colorbar 通常更合适。如果只需要标注为 channels 的渐变图例,用示例中的自定义 handler 即可。

备注:内容仅供参考。