
本文介绍多种低内存开销的方式,将大型 scipy.sparse 矩阵直接保存为 .txt 或 .mtx 等纯文本格式,避免调用 .toarray() 导致的内存爆炸,并支持后续精确还原。
本文介绍多种低内存开销的方式,将大型 scipy.sparse 矩阵直接保存为 `.txt` 或 `.mtx` 等纯文本格式,避免调用 `.toarray()` 导致的内存爆炸,并支持后续精确还原。
在处理大规模稀疏矩阵(如图神经网络邻接矩阵、推荐系统协同过滤矩阵)时,常需将其持久化为人类可读或下游工具兼容的文本格式(如 CSV、Matrix Market),但直接调用 A.toarray() 会瞬间将稀疏结构展开为稠密 NumPy 数组,极易触发 MemoryError。幸运的是,scipy 提供了流式逐行转换与标准稀疏格式导出两类内存友好的解决方案。
✅ 方案一:逐行写入稠密文本(适用于中小规模矩阵)
若目标是生成类似 0 1 0\n0 0 1\n1 0 0 的纯稠密文本(如用于调试或简单脚本解析),可利用 CSR 矩阵按行迭代的特性,逐行转为稠密向量并写入文件,全程不加载全量稠密矩阵:
import numpy as np
from scipy import sparse
# 示例:5×5 稀疏单位阵(π 倍)
A = sparse.eye(5, format='csr') * np.pi
# 配置打印精度与宽度,避免科学计数法截断
np.set_printoptions(precision=16, linewidth=1000, suppress=True)
with open('dense_matrix.txt', 'w') as f:
for row in A:
# row 是 1×n 的 sparse matrix,.toarray()[0] 得到 1D numpy array
dense_row = row.toarray()[0]
# 转为字符串并去除括号/逗号,保留空格分隔
line = ' '.join(f'{x:.16g}' for x in dense_row)
f.write(line + '\n')
⚠️ 注意事项:
- 此方法内存占用 ≈ O(nnz + n)(单行稠密向量大小),远低于 O(n²);
- 对超宽矩阵(列数 > 10⁵),.toarray()[0] 仍可能短暂占用较大内存,此时应改用方案二;
- 若需保留 [...] 包裹格式,可用 str(dense_row).strip('[]') 替代手动格式化。
✅ 方案二:使用标准稀疏文本格式(推荐用于生产环境)
scipy 内置的 Matrix Market(.mtx) 和 Harwell-Boeing(.hb) 格式均为纯文本、广泛支持(MATLAB、R、PETSc 等),且完全基于稀疏三元组存储,零内存膨胀:
from scipy import io
# 保存为 Matrix Market 格式(推荐:简洁、通用、带头部注释)
io.mmwrite('matrix.mtx', A) # 生成 matrix.mtx
# 保存为 Harwell-Boeing 格式(更紧凑,适合高性能计算)
io.hb_write('matrix.hb', A) # 生成 matrix.hb
生成的 matrix.mtx 文件内容示例:
%%MatrixMarket matrix coordinate real general % 3 3 3 1 1 3.141592653589793e+00 2 2 3.141592653589793e+00 3 3 3.141592653589793e+00
✅ 优势:
- 文件体积 ≈ O(nnz),与原始稀疏矩阵内存占用同量级;
- 可直接用 io.mmread('matrix.mtx') 或 io.hb_read('matrix.hb') 精确还原为相同格式的 sparse matrix;
- 支持跨语言/平台互操作,是科研与工程部署的事实标准。
✅ 方案三:手动拆解 CSR 三元组(极致控制权)
若需自定义文本结构(如分文件存储 data/indices/indptr),可直接访问 CSR 矩阵底层属性:
# 分别保存三个核心数组
np.savetxt('data.txt', A.data, fmt='%.16g')
np.savetxt('indices.txt', A.indices, fmt='%d')
np.savetxt('indptr.txt', A.indptr, fmt='%d')
# 还原时(无需加载全矩阵)
data = np.loadtxt('data.txt')
indices = np.loadtxt('indices.txt', dtype=np.int32)
indptr = np.loadtxt('indptr.txt', dtype=np.int32)
A_restored = sparse.csr_matrix((data, indices, indptr), shape=A.shape)
? 提示:此方式最灵活,适合构建自定义数据管道,但需自行维护格式一致性。
总结与选型建议
| 场景 | 推荐方案 | 关键理由 |
|---|---|---|
| 需人工查看稠密形式(小矩阵 | 方案一(逐行 toarray) | 直观易读,代码简洁 |
| 与其他工具/团队共享、长期存档 | 方案二(mmwrite) | 标准化、无损、跨平台、零内存压力 |
| 需集成到定制化数据流水线 | 方案三(手动拆解) | 完全可控,便于并行/分片处理 |
无论选择哪种方式,核心原则是:永远避免 .toarray() 全量展开。充分利用稀疏矩阵的迭代能力与标准格式协议,即可在内存受限环境下稳健完成文本导出任务。










