我有一堆SciPy compressed sparse row (CSR)格式的数据。当然大多数元素都是零,我还知道所有非零元素的值都是1.我想计算矩阵行的不同子集的和。目前我正在做以下事情:
import numpy as np
import scipy as sp
import scipy.sparse
# create some data with sparsely distributed ones
data = np.random.choice((0, 1), size=(1000, 2000), p=(0.95, 0.05))
data = sp.sparse.csr_matrix(data, dtype='int8')
# generate column-wise sums over random subsets of rows
nrand = 1000
for k in range(nrand):
inds = np.random.choice(data.shape[0], size=100, replace=False)
# 60% of time is spent here
extracted_rows = data[inds]
# 20% of time is spent here
row_sum = extracted_rows.sum(axis=0)
最后几行存在较大计算流水线的瓶颈。正如我在代码中注释的那样,60%的时间用于从随机索引中切片数据,20%用于计算实际总和。
在我看来,我应该能够使用我对数组中数据的了解(即,稀疏矩阵中的任何非零值将为1;不存在其他值)来更有效地计算这些和。不幸的是,我无法弄清楚如何。或许只处理data.indices
?我已经尝试过其他稀疏结构(例如CSC矩阵),以及首先转换为密集阵列,但这些方法都比这种CSR矩阵方法慢。
答案 0 :(得分:1)
众所周知,稀疏矩阵的索引相对较慢。通过直接访问数据属性,可以解决这个问题。
但首先是一些时间。在您显示时我会使用data
和ind
In [23]: datad=data.A # times at 3.76 ms per loop
In [24]: timeit row_sumd=datad[inds].sum(axis=0)
1000 loops, best of 3: 529 µs per loop
In [25]: timeit row_sum=data[inds].sum(axis=0)
1000 loops, best of 3: 890 µs per loop
In [26]: timeit d=datad[inds]
10000 loops, best of 3: 55.9 µs per loop
In [27]: timeit d=data[inds]
1000 loops, best of 3: 617 µs per loop
稀疏版本比密集版本慢,但不是很多。稀疏索引要慢得多,但它的总和要快一些。
使用矩阵乘积
完成稀疏和def sparse.spmatrix.sum
....
return np.asmatrix(np.ones((1, m), dtype=res_dtype)) * self
这表明更快的方式 - 将inds
转换为适当的1s数组并乘以。
In [49]: %%timeit
....: b=np.zeros((1,data.shape[0]),'int8')
....: b[:,inds]=1
....: rowmul=b*data
....:
1000 loops, best of 3: 587 µs per loop
这使得稀疏操作与等效密集操作一样快。 (但转换为密集的速度要慢得多)
==================
最后一次测试缺少稀疏np.asmatrix
中存在的sum
。但时间相似,结果相同
In [232]: timeit b=np.zeros((1,data.shape[0]),'int8'); b[:,inds]=1; x1=np.asmatrix(b)*data
1000 loops, best of 3: 661 µs per loop
In [233]: timeit b=np.zeros((1,data.shape[0]),'int8'); b[:,inds]=1; x2=b*data
1000 loops, best of 3: 605 µs per loop
一个产生一个矩阵,另一个产生一个数组。但两者都在制作一个矩阵乘积,B
的第二个暗点与data
的第一个相比。尽管b
是一个数组,但该任务实际上是以一种不那么透明的方式委托给data
及其矩阵产品。
In [234]: x1
Out[234]: matrix([[9, 9, 5, ..., 9, 5, 3]], dtype=int8)
In [235]: x2
Out[235]: array([[9, 9, 5, ..., 9, 5, 3]], dtype=int8)
b*data.A
是元素乘法并引发错误; np.dot(b,data.A)
有效,但速度较慢。
较新的numpy/python
有matmul
运算符。我看到了相同的时间模式:
In [280]: timeit b@dataA # dense product
100 loops, best of 3: 2.64 ms per loop
In [281]: timeit b@data.A # slower due to `.A` conversion
100 loops, best of 3: 6.44 ms per loop
In [282]: timeit b@data # sparse product
1000 loops, best of 3: 571 µs per loop
np.dot
也可以将操作委托给sparse
,但您必须小心。我只是用np.dot(csr_matrix(b),data.A)
挂了我的机器。
答案 1 :(得分:1)
以下是将data
转换为密集数组并使用argpartition-based method
以矢量化方式获取所有inds
后的矢量化方法 -
# Number of selections as a parameter
n = 100
# Get inds across all iterations in a vectorized manner as a 2D array.
inds2D = np.random.rand(nrand,data.shape[0]).argpartition(n)[:,:n]
# Index into data with those 2D array indices. Then, convert to dense NumPy array,
# reshape and sum reduce to get the final output
out = np.array(data.todense())[inds2D.ravel()].reshape(nrand,n,-1).sum(1)
运行时测试 -
1)功能定义:
def org_app(nrand,n):
out = np.zeros((nrand,data.shape[1]),dtype=int)
for k in range(nrand):
inds = np.random.choice(data.shape[0], size=n, replace=False)
extracted_rows = data[inds]
out[k] = extracted_rows.sum(axis=0)
return out
def vectorized_app(nrand,n):
inds2D = np.random.rand(nrand,data.shape[0]).argpartition(n)[:,:n]
return np.array(data.todense())[inds2D.ravel()].reshape(nrand,n,-1).sum(1)
时间:
In [205]: # create some data with sparsely distributed ones
...: data = np.random.choice((0, 1), size=(1000, 2000), p=(0.95, 0.05))
...: data = sp.sparse.csr_matrix(data, dtype='int8')
...:
...: # generate column-wise sums over random subsets of rows
...: nrand = 1000
...: n = 100
...:
In [206]: %timeit org_app(nrand,n)
1 loops, best of 3: 1.38 s per loop
In [207]: %timeit vectorized_app(nrand,n)
1 loops, best of 3: 826 ms per loop