Python 高效计算稀疏数组的列式和,其中每个非零元素为1
我有一大堆格式的数据。当然大多数元素都是零,我还知道所有非零元素的值都是1。我想计算矩阵中行的不同子集的和。目前,我正在做以下工作:Python 高效计算稀疏数组的列式和,其中每个非零元素为1,python,arrays,numpy,scipy,sparse-matrix,Python,Arrays,Numpy,Scipy,Sparse Matrix,我有一大堆格式的数据。当然大多数元素都是零,我还知道所有非零元素的值都是1。我想计算矩阵中行的不同子集的和。目前,我正在做以下工作: 将numpy导入为np 将scipy作为sp导入 导入scipy.sparse #创建一些分布稀疏的数据 数据=np.随机选择((0,1),大小=(10002000),p=(0.95,0.05)) data=sp.sparse.csr_矩阵(数据,dtype='int8') #在行的随机子集上生成列式和 nrand=1000 对于范围内的k(nrand): ind
将numpy导入为np
将scipy作为sp导入
导入scipy.sparse
#创建一些分布稀疏的数据
数据=np.随机选择((0,1),大小=(10002000),p=(0.95,0.05))
data=sp.sparse.csr_矩阵(数据,dtype='int8')
#在行的随机子集上生成列式和
nrand=1000
对于范围内的k(nrand):
inds=np.random.choice(data.shape[0],size=100,replace=False)
#60%的时间都花在这里
提取的行=数据[IND]
#20%的时间都花在这里
行总和=提取的行总和(轴=0)
最后几行是更大的计算管道中的瓶颈。正如我在代码中所注释的,60%的时间用于从随机索引中分割数据,20%用于计算实际总和
在我看来,我应该能够利用我对数组中数据的了解(即稀疏矩阵中的任何非零值都是1;没有其他值存在)来更有效地计算这些和。不幸的是,我不知道怎么做。只处理
数据。索引
也许?我尝试过其他稀疏结构(例如CSC矩阵),以及首先转换为密集数组,但这些方法都比CSR矩阵方法慢。众所周知,稀疏矩阵的索引速度相对较慢。通过直接访问数据属性来解决这个问题有很多问题
但首先是一些时间安排。使用数据
和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
转换成一个适当的1数组并进行乘法
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
。但时间是相似的,结果也是一样的
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
,而第一个维度是数据。尽管b
是一个数组,但任务实际上是委托给数据及其矩阵乘积的——以一种不那么透明的方式
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)
可以工作,但速度较慢
较新的numpython/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\u矩阵(b),data.A)
将数据
转换为密集数组,并使用-
运行时测试-
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
这里可能对您有用的两个函数是rows,cols=extracted\u rows.nonzero()
,它为您提供非零组件的索引,也可能是np.count\u nonzero()
,它在一个麻木的数组中统计非零项,但不幸的是,这种方法在实际应用程序中消耗了太多内存。这很好,这是一个巨大的速度提升!但是,请注意,您的代码(最终代码段)本身并没有正确运行:b
必须是一个Numpymatrix
,因此:b=np.asmatrix(…)
,就像原始的sparse.spmatrix.sum
实现一样。否则,Numpy将尝试计算elementwise乘积,这将不起作用。(还要注意,np.dot
对稀疏矩阵不起作用,csr\u矩阵.dot
在这里不适用,因为这里的csr矩阵在等式的右侧。)非常感谢!asmatrix
没有多大区别。查看我的编辑。
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