要操作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秒
使用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 ms
用这些方法读取Scipy稀疏矩阵根本没有效率。有没有更快的方法来读取这些矩阵?
一个类似的问题,但涉及设置稀疏值,而不仅仅是读取它们:
python/scipy/numpy 中高效的增量稀疏矩阵
更多关于使用底层表示访问值的信息
在scipy 中从稀疏矩阵的每一行中有效地选择随机非零列
还有
为什么scipy-csr矩阵的行索引比numpy数组慢
为什么lil_matrix和dok_matrix与dicts中的常见dict相比如此缓慢?
看看M.nonzero
的作用:
A = self.tocoo()
nz_mask = A.data != 0
return (A.row[nz_mask],A.col[nz_mask])
它将矩阵转换为coo
格式,并在过滤掉.data
属性中的任何"杂散"0之后返回.row
和.col
属性。
所以你可以跳过中间人,直接使用这些属性:
A = lil.tocoo()
for i,j,d in zip(A.row, A.col, A.data):
a[i,j] = d
这几乎和toarray
:一样好
In [595]: %%timeit
.....: aa = M.tocoo()
.....: for i,j,d in zip(aa.row,aa.col,aa.data):
.....: A[i,j]=d
.....:
100 loops, best of 3: 14.3 ms per loop
In [596]: timeit arr=M.toarray()
100 loops, best of 3: 12.3 ms per loop
但如果你的目标真的是一个数组,你不需要迭代
In [603]: %%timeit
.....: A=np.empty(M.shape,M.dtype)
.....: aa=M.tocoo()
.....: A[aa.row,aa.col]=aa.data
.....:
100 loops, best of 3: 8.22 ms per loop
我使用@Thoran的2种方法的时间是:
100 loops, best of 3: 5.81 ms per loop
100 loops, best of 3: 17.9 ms per loop
大致相同的时间。
尝试读取原始数据。Scipy稀疏矩阵存储在Numpy ndarrays中,每个ndarrays具有不同的格式。
读取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矩阵来说,从原始数据中读取有点不太像蟒蛇,但速度是值得的。
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矩阵,将所有方法放在一个地方。