【问题标题】:why is row indexing of scipy csr matrices slower compared to numpy arrays为什么 scipy csr 矩阵的行索引比 numpy 数组慢
【发布时间】:2016-03-04 18:45:11
【问题描述】:

我不确定自己做错了什么,但与 numpy 数组相比,索引 scipy csr_matrix 的行似乎慢了大约 2 倍(参见下面的代码)。

csr 矩阵的行索引是否应该比密集矩阵更快,因为只提取了很少的非零元素,如下例所示?

是否有技巧可以使 scipy csr 矩阵的行索引更快?

import numpy as np
import timeit
from scipy.sparse import csr_matrix

# Generate random matrix
A = np.random.rand(5000, 1000)

# Make A sparse
A[:, 4:] =0

# Create sparse matrix
A_sparse = csr_matrix(A)

# Create row indexing functions
def index_A_dense():
    A[4]

def index_A_dense_copy():
    a = A[4].copy()

def index_A_sparse():
    A_sparse[4]

timeit.timeit(index_A_sparse, number=10000)
Out: 0.6668063667304978
timeit.timeit(index_A_dense, number=10000)
Out: 0.0029007720424942818
timeit.timeit(index_A_dense_copy, number=10000)
Out: 0.00979283193282754

提前致谢!

【问题讨论】:

  • 我不记得稀疏矩阵的情况,但是对于数组,A[4] 只是做一个视图。
  • 谢谢!我更新了上面的帖子,以显示制作副本而不是视图所需的时间。

标签: python numpy time scipy sparse-matrix


【解决方案1】:

我在下面演示的简短答案是构建一个新的稀疏矩阵是昂贵的。存在不依赖于行数或特定行中非零元素的数量的显着开销。


稀疏矩阵的数据表示与密集数组的数据表示完全不同。数组将数据存储在一个连续的缓冲区中,并有效地使用shapestrides 迭代选定的值。这些值,加上索引,精确定义在缓冲区中,它将找到数据。将这些 N 字节从一个位置复制到另一个位置是整个操作中相对较小的一部分。

稀疏矩阵将数据存储在多个数组(或其他结构)中,其中包含索引和数据。然后选择一行需要查找相关索引,并使用选定的索引和数据构造一个新的稀疏矩阵。 sparse 包中有已编译的代码,但没有 numpy 数组那么多的低级代码。

为了说明我会制作一个小矩阵,而且不是那么密集,所以我们没有很多空行:

In [259]: A = (sparse.rand(5,5,.4,'csr')*20).floor()
In [260]: A
Out[260]: 
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 10 stored elements in Compressed Sparse Row format>

dense 等价物和行副本:

In [262]: Ad=A.A
In [263]: Ad
Out[263]: 
array([[  0.,   0.,   0.,   0.,  10.],
       [  0.,   0.,   0.,   0.,   0.],
       [ 17.,  16.,  14.,  19.,   6.],
       [  0.,   0.,   1.,   0.,   0.],
       [ 14.,   0.,   9.,   0.,   0.]])
In [264]: Ad[4,:]
Out[264]: array([ 14.,   0.,   9.,   0.,   0.])
In [265]: timeit Ad[4,:].copy()
100000 loops, best of 3: 4.58 µs per loop

矩阵行:

In [266]: A[4,:]
Out[266]: 
<1x5 sparse matrix of type '<class 'numpy.float64'>'
    with 2 stored elements in Compressed Sparse Row format>

查看这个csr 矩阵的数据表示,3 个一维数组:

In [267]: A.data
Out[267]: array([  0.,  10.,  17.,  16.,  14.,  19.,   6.,   1.,  14.,   9.])  
In [268]: A.indices
Out[268]: array([3, 4, 0, 1, 2, 3, 4, 2, 0, 2], dtype=int32)
In [269]: A.indptr
Out[269]: array([ 0,  2,  2,  7,  8, 10], dtype=int32)

以下是选择行的方式(但在编译后的代码中):

In [270]: A.indices[A.indptr[4]:A.indptr[5]]
Out[270]: array([0, 2], dtype=int32)
In [271]: A.data[A.indptr[4]:A.indptr[5]]
Out[271]: array([ 14.,   9.])

“行”是另一个稀疏矩阵,具有相同类型的数据数组:

In [272]: A[4,:].indptr
Out[272]: array([0, 2])
In [273]: A[4,:].indices
Out[273]: array([0, 2])
In [274]: timeit A[4,:]

是的,稀疏矩阵的计时很慢。不知道实际选择数据花费了多少时间,构建新矩阵花费了多少时间。

10000 loops, best of 3: 145 µs per loop
In [275]: timeit Ad[4,:].copy()
100000 loops, best of 3: 4.56 µs per loop

lil 格式可能更容易理解,因为数据和索引存储在子列表中,每行一个。

In [276]: Al=A.tolil()
In [277]: Al.data
Out[277]: array([[0.0, 10.0], [], [17.0, 16.0, 14.0, 19.0, 6.0], [1.0], [14.0, 9.0]], dtype=object)
In [278]: Al.rows
Out[278]: array([[3, 4], [], [0, 1, 2, 3, 4], [2], [0, 2]], dtype=object)
In [279]: Al[4,:].data
Out[279]: array([[14.0, 9.0]], dtype=object)
In [280]: Al[4,:].rows
Out[280]: array([[0, 2]], dtype=object)

在处理紧凑的编译代码时,像这样的速度比较是有意义的,其中字节从一部分内存移动到另一个内存会耗费大量时间。在numpyscipy 中混合使用Python 和编译代码,您不能只计算O(n) 操作。

==============================

这是从A 中选择一行所需的估计时间,以及返回一个新的稀疏矩阵所需的时间:

只需获取数据:

In [292]: %%timeit
d1=A.data[A.indptr[4]:A.indptr[5]]
i1=A.indices[A.indptr[4]:A.indptr[5]]
   .....: 
100000 loops, best of 3: 4.92 µs per loop

加上制作矩阵所需的时间:

In [293]: %%timeit
d1=A.data[A.indptr[4]:A.indptr[5]]
i1=A.indices[A.indptr[4]:A.indptr[5]]
sparse.csr_matrix((d1,([0,0],i1)),shape=(1,5))
   .....: 
1000 loops, best of 3: 445 µs per loop

尝试更简单的coo 矩阵

In [294]: %%timeit
d1=A.data[A.indptr[4]:A.indptr[5]]
i1=A.indices[A.indptr[4]:A.indptr[5]]
sparse.coo_matrix((d1,([0,0],i1)),shape=(1,5))
   .....: 
10000 loops, best of 3: 135 µs per loop

【讨论】:

  • 谢谢!这有很大帮助。现在,大部分计算成本在于制作稀疏矩阵的副本是有道理的。我使用A.dataA.indices 来返回一个视图,它工作得更快!谢谢。
猜你喜欢
  • 2018-07-01
  • 1970-01-01
  • 2018-02-17
  • 1970-01-01
  • 1970-01-01
  • 2019-12-04
  • 1970-01-01
  • 1970-01-01
  • 2016-11-19
相关资源
最近更新 更多