我有一个O(N)NxN scipy.sparse.csr_matrix
的集合,每个稀疏矩阵都有N个元素集的顺序。我想将所有这些矩阵一起添加到一个常规的NxN numpy数组中。 (N大约为1000)。矩阵内非零元素的排列使得得到的和肯定不稀疏(实际上几乎没有剩余零元素)。
目前我正在做
reduce(lambda x,y: x+y,[m.toarray() for m in my_sparse_matrices])
虽然有效,但有点慢:当然,在那里进行的零点无意义处理绝对是可怕的。
有更好的方法吗?我docs中没有任何明显的东西。
更新:根据user545424的建议,我尝试了对稀疏矩阵求和的替代方案,并将稀疏矩阵求和到密集矩阵上。下面的代码显示了在相当的时间内运行的所有方法(关于四核i7的amd64 Debian / Squeeze上的Python 2.6.6)
import numpy as np
import numpy.random
import scipy
import scipy.sparse
import time
N=768
S=768
D=3
def mkrandomsparse():
m=np.zeros((S,S),dtype=np.float32)
r=np.random.random_integers(0,S-1,D*S)
c=np.random.random_integers(0,S-1,D*S)
for e in zip(r,c):
m[e[0],e[1]]=1.0
return scipy.sparse.csr_matrix(m)
M=[mkrandomsparse() for i in xrange(N)]
def plus_dense():
return reduce(lambda x,y: x+y,[m.toarray() for m in M])
def plus_sparse():
return reduce(lambda x,y: x+y,M).toarray()
def sum_dense():
return sum([m.toarray() for m in M])
def sum_sparse():
return sum(M[1:],M[0]).toarray()
def sum_combo(): # Sum the sparse matrices 'onto' a dense matrix?
return sum(M,np.zeros((S,S),dtype=np.float32))
def benchmark(fn):
t0=time.time()
fn()
t1=time.time()
print "{0:16}: {1:.3f}s".format(fn.__name__,t1-t0)
for i in xrange(4):
benchmark(plus_dense)
benchmark(plus_sparse)
benchmark(sum_dense)
benchmark(sum_sparse)
benchmark(sum_combo)
print
并退出
plus_dense : 1.368s
plus_sparse : 1.405s
sum_dense : 1.368s
sum_sparse : 1.406s
sum_combo : 1.039s
虽然通过弄乱N,S,D参数可以得到一种方法或者另一种方法提前2倍左右......但是没有什么比你希望从中看到的数量级改进考虑到零添加的数量,应该可以跳过。
答案 0 :(得分:4)
如果您的矩阵非常稀疏,我认为我已经找到了将速度加快~10倍的方法。
In [1]: from scipy.sparse import csr_matrix
In [2]: def sum_sparse(m):
...: x = np.zeros(m[0].shape)
...: for a in m:
...: ri = np.repeat(np.arange(a.shape[0]),np.diff(a.indptr))
...: x[ri,a.indices] += a.data
...: return x
...:
In [6]: m = [np.zeros((100,100)) for i in range(1000)]
In [7]: for x in m:
...: x.ravel()[np.random.randint(0,x.size,10)] = 1.0
...:
m = [csr_matrix(x) for x in m]
In [17]: (sum(m[1:],m[0]).todense() == sum_sparse(m)).all()
Out[17]: True
In [18]: %timeit sum(m[1:],m[0]).todense()
10 loops, best of 3: 145 ms per loop
In [19]: %timeit sum_sparse(m)
100 loops, best of 3: 18.5 ms per loop
答案 1 :(得分:2)
@ user545424已经发布了可能是最快的解决方案。有同样精神的东西,更具可读性和速度...... nonzero() 有各种有用的应用程序。
def sum_sparse(m):
x = np.zeros(m[0].shape,m[0].dtype)
for a in m:
# old lines
#ri = np.repeat(np.arange(a.shape[0]),np.diff(a.indptr))
#x[ri,a.indices] += a.data
# new line
x[a.nonzero()] += a.data
return x
答案 2 :(得分:1)
在转换为密集矩阵之前,你不能将它们加在一起吗?
>>> sum(my_sparse_matrices[1:],my_sparse_matrices[0]).todense()
答案 3 :(得分:1)
这不是一个完整的答案(我也希望看到更详细的答案),但是你可以通过不创造中间结果来获得两个或更多改进的简单因素:
def sum_dense():
return sum([m.toarray() for m in M])
def sum_dense2():
return sum((m.toarray() for m in M))
在我的机器(YMMV)上,这导致计算速度最快。通过将求和放在()
而不是[]
中,我们在求和之前构造生成器而不是整个列表。