2016-11-27 64 views
1

我有兩個密集矩陣, [200000,10], [10,100000]。我需要將它們相乘得到矩陣C。我不能直接這樣做,因爲生成的矩陣不適合內存。而且,我只需要矩陣中的幾個元素,比如元素總數的1-2%。我有第三個矩陣W [200000,100000]這是稀疏的,並有正好在我感興趣的矩陣C這些地方非零元素。 有沒有辦法使用W作爲一個「掩碼」,以便產生的矩陣C將是稀疏的,將只包含所需的元素?兩個陣列之間執行矩陣乘法並得到導致僅在掩蔽的地方

回答

3

由於矩陣乘法只是一個點乘積表,我們可以用向量化的方式執行我們需要的特定點乘積。

import numpy as np 
import scipy as sp 

iX, iY = sp.nonzero(W) 
values = np.sum(A[iX]*B[:, iY].T, axis=-1) #batched dot product 
C = sp.sparse.coo_matrix(values, np.asarray([iX,iY]).T) 
+0

在那裏很好的使用'sp.nonzero'!可能是更快的選擇。你介意我在帖子中提到它嗎? :) – Divakar

+0

'sp.nonzero'和'sp.find'幾乎相同 - 返回'coo'格式的屬性。 'find'也只返回'data'屬性。如果'W'已經是'coo',則直接使用它的'row'和'col'屬性。 – hpaulj

+1

'scipy.nonzero'是'np.nonzero',它又轉向'W.nonzero'。在進一步的檢查中,'sparse.find'比較慢,因爲它通過'csr'格式進行一次往返以對重複進行求和。 'iX,iY = W.row,W.col'甚至更快(如果W已經是coo)。 – hpaulj

3

首先,得到W中非零位的索引,然後通過將A中的第i行與B中的第j列相乘,得到結果矩陣的(i,j)元素,並保存結果作爲元組(i,j,res)而不是將其保存爲矩陣(這是保存稀疏矩陣的正確方法)。

+1

如果你多了一點你的答案,我相信這是解決這個問題的正確方法。 –

+0

這當然是一個可能的解決方案。但是,它將包括迭代200多個元素(200000x100000的1%)。我更喜歡矢量化解決方案(如果有的話) – tokarev

+0

如果在W中只有零個元素的完整行,那麼可以將A中的對應行置零,如果您有完整的只有零的列,則相同 - 您可以將零B中的相應元素 - 然後使用'scipy.sparse'來乘以矩陣。 –

3

下面是使用np.einsum用於矢量化溶液中的一種方法 -

from scipy import sparse 
from scipy.sparse import coo_matrix 

# Get row, col for the output array 
r,c,_= sparse.find(W) 

# Get the sum-reduction using valid rows and corresponding cols from A, B 
out = np.einsum('ij,ji->i',A[r],B[:,c]) 

# Store as sparse matrix 
out_sparse = coo_matrix((out, (r, c)), shape=W.shape) 

樣品運行 -

1)輸入:

In [168]: A 
Out[168]: 
array([[4, 6, 1, 1, 1], 
     [0, 8, 1, 3, 7], 
     [2, 8, 3, 2, 2], 
     [3, 4, 1, 6, 3]]) 

In [169]: B 
Out[169]: 
array([[5, 2, 4], 
     [2, 1, 3], 
     [7, 7, 2], 
     [5, 7, 5], 
     [8, 5, 0]]) 

In [176]: W 
Out[176]: 
<4x3 sparse matrix of type '<type 'numpy.bool_'>' 
    with 5 stored elements in Compressed Sparse Row format> 

In [177]: W.toarray() 
Out[177]: 
array([[ True, False, False], 
     [False, False, False], 
     [ True, True, False], 
     [ True, False, True]], dtype=bool) 

2)使用密集陣列,以直接執行計算並稍後驗證結果:

In [171]: (A.dot(B))*W.toarray() 
Out[171]: 
array([[52, 0, 0], 
     [ 0, 0, 0], 
     [73, 57, 0], 
     [84, 0, 56]]) 

3)使用所提出的代碼,並獲得稀疏矩陣輸出:

In [172]: # Using proposed codes 
    ...: r,c,_= sparse.find(W) 
    ...: out = np.einsum('ij,ji->i',A[r],B[:,c]) 
    ...: out_sparse = coo_matrix((out, (r, c)), shape=W.shape) 
    ...: 

4)通過轉換爲密/陣列版本和檢查免受直接版本最後驗證結果 -

In [173]: out_sparse.toarray() 
Out[173]: 
array([[52, 0, 0], 
     [ 0, 0, 0], 
     [73, 57, 0], 
     [84, 0, 56]])