最快的方式访问和放置矩阵中的值

时间:2015-06-08 14:12:35

标签: python numpy scipy sparse-matrix

我写了一个程序,并一直在分析它。瓶颈如下(如果我使用稀疏矩阵):

  26534    0.775    0.000   66.657    0.003 compressed.py:638(__setitem__)
  26534    2.240    0.000   59.438    0.002 compressed.py:688(_set_many)
  13318    2.993    0.000   50.024    0.004 compressed.py:742(_insert_many)
3034231   23.087    0.000   38.101    0.000 defmatrix.py:312(__getitem__)

如果我使用密集矩阵,那么这些操作很慢(必须将init矩阵归零)

3034072   24.902    0.000   41.135    0.000 defmatrix.py:312(__getitem__)
  11780   19.586    0.002   19.586    0.002 {numpy.core.multiarray.zeros}

稀疏矩阵版本更快(193秒对178秒)。但检索和放入行显然是我的瓶颈。我尝试使用take函数,我使用range()创建一个包含行索引的数组。然而,这比我目前正在进行的情况要差得多(比10000倍),对于矩阵X,X[idx,:]用于放置,X.getrow(idx).todense()用于拍摄。

有没有更好的方法来访问和替换这些行?我的矩阵非常大(~100000行20-500列)。

编辑: 我正在使用csr_matrix(但对任何类型的稀疏矩阵都开放 - 这个似乎适合抓取行)。以下是一系列测试,只是为了给出一个MWE。速度约为3E-4s,7E-3s,.1s。这对我来说是令人惊讶的,我想知道是否有一种比顶级方法更快的方法。如果我删除todense()调用稀疏时间减少一半 - 但这似乎仍然很慢。

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

def testFancy(mat,idxs):
    for i in idxs:
        x = mat[i,:]

def testTake(mat,idxs):
    for i in idxs:
        x = mat.take(range(i*50,i*50+50))

def testSparse(mat,idxs):
    for i in idxs:
        x = mat.getrow(i).todense()

mat = np.random.rand(50000,50)
idxs = np.random.randint(50000-1, size=1000)

#fancy
t0 = time()
testFancy(mat,idxs)
t1 = time()
print str(t1-t0)

#take
t0 = time()
testTake(mat,idxs)
t1 = time()
print str(t1-t0)

#sparse
mat = csr_matrix((50000,50))
t0 = time()
testSparse(mat,idxs)
t1 = time()
print str(t1-t0)

2 个答案:

答案 0 :(得分:1)

只需使用花哨的索引,用于获取和设置数组中的行,

import numpy as np
from scipy.sparse import csr_matrix

mat_ds = np.random.rand(50000,50)
mat_csr = csr_matrix((50000,50))
mat_lil = mat_csr.tolil()

idxs = np.random.randint(50000-1, size=1000)

print(mat_sp[idxs, :].todense())
print(mat_csr[idxs, :])

mat_sp[idxs, :] = 2.0 # or any other expression that makes sens here
mat_csr[idxs, :] = 2.0 

如果数组是稀疏的,那不重要。这将比任何带循环的自定义解决方案更快(在我的情况下比testSparse快〜250倍)。

当然,稀疏数组的赋值应该以保留稀疏性的方式完成,否则它将被重新分配,这对csr_matrix来说是昂贵的。例如上面的例子,因为它而产生一个警告。

修改:回复评论。我们考虑只查询一行,

In [1]: %timeit -n 1 mat_csr[0, :].todense()
1 loops, best of 3: 101 µs per loop

In [2]: %timeit -n 1 mat_lil[0, :].todense()
1 loops, best of 3: 157 µs per loop

In [3]: %timeit -n 1 mat_ds[0, :]
The slowest run took 8.25 times longer than the fastest. This could mean that an intermediate result is being cached 
1 loops, best of 3: 954 ns per loop

所以是的,查询密集数组,比稀疏数组快10到100倍,结果转换为密集(无论你使用csr还是lil数组),因为开销较少。没有什么可以做的,你只需要选择是否需要稀疏数组。

答案 1 :(得分:0)

我尝试使用dok_mat:rix。我的测试结果是0.03s,整个应用程序是66s:

3034124   11.833    0.000   20.293    0.000 defmatrix.py:312(__getitem__)

这似乎是一个很好的妥协 - 但我确实想知道它是否会更好。