【问题标题】:How to read/traverse/slice Scipy sparse matrices (LIL, CSR, COO, DOK) faster?如何更快地读取/遍历/切片 Scipy 稀疏矩阵(LIL、CSR、COO、DOK)?
【发布时间】:2016-08-29 00:41:34
【问题描述】:

为了操作 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 毫秒

使用内置方法直接转换为数组

这个不是读取矩阵数据的通用解决方案,所以不算解决方案。

%timeit -n3 arr = lil.toarray()

3 个循环,3 个循环中的最佳值:每个循环 7.85 毫秒

使用这些方法读取 Scipy 稀疏矩阵根本没有效率。有没有更快的方法来读取这些矩阵?

【问题讨论】:

  • 迭代密集数组arr的所有值的时间是多少?这并不快。但通常索引稀疏矩阵比索引密集数组慢。如果您真的需要速度,请直接使用矩阵的数据属性 - 以牺牲一般性为代价。
  • 56.4 毫秒。几个月前我实际上了解了原始数据,并使用了@github.com/aahoo/dbscan/blob/master/dbscan.py 代码。我只是想让其他人也知道这件事。顺便说一句,感谢您的出色回答。虽然关于 SO 有类似的问题,但没有一个有一个通用的标题和有组织的答案。这就是为什么我当时找不到他们,我不得不努力学习。

标签: python numpy scipy


【解决方案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 矩阵,从原始数据中读取有点不那么 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

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 矩阵以将所有方法集中在一个地方。

【讨论】:

    【解决方案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 格式并返回.row.col 属性——在过滤掉.data 属性中的任何“杂散”0 之后。

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

     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
    

    同样的时间。

    【讨论】:

      猜你喜欢
      • 2016-10-29
      • 2017-12-04
      • 2023-03-03
      • 2011-11-28
      • 2019-06-11
      • 1970-01-01
      • 2016-03-14
      • 2019-02-12
      • 2019-10-04
      相关资源
      最近更新 更多