2017-07-28 31 views
5

我想遍歷一個CSR矩陣的行和列的總和除以每個元素,類似這樣的位置:大NumPy的SciPy的CSR矩陣,行明智的操作

numpy divide row by row sum

我的問題是我正在處理一個大矩陣:(96582,350138)

並且當從鏈接的帖子應用該操作時,由於返回的矩陣是密集的,所以它擴大了我的記憶。

所以這是我第一次嘗試:

for row in counts: 
    row = row/row.sum() 

不幸的是,這並不影響基質可言,所以我想出了第二個想法,以創建一個新的CSR矩陣和CONCAT行使用vstack:

from scipy import sparse 
import time 

start_time = curr_time = time.time() 
mtx = sparse.csr_matrix((0, counts.shape[1])) 
for i, row in enumerate(counts): 
    prob_row = row/row.sum() 
    mtx = sparse.vstack([mtx, prob_row]) 
    if i % 1000 == 0: 
     delta_time = time.time() - curr_time 
     total_time = time.time() - start_time 
     curr_time = time.time() 
     print('step: %i, total time: %i, delta_time: %i' % (i, total_time, delta_time)) 

這種運作良好,但一些迭代之後它變得越來越慢:

step: 0, total time: 0, delta_time: 0 
step: 1000, total time: 1, delta_time: 1 
step: 2000, total time: 5, delta_time: 4 
step: 3000, total time: 12, delta_time: 6 
step: 4000, total time: 23, delta_time: 11 
step: 5000, total time: 38, delta_time: 14 
step: 6000, total time: 55, delta_time: 17 
step: 7000, total time: 88, delta_time: 32 
step: 8000, total time: 136, delta_time: 47 
step: 9000, total time: 190, delta_time: 53 
step: 10000, total time: 250, delta_time: 59 
step: 11000, total time: 315, delta_time: 65 
step: 12000, total time: 386, delta_time: 70 
step: 13000, total time: 462, delta_time: 76 
step: 14000, total time: 543, delta_time: 81 
step: 15000, total time: 630, delta_time: 86 
step: 16000, total time: 722, delta_time: 92 
step: 17000, total time: 820, delta_time: 97 

有何建議?任何想法爲什麼vstack變得越來越慢?

+1

見https://stackoverflow.com/a/45339754和https://stackoverflow.com/q/44080315 – hpaulj

+0

與密集數組一樣,循環中的重複級聯很慢。在列表中累積結果並執行'vstack'更快。 – hpaulj

回答

4

vstackO(n)操作,因爲它需要爲結果分配內存,然後將作爲參數傳遞的所有數組的內容複製到結果數組中。

您可以簡單地使用multiply的運做:

>>> res = counts.multiply(1/counts.sum(1)) # multiply with inverse 
>>> res.todense() 
matrix([[ 0.33333333, 0.  , 0.66666667], 
     [ 0.  , 0.  , 1.  ], 
     [ 0.26666667, 0.33333333, 0.4  ]]) 

但它也很容易使用np.lib.stride_tricks.as_strided做你想要的操作(相對高性能的)。這個as_strided函數還允許在數組上執行更復雜的操作(如果沒有方法或函數)。使用scipy documentation的例子CSR

例如:

>>> from scipy.sparse import csr_matrix 
>>> import numpy as np 
>>> row = np.array([0,0,1,2,2,2]) 
>>> col = np.array([0,2,2,0,1,2]) 
>>> data = np.array([1.,2,3,4,5,6]) 
>>> counts = csr_matrix((data,(row,col)), shape=(3,3)) 
>>> counts.todense() 
matrix([[ 1., 0., 2.], 
     [ 0., 0., 3.], 
     [ 4., 5., 6.]]) 

您可以通過將其分割每行的總和是這樣的:

>>> row_start_stop = np.lib.stride_tricks.as_strided(counts.indptr, 
                shape=(counts.shape[0], 2), 
                strides=2*counts.indptr.strides) 
>>> for start, stop in row_start_stop: 
... row = counts.data[start:stop] 
... row /= row.sum() 
>>> counts.todense() 
matrix([[ 0.33333333, 0.  , 0.66666667], 
     [ 0.  , 0.  , 1.  ], 
     [ 0.26666667, 0.33333333, 0.4  ]]) 
+1

很好的答案,您更新行的方式比我的效率高得多。像一百次!這應該是被接受的答案。 – Anis

2

編輯

@MSeifert答案更有效率,這應該是正確的做法。我認爲寫作counts[i, :]意味着一些列切片完成,我沒有意識到。該文檔明確指出這些對csr_matrix的操作確實是低效的。方式確實是一個很好的例子。

回答

的醫生說排切片是有效的,我認爲你應該做

for i in range(counts.shape[0]): 
    counts[i,:] /= counts[i,:].sum() 

這樣,你在地方編輯矩陣,它保持稀疏,你不必使用vstack 。我不知道這是最高效的操作,但至少你不應該有內存問題,並沒有因爲被計算的行沒有減速效果:

import time() 
s = time.time() 
for i in range(counts.shape[0]): 
    counts[i, :] /= (counts[i, :].sum() + 1) 
    if i % 1000 == 0: 
     e = time.time() 
     if i > 0: 
      print i, e-s 
     s = time.time() 

性能:

1000 6.00199794769 2000 6.02894091606 3000 7.44459486008 4000 7.10011601448 5000 6.16998195648 6000 7.79510307312 7000 7.00139117241 8000 7.37821507454 9000 7.28075814247 ...

性能爲MSeifert的回答是:

row_start_stop = np.lib.stride_tricks.as_strided(counts.indptr, shape=(counts.shape[0], 2), 
               strides=2*counts.indptr.strides) 
for i, (start, stop) in enumerate(row_start_stop): 
    row = counts.data[start:stop] 
    row /= row.sum() 
    if i % 1000 == 0: 
     e = time.time() 
     if i > 0: 
      print i,e-s 
     s = time.time() 

1000 0.00735783576965 2000 0.0108380317688 3000 0.0102109909058 4000 0.0131571292877 5000 0.00670218467712 6000 0.00608897209167 7000 0.00663685798645 8000 0.0164499282837 9000 0.0061981678009 ... 至於爲什麼使用至很慢,@ MSeifert答案很棒。

+0

只是出於好奇,'爲項目計數:item/= item.sum()'票價?我已經確信,接受的答案應該是Mseifert's,這是對隱式和顯式切片之間性能差異的個人好奇心 – Uvar

+0

問題在於OP的問題。當在計數中爲'item'做'''時,'''item'''實際上不是對實際計數行的引用。這就是爲什麼修改不會影響矩陣計數。所以我可以衡量表演,但我真的不明白這一點。 – Anis

+0

也許我對稀疏矩陣的理解只是不瞭解而已。 :/無論如何,正如你在我的答案中可以看到的那樣,對於密集矩陣,你可以用這種方式對實際行進行引用。以爲它會以相同的方式工作..:$ – Uvar