如何更快地读取/遍历/切片Scipy稀疏矩阵(LIL,CSR,COO,DOK)?

时间:2016-05-03 19:47:41

标签: python numpy scipy

为了操纵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稀疏矩阵根本就没有效率。有没有更快的方法来阅读这些矩阵?

2 个答案:

答案 0 :(得分:2)

一个类似的问题,但处理设置稀疏值,而不是只读它们:

Efficient incremental sparse matrix in python / scipy / numpy

有关使用基础表示法访问值的更多信息

Efficiently select random non-zero column from each row of sparse matrix in scipy

另外

why is row indexing of scipy csr matrices slower compared to numpy arrays

Why are lil_matrix and dok_matrix so slow compared to common dict of dicts?

看看M.nonzero做了什么:

    A = self.tocoo()
    nz_mask = A.data != 0
    return (A.row[nz_mask],A.col[nz_mask])

在过滤掉coo属性中的任何“迷路”0后,它会将矩阵转换为.row格式并返回.col.data属性。

所以你可以跳过中间人并直接使用这些属性:

 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

同样的球场。

答案 1 :(得分:1)

尝试阅读原始数据。 Scipy稀疏矩阵存储在Numpy 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矩阵,从原始数据中读取它的pythonic要小一些,但它的速度是值得的。

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

this DBSCAN implementation中使用了类似的方法。

读取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矩阵,将所有方法放在一个地方。