不用list of lists存稀疏矩阵,因其不压缩零值、遍历效率低、易OOM;应选CSR(行操作多)或CSC(列操作多),构造优先用COO;手动实现COO仅适用于一次性生成且无复杂运算的极简场景。

为什么不用 list of lists 存稀疏矩阵
用嵌套列表 [[0, 0, 5], [0, 0, 0], [2, 0, 0]] 表示稀疏矩阵,内存和计算都浪费。每行都存满 3 个元素,哪怕其中两个是 0;迭代时还得遍历所有 0,无法跳过。Python 的 list 本身不压缩零值,也不支持向量化操作。
- 真实场景中,10⁶×10⁶ 的矩阵可能只有 10⁴ 个非零元,用稠密结构会直接 OOM
- 某些算法(如 PageRank、共现统计)只依赖非零位置和值,零值参与运算是纯噪声
-
numpy.ndarray 默认是稠密的,不能自动识别稀疏性
CSR 和 CSC:选哪个取决于主要操作scipy.sparse.csr_matrix(按行压缩)适合**按行切片、行向量乘法、逐行更新**;scipy.sparse.csc_matrix(按列压缩)适合**按列切片、列向量乘法、频繁列访问**。两者底层都是三个一维数组:data(非零值)、indices(列号或行号)、indptr(行/列起始偏移)。
- 构造时用
coo_matrix 最方便:先收集 (row, col, value) 元组,再转成 csr 或 csc
- 不要直接拼接多个
csr_matrix:scipy.sparse.vstack 和 hstack 是安全的,但反复 .append() 会触发多次重分配
- 修改单个元素(如
mat[2, 3] = 5)在 CSR/CSC 中是 O(n) 操作,因为要维护排序索引;改前请确认是否真需要随机写入
手动实现 COO 结构仅在极特殊场景下值得考虑
如果你明确知道数据一次性生成、永不修改、且只需简单迭代(比如构建图邻接表后只做一次 SpMV),可以手写轻量 COO:
class SimpleCOO:
def __init__(self, rows, cols, data):
self.rows = np.asarray(rows, dtype=np.int32)
self.cols = np.asarray(cols, dtype=np.int32)
self.data = np.asarray(data)
- 这比
scipy.sparse.coo_matrix 少了验证、去重、排序逻辑,内存更省,构造更快
- 但没法直接和
numpy 数组运算,也不能调 .dot();想乘向量得自己写循环或用 np.bincount 聚合
- 一旦需要转 CSR、求逆、解线性方程,还是得喂给
scipy,此时手写结构反而增加转换开销
从 Pandas DataFrame 构建稀疏矩阵的坑
用 pandas.SparseDataFrame 已废弃;现在推荐用 pd.DataFrame.to_sparse()(仅标记稀疏性)或直接导出三元组:
- 错误做法:
scipy.sparse.csr_matrix(df.values) —— 会把整个 df.values 当稠密数组加载,零值全占内存
- 正确做法:提取非零行,例如
mask = df.values != 0
rows, cols = np.where(mask)
data = df.values[mask]
mat = scipy.sparse.csr_matrix((data, (rows, cols)), shape=df.shape)
- 如果
df 列名/索引是字符串,别用 df.stack() 后转 coo,容易因隐式排序打乱行列对应关系
numpy.ndarray 默认是稠密的,不能自动识别稀疏性 scipy.sparse.csr_matrix(按行压缩)适合**按行切片、行向量乘法、逐行更新**;scipy.sparse.csc_matrix(按列压缩)适合**按列切片、列向量乘法、频繁列访问**。两者底层都是三个一维数组:data(非零值)、indices(列号或行号)、indptr(行/列起始偏移)。
- 构造时用
coo_matrix最方便:先收集(row, col, value)元组,再转成csr或csc - 不要直接拼接多个
csr_matrix:scipy.sparse.vstack和hstack是安全的,但反复.append()会触发多次重分配 - 修改单个元素(如
mat[2, 3] = 5)在 CSR/CSC 中是 O(n) 操作,因为要维护排序索引;改前请确认是否真需要随机写入
手动实现 COO 结构仅在极特殊场景下值得考虑
如果你明确知道数据一次性生成、永不修改、且只需简单迭代(比如构建图邻接表后只做一次 SpMV),可以手写轻量 COO:
class SimpleCOO:
def __init__(self, rows, cols, data):
self.rows = np.asarray(rows, dtype=np.int32)
self.cols = np.asarray(cols, dtype=np.int32)
self.data = np.asarray(data)
- 这比
scipy.sparse.coo_matrix 少了验证、去重、排序逻辑,内存更省,构造更快
- 但没法直接和
numpy 数组运算,也不能调 .dot();想乘向量得自己写循环或用 np.bincount 聚合
- 一旦需要转 CSR、求逆、解线性方程,还是得喂给
scipy,此时手写结构反而增加转换开销
从 Pandas DataFrame 构建稀疏矩阵的坑
用 pandas.SparseDataFrame 已废弃;现在推荐用 pd.DataFrame.to_sparse()(仅标记稀疏性)或直接导出三元组:
- 错误做法:
scipy.sparse.csr_matrix(df.values) —— 会把整个 df.values 当稠密数组加载,零值全占内存
- 正确做法:提取非零行,例如
mask = df.values != 0
rows, cols = np.where(mask)
data = df.values[mask]
mat = scipy.sparse.csr_matrix((data, (rows, cols)), shape=df.shape)
- 如果
df 列名/索引是字符串,别用 df.stack() 后转 coo,容易因隐式排序打乱行列对应关系
scipy.sparse.coo_matrix 少了验证、去重、排序逻辑,内存更省,构造更快 numpy 数组运算,也不能调 .dot();想乘向量得自己写循环或用 np.bincount 聚合 scipy,此时手写结构反而增加转换开销 pandas.SparseDataFrame 已废弃;现在推荐用 pd.DataFrame.to_sparse()(仅标记稀疏性)或直接导出三元组:
- 错误做法:
scipy.sparse.csr_matrix(df.values)—— 会把整个df.values当稠密数组加载,零值全占内存 - 正确做法:提取非零行,例如
mask = df.values != 0 rows, cols = np.where(mask) data = df.values[mask] mat = scipy.sparse.csr_matrix((data, (rows, cols)), shape=df.shape)
- 如果
df列名/索引是字符串,别用df.stack()后转coo,容易因隐式排序打乱行列对应关系
稀疏性的收益不是自动来的,它依赖你从数据源头就拒绝存储零,并在整个计算链路中保持结构感知——哪怕只是多写一行 .tocsr(),也可能让后续十次乘法快一个数量级。


















