如何在 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 即可。
备注:内容仅供参考。
