如何更快地读取/遍历/切片Scipy稀疏矩阵(LIL,CSR,COO,DOK)?
问题内容:
通常,要使用Scipy矩阵,请使用内置方法。但是有时您需要读取矩阵数据以将其分配给非稀疏数据类型。为了演示,我创建了一个随机的LIL稀疏矩阵,并使用不同的方法将其转换为Numpy数组(纯python数据类型会更好!)。
from __future__ import print_function
from scipy.sparse import rand, csr_matrix, lil_matrix
import numpy as np
dim = 1000
lil = rand(dim, dim, density=0.01, format='lil', dtype=np.float32, random_state=0)
print('number of nonzero elements:', lil.nnz)
arr = np.zeros(shape=(dim,dim), dtype=float)
非零元素数:10000
通过索引阅读
%%timeit -n3
for i in xrange(dim):
for j in xrange(dim):
arr[i,j] = lil[i,j]
3个循环,每个循环最好3:6.42 s
使用nonzero()
方法
%%timeit -n3
nnz = lil.nonzero() # indices of nonzero values
for i, j in zip(nnz[0], nnz[1]):
arr[i,j] = lil[i,j]
3个循环,最好为3:每个循环75.8 ms
使用内置方法直接转换为数组
这 不是 读取矩阵数据的通用解决方案,因此不算作解决方案。
%timeit -n3 arr = lil.toarray()
3个循环,每循环最好3:7.85毫秒
用这些方法读取Scipy稀疏矩阵根本没有效率。有没有更快的方法来读取这些矩阵?
问题答案:
尝试读取原始数据。Scipy稀疏矩阵存储在Numpy ndarray中,每个矩阵具有不同的格式。
读取LIL稀疏矩阵的原始数据
%%timeit -n3
for i, (row, data) in enumerate(zip(lil.rows, lil.data)):
for j, val in zip(row, data):
arr[i,j] = val
3 loops, best of 3: 4.61 ms per loop
读取CSR稀疏矩阵的原始数据
对于csr矩阵,从原始数据中读取的Python语言要少一些,但是值得这样做。
csr = lil.tocsr()
%%timeit -n3
start = 0
for i, end in enumerate(csr.indptr[1:]):
for j, val in zip(csr.indices[start:end], csr.data[start:end]):
arr[i,j] = val
start = end
3 loops, best of 3: 8.14 ms per loop
此DBSCAN实现中使用了类似的方法。
读取COO稀疏矩阵的原始数据
%%timeit -n3
for i,j,d in zip(coo.row, coo.col, coo.data):
arr[i,j] = d
3 loops, best of 3: 5.97 ms per loop
根据这些有限的测试:
- COO矩阵:最干净
- LIL矩阵:最快
- CSR矩阵:最慢和最丑。唯一的好处是,与CSR的转换非常快。
编辑:从@hpaulj,我添加了COO矩阵以将所有方法都放在一个位置。