有效地累积稀疏scipy矩阵的集合

to94eoyn  于 2022-11-09  发布在  其他
关注(0)|答案(5)|浏览(156)

我有一个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中没有什么对我来说是明显的。

**更新:**根据user 545424的建议,我尝试了另一种方法,即对稀疏矩阵求和,并将稀疏矩阵求和为密集矩阵。下面的代码显示了在可比时间内运行的所有方法(Python 2.6.6在amd 64 Debian上/Squeeze在四核i7上)

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倍左右......但是,考虑到可以跳过的零加法次数,您希望看到的数量级改进是无法比拟的。

9o685dep

9o685dep1#

我想我已经找到了一种方法,如果你的矩阵非常稀疏,可以把它加速到原来的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
ubby3x7f

ubby3x7f2#

@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
gkl3eglg

gkl3eglg3#

在转换成密集矩阵之前,你不能把它们加在一起吗?

>>> sum(my_sparse_matrices[1:],my_sparse_matrices[0]).todense()
d5vmydt9

d5vmydt94#

这并不是一个完整的答案(我也希望看到更详细的答案),但通过不创建中间结果,您可以轻松获得两个或更多的改进因素:

def sum_dense():
    return sum([m.toarray() for m in M])

def sum_dense2():
    return sum((m.toarray() for m in M))

在我的机器(YMMV)上,这是最快的计算。通过将求和放在()而不是[]中,我们在求和之前构造了一个生成器而不是整个列表。

b0zn9rqh

b0zn9rqh5#

转换为二维数组并使用内置的稀疏矩阵乘法。这比@user545424的方法更快。

import numpy as np
from scipy.sparse import csr_matrix

m = [np.zeros((100,100)) for i in range(1000)]
for x in m:
   x.ravel()[np.random.randint(0,x.size,10)] = 1.0

m = [csr_matrix(x) for x in m]

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

def sum_sparse2(m):
    n_idx = []
    count = 0
    data = []
    indptr = [0]
    for a in m:
        b = a.indptr
        c = np.repeat(np.arange(b.shape[0]-1), b[1:] - b[:-1])
        n_idx.append(np.ravel_multi_index((c,a.indices), dims=a.shape))
        data.append(a.data)
        count += len(a.indices)
        indptr.append(count)

    data = np.concatenate(data)
    indptr = np.array(indptr)
    n_idx = np.concatenate(n_idx)
    mc = csr_matrix((data, n_idx, indptr), shape=(1000,100*100))

    res_sum = (np.ones(1000) @ mc).reshape((100,100))
    return res_sum

%timeit -r 10 sum_sparse2(m)

# 6.46 ms ± 145 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)

%timeit -r 10 sum_sparse(m)

# 10.3 ms ± 114 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)

相关问题