文章/Matplotlib/第 21 章
稀疏矩阵结构
Matplotlib · 第 21 / 43 章
529 字约 2 分钟
spy() —— 稀疏矩阵结构
spy() 只回答一个问题:矩阵里哪些位置非零。值多大不重要,所以它把所有非零点画成同一种颜色的小方块。用途:检查稀疏矩阵的带宽、对角线结构、分块结构;调试「这个矩阵到底有多稀疏」。
Python9 行
Axes.spy(
Z, # ndarray 或 scipy.sparse 矩阵
precision=0, # 小于等于此值的当作 0
marker=None, # 给了 marker 就切到标记模式
markersize=None, # 标记大小
aspect="equal", # ★ 非方阵要改成 'auto'
origin="upper", # 同 imshow
**kwargs
)| 参数 | 一句话 | 默认值的影响 |
|---|---|---|
Z |
矩阵 | 支持 scipy.sparse,不用先 .toarray() |
precision |
零的阈值 | 0:任何非零都画 |
marker |
标记形状 | None → 走 imshow;给了 → 走 Line2D |
markersize |
标记大小 | 仅标记模式有效 |
aspect |
格子形状 | 'equal',非方阵会被压扁 |
origin |
原点位置 | 'upper' |
precision:把小值当零
Python13 行
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(42)
Z = np.zeros((20, 20))
idx = rng.choice(400, 60, replace=False)
Z.flat[idx] = rng.random(60) * 0.9 + 0.1
Z[3, 7] = 0.02 # 两个「几乎为零」的杂质
Z[12, 15] = 0.03
fig, ax = plt.subplots()
ax.spy(Z)
plt.show()
Python2 行
ax.spy(Z) # precision=0:0.02 和 0.03 也算非零,画出来
ax.spy(Z, precision=0.1) # 小于 0.1 的当作 0,那两个格子消失这不是「灵敏度调节」,而是数值噪声过滤:迭代求解器、近似求逆常常留下 1e-17 这种残渣,precision=1e-10 才能看到真实的稀疏结构。

marker:换个渲染后端
这一个参数会把返回类型换掉:
Python7 行
sp = ax.spy(Z)
print(type(sp).__name__)
# 输出: AxesImage
sp = ax.spy(Z, marker="s", markersize=5)
print(type(sp).__name__)
# 输出: Line2D不传 marker → 底层用 imshow 渲染,一个像素块是一个非零元素,大矩阵更快。
传 marker → 每个非零点变成一个 marker,返回单个 Line2D(不是 list!),风格更统一、可以逐点设边色,但大矩阵会明显变慢。
markersize 只在标记模式下有效,imshow 模式下改它没有任何效果。
aspect:非方阵必须改
Python1 行
ax.spy(Z[:8, :20], aspect="auto") # 8×20 的矩阵默认 'equal' 会把它压成一条扁带——和 imshow / matshow 完全一样的坑。
返回对象
Python14 行
sp = ax.spy(np.eye(4))
print(type(sp).__name__)
# 输出: AxesImage
if hasattr(sp, "set_cmap"): # imshow 模式
sp.set_cmap("gray")
sp.set_clim(0, 1)
else: # marker 模式
sp.set_markersize(5)
sp.set_markerfacecolor("#C44E52")
sp2 = ax.spy(np.eye(4), marker="s")
print(type(sp2).__name__, len(sp2.get_xdata()))
# 输出: Line2D 4稳妥写法就是上面这个 hasattr 判断——返回类型取决于有没有传 marker,这是 spy 唯一需要小心的地方。
注意事项
- 返回类型随
marker变化:不给marker是AxesImage,给了是Line2D。 marker模式下返回的是一个Line2D,不是 list,不要len()。aspect默认'equal',非方阵会被压扁。precision是「小于等于就当作零」,不是相对阈值。scipy.sparse矩阵可以直接传,不需要.toarray()——反而转成稠密矩阵可能直接撑爆内存。- 大矩阵别用
marker模式,逐个画 marker 会慢到不可用。
综合示例:三种稀疏结构
Python27 行
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
n = 60
diag = np.diag(np.ones(n)) + np.diag(np.ones(n - 1), 1) + np.diag(np.ones(n - 1), -1)
band = np.diag(np.ones(n)) * 4 + np.diag(np.ones(n - 2), 2) + np.diag(np.ones(n - 2), -2)
rng = np.random.default_rng(6)
blocky = np.zeros((n, n))
for b in range(0, n, 12):
blocky[b:b + 12, b:b + 12] = rng.random((12, 12)) * 0.9 + 0.1
blocky[blocky < 0.55] = 0
fig, axes = plt.subplots(1, 3, figsize=(14, 4.6))
for ax, mat, title in zip(
axes,
[diag, band, blocky],
["(a) 三对角矩阵", "(b) 带状矩阵(带宽 2)", "(c) 分块结构"]):
ax.spy(mat, markersize=3, color="#1f77b4")
ax.set_title(title)
plt.tight_layout()
plt.show()三个图一眼就能读出矩阵的结构:(a) 一条对角带、(b) 带宽为 2、(c) 每 12 个一组的块——这正是 spy 存在的意义,换成 imshow 反而要跟色阶和抗锯齿纠缠。