目录4

文章Matplotlib第 20 章

矩阵可视化

Matplotlib · 第 20 / 43 章

matshow() —— 矩阵可视化

matshow() 是 imshow() 的一行封装,专门用来「看矩阵」:它把 aspect 强制成 'equal',并把刻度移到顶部(这是线性代数的书写习惯——矩阵的行列标号在上下方,不在下方)。

Python1 行
Axes.matshow(self, Z, **kwargs)

matshow 自己只有 Z 一个参数,其余 **kwargs 全部原样透传给 imshow。所以 cmap、vmin、vmax、alpha、interpolation、origin 都能用。

与 imshow 的差别 imshow matshow
aspect 'equal'(但可传 'auto' 覆盖) 强制 'equal'
刻度位置 底部 / 左侧 顶部 / 左侧
参数 几十个 只有 Z + 透传

三栏对比:aspect 才是真正的差别

Python5 行
mat = rng.random((5, 30))        # 非方阵:差别最明显

ax.imshow(mat)                   # 默认 'equal' → 压成一条扁带
ax.matshow(mat)                  # 同样 'equal',但刻度跑到顶部
ax.matshow(mat, aspect="auto")   # 覆盖掉强制值,铺满

imshow / matshow / matshow(aspect=‘auto’) 三栏对比

注意第三栏:matshow 的 'equal' 是默认值而不是硬编码——aspect="auto" 依然能覆盖它。所以「matshow 强制等比例」这句话更准确的说法是「matshow 把默认值从 auto 改成了 equal」。

返回对象
Python13 行
im = ax.matshow(Z)                 # 和 imshow 同一个类
print(type(im).__name__)
# 输出: AxesImage

fig.colorbar(im, ax=ax)
im.set_clim(0, 1)
im.set_cmap("magma")

print(ax.get_aspect())             # 数值形式,1.0 就是 'equal'
# 输出: 1.0

ax.set_aspect("auto")              # 事后覆盖
plt.show()

matshow 返回的也是 AxesImage,用法和 imshow 完全一致。ax.get_aspect() 返回数值(1.0)而不是字符串('equal')——两者等价,但判断时注意别拿字符串去比。

注意事项

  • matshow 把刻度放在顶部,想让它们在底部得手动 ax.xaxis.set_ticks_position("bottom")。
  • aspect='equal' 让非方阵矩阵极扁;要看清格子必须显式传 aspect="auto"。
  • 除了 Z,所有参数都是透传给 imshow 的,imshow 的坑它全有(origin、vmin 等)。
  • 只是「看一个矩阵」时 matshow 更省事;要做热力图排版(带 label、colorbar、多子图)还是用 imshow 更可控。

综合示例:矩阵 + 刻度贴顶 + 色块数值

Python41 行
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

A = np.array([[4, -2, 1, 0],
              [-2, 4, -2, 1],
              [1, -2, 4, -2],
              [0, 1, -2, 4]], float)
labels = ["x₁", "x₂", "x₃", "x₄"]

fig, axes = plt.subplots(1, 2, figsize=(11, 4.4))

# ---- (a) 默认 matshow:刻度在顶部,格子正方 ----
im = axes[0].matshow(A, cmap="PuOr_r", vmin=-4, vmax=4)
axes[0].set_xticks(range(4), labels)
axes[0].set_yticks(range(4), labels)
for i in range(4):
    for j in range(4):
        axes[0].text(j, i, A[i, j], ha="center", va="center", fontsize=10)
fig.colorbar(im, ax=axes[0], shrink=0.85)
axes[0].set_title("(a) matshow 默认(刻度在上)")

# ---- (b) 转成热力图排版:刻度回到底部 + 白线分格 ----
im2 = axes[1].matshow(A, cmap="PuOr_r", vmin=-4, vmax=4, aspect="auto")
axes[1].xaxis.set_ticks_position("bottom")
axes[1].set_xticks(range(4), labels)
axes[1].set_yticks(range(4), labels)
for i in range(4):
    for j in range(4):
        axes[1].text(j, i, A[i, j], ha="center", va="center", fontsize=10)
axes[1].set_xticks(np.arange(-0.5, 4, 1), minor=True)
axes[1].set_yticks(np.arange(-0.5, 4, 1), minor=True)
axes[1].grid(which="minor", color="white", linewidth=1.5)
axes[1].tick_params(which="minor", length=0)
fig.colorbar(im2, ax=axes[1], shrink=0.85)
axes[1].set_title("(b) 刻度移到底部 + 白线分格")

plt.tight_layout()
plt.show()

(b) 那个「次刻度打在白线位置 + 只画网格」的套路,是把 matshow 改造成标准热力图排版的通用做法。