目录6

文章Matplotlib第 22 章

伪彩色网格

Matplotlib · 第 22 / 43 章

pcolormesh() —— 伪彩色网格(支持非均匀网格)

pcolormesh() 是 imshow() 的「坐标灵活版」。核心差别只有一个:x、y 可以是任意递增数组(间距不等),而 imshow 只能等间距。所以凡是「坐标非均匀」的场——对数坐标、实测采样点、不规则网格——都得用它。

代价是它画的是一个个四边形(QuadMesh),比 imshow 的整块位图慢,大网格下差距明显。

Python11 行
Axes.pcolormesh(
    *args,                 # (C,) / (X, Y, C)
    alpha=None,
    norm=None,
    cmap=None,             # 同 imshow
    vmin=None, vmax=None,
    shading=None,          # ★ 'flat' / 'nearest' / 'gouraud' / 'auto'
    antialiased=False,     # 默认 False;开格线时设 True 减少细缝
    data=None,
    **kwargs               # 传给 QuadMesh:edgecolors / linewidth / rasterized
)
参数 一句话 默认值的影响
*args (C) 或 (X, Y, C) 只给 C 时用索引当坐标
shading 着色方式 None → 'auto'(按尺寸猜),猜错就报 TypeError
cmap / vmin / vmax 同 imshow —
edgecolors / linewidth 格子描边 白色细线让网格可读
antialiased 抗锯齿 False;开描边时容易出细缝
rasterized 光栅化导出 大网格导 PDF/SVG 必加

shading:四种取值,坐标尺寸要求不同

shading='flat'     X、Y 比 C 各多 1  → 坐标是「顶点」,C 是格子
shading='nearest'  X、Y 与 C 同形     → 坐标是「格子中心」
shading='gouraud'  X、Y 与 C 同形     → 同上 + 双线性插值(光滑)
shading='auto'     按 X、Y、C 的尺寸自动选 flat 或 nearest

尺寸对不上,报错信息很直白:

Python2 行
ax.pcolormesh(np.zeros(7), np.zeros(6), np.zeros((5, 6)), shading="nearest")
# TypeError: Dimensions of C (5, 6) are incompatible with X (7) and/or Y (6); see help(pcolormesh)

C 是 5×6,X 给了 7 个 → 这是 'flat' 的尺寸。改 shading="flat" 或者把 X 换成 6 个格子中心就好了。

pcolormesh 的 shading 四态 + 描边

'gouraud' 的代价:颜色在格子之间插值,看起来最漂亮但最失真——图上再也没有「一格一个值」的对应关系。要读数值就别用它。

非均匀网格:这才是它的主场

Python6 行
x = np.array([0, 0.2, 0.5, 1.0, 2.0, 4.0, 8.0])   # 间距逐级翻倍
y = np.array([0, 1, 2, 3, 4, 5])
Xv, Yv = np.meshgrid(x, y)                     # (M+1, N+1) 顶点
C = np.sin(Xv[:-1, :-1]) + np.cos(Yv[:-1, :-1])  # 格子值 (M, N)

ax.pcolormesh(x, y, C, shading="flat")

注意要用 meshgrid 生成顶点、再用 [:-1, :-1] 取格子值——C 永远比顶点少一行一列。这一步写错就是上面那个 TypeError。

edgecolors / linewidth:让格子看得见

Python5 行
xg = np.linspace(0, 10, 9)          # 8 列格子的 9 个左边界
yg = np.linspace(0, 5, 6)           # 5 行格子的 6 个下边界
Cg = np.random.default_rng(1).random((5, 8))

ax.pcolormesh(xg, yg, Cg, cmap="viridis", edgecolors="white", linewidth=0.6)

描白边是热力图的标配,能避免相邻色块糊成一片。副作用是可能出现发丝细缝,这时配 antialiased=True。

返回对象
Python19 行
C = np.random.default_rng(1).random((4, 5))     # 4 行 5 列的格子值
pm = ax.pcolormesh(np.linspace(0, 5, 6),         # 6 个 x 顶点
                   np.linspace(0, 4, 5),         # 5 个 y 顶点
                   C, shading="flat", edgecolors="white", linewidth=0.5)
print(type(pm).__name__)
# 输出: QuadMesh

fig.colorbar(pm, ax=ax)                  # 直接传
print(pm.get_array().shape)
# 输出: (4, 5)

print(pm.get_array().size)
# 输出: 20

pm.set_clim(0, 1)
pm.set_cmap("magma")
pm.set_edgecolor("gray")
pm.set_linewidth(0.4)
plt.show()

QuadMesh 是 Collection 的子类,所以 set_edgecolor / set_linewidth / set_antialiased 这些批量方法都能用。get_array() 返回的是二维 MaskedArray(形状 (nrows, ncols)),所以 len() 拿到的是行数 4、元素总数要用 .size——这一点和旧版本返回扁平数组不同。

注意事项

  • shading 的尺寸规则是最大的坑:flat 要顶点(多 1 个),nearest / gouraud 要中心(同形)。
  • shading=None 时是 'auto',会按尺寸猜;猜不出就报 TypeError,报错信息里直接写了期望的维度。
  • 'gouraud' 会插值,图上不再有「一格一个值」的精确对应。
  • 网格大时比 imshow 慢很多,能等间距就用 imshow。
  • 导 PDF/SVG 时大网格会让文件暴涨,加 rasterized=True。
  • get_array() 返回二维 MaskedArray,len() 是行数不是总数,数元素请用 .size。

综合示例:非均匀场 / 描边热力图 / 对数坐标

Python39 行
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False

fig, axes = plt.subplots(1, 3, figsize=(15, 4.2))

# ---- (a) 非均匀网格:x 间距逐级翻倍 ----
x = np.array([0, 0.2, 0.5, 1.0, 2.0, 4.0, 8.0])
y = np.linspace(0, 5, 7)
Xv, Yv = np.meshgrid(x, y)
C = np.sin(Xv[:-1, :-1]) * np.cos(Yv[:-1, :-1])
im0 = axes[0].pcolormesh(x, y, C, shading="flat", cmap="coolwarm")
fig.colorbar(im0, ax=axes[0])
axes[0].set_title("(a) 非均匀网格(间距翻倍)")

# ---- (b) 规则网格 + 白描边:标准热力图 ----
xg = np.linspace(0, 10, 11)
yg = np.linspace(0, 6, 7)
Cg = np.random.default_rng(2).random((6, 10))
im1 = axes[1].pcolormesh(xg, yg, Cg, cmap="YlOrRd",
                         edgecolors="white", linewidth=0.8, antialiased=True)
fig.colorbar(im1, ax=axes[1])
axes[1].set_title("(b) edgecolors='white' 的标准热力图")

# ---- (c) 极坐标式分布:同心环 ----
theta = np.linspace(0, 2 * np.pi, 41)
r = np.array([0, 0.2, 0.4, 0.6, 0.8, 1.0])
T, R = np.meshgrid(theta, r)
Xc, Yc = R * np.cos(T), R * np.sin(T)
V = np.cos(4 * T) * R
im2 = axes[2].pcolormesh(Xc, Yc, V, shading="gouraud", cmap="twilight")
axes[2].set_aspect("equal")
fig.colorbar(im2, ax=axes[2])
axes[2].set_title("(c) gouraud + 极坐标映射")

plt.tight_layout()
plt.show()

(c) 那招很实用:把网格坐标本身做变换(R·cosθ, R·sinθ),pcolormesh 就会把四边形画成扇形——imshow 永远做不到这一点,因为它只认矩形像素。