2017-01-06 1 views
5

J'ai deux scipy_sparse_csr_matrix 'a' et scipy_sparse_csr_matrix (boolean) 'masque', et je veux mettre les éléments de 'a' à zéro où élément du masque est vrai.Méthode efficace pour mettre les éléments à zéro où masque est vrai sur matrice scipy vide

par exemple

>>>a 
<3x3 sparse matrix of type '<type 'numpy.int32'>' 
    with 4 stored elements in Compressed Sparse Row format> 
>>>a.todense() 
matrix([[0, 0, 3], 
     [0, 1, 5], 
     [7, 0, 0]]) 

>>>mask 
<3x3 sparse matrix of type '<type 'numpy.bool_'>' 
    with 4 stored elements in Compressed Sparse Row format> 
>>>mask.todense() 
matrix([[ True, False, True], 
     [False, False, True], 
     [False, True, False]], dtype=bool) 

Ensuite, je veux obtenir le résultat suivant.

>>>result 
<3x3 sparse matrix of type '<type 'numpy.int32'>' 
    with 2 stored elements in Compressed Sparse Row format> 
>>>result.todense() 
matrix([[0, 0, 0], 
     [0, 1, 0], 
     [7, 0, 0]]) 

je peux le faire par l'opération comme

result = a - a.multiply(mask) 

ou

a -= a.multiply(mask) #I don't care either in-place or copy. 

Mais je pense que les opérations ci-dessus sont inefficaces. Puisque la forme réelle de 'a' et de 'mask' est de 67.108.864 × 2.000.000, ces opérations prennent plusieurs secondes sur un serveur à haute spécification (64 cœurs Xeon, 512 Go de mémoire). Par exemple, 'a' a environ 30 000 000 éléments non nuls, et 'mask' a environ 1 800 000 éléments non-True (True), alors l'opération ci-dessus prend environ 2 secondes.

Existe-t-il un moyen plus efficace de le faire?

Les conditions sont ci-dessous.

  1. a.getnnz()! = Mask.getnnz()
  2. a.shape = mask.shape

Merci!

Autre moyen (essayé)

a.data*=~np.array(mask[a.astype(np.bool)]).flatten();a.eliminate_zeros() #This takes twice the time longer than above method. 
+0

Comment les 'nnz' de' a' et 'mask' se comparent-ils? En plus d'être pas la même chose. Les deux sont également clairsemés? – hpaulj

+0

Merci pour votre réponse, @hpaulj! Je suis désolé de vous confondre. La condition 1 signifie seulement 'Le nombre d'éléments non différents (faux) du masque est différent du nombre d'éléments de a'. – hiroto1228

Répondre

0

Mon impression initiale est que cela se multiplier et soustraire l'approche est raisonnable. Assez souvent, le code sparse implémente des opérations comme une sorte de multiplication, même si les équivalents denses utilisent des méthodes plus directes. La somme éparse sur les lignes ou les colonnes utilise une multiplication matricielle avec la matrice de ligne ou de colonne appropriée de 1s. Même l'indexation de ligne ou de colonne utilise la multiplication matricielle (au moins au format csr). Nous pouvons parfois améliorer les opérations en travaillant directement avec les attributs de la matrice (data, indices, indptr). Mais cela nécessite beaucoup plus de réflexion et d'expérimentation.

Pour les tableaux denses mon premier essai serait

In [611]: a.A*~(mask.A) 
Out[611]: 
array([[0, 0, 0], 
     [0, 1, 0], 
     [7, 0, 0]], dtype=int32) 

Mais il n'y a pas une façon directe de faire not à une matrice clairsemée. Si mask était en effet éparse, ~mask ne le serait pas. Dans votre exemple mask a 4 termes vrai, et 5 Faux, donc une version dense fonctionnerait tout aussi bien:

In [612]: nmask=sparse.csr_matrix(~(mask.A)) 
In [615]: a.multiply(nmask) 
Out[615]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>' 
    with 2 stored elements in Compressed Sparse Row format> 

CSR scipy matrix does not update after updating its values explore le réglage de la diagonale d'une matrice creuse à 0. Il est possible de définir des valeurs de la data attribuer à 0, puis eliminate_zeros une fois à la fin.

L'autre méthode dense est

In [618]: a1=a.A 
In [619]: a1[mask.A]=0 

Cela fonctionne aussi dans sparse - sorte de

In [622]: a2=a.copy() 
In [624]: a2[mask] 
Out[624]: matrix([[0, 3, 5, 0]], dtype=int32) 
In [625]: a2[mask]=0 
/usr/local/lib/python3.5/dist-packages/scipy/sparse/compressed.py:730: SparseEfficiencyWarning: Changing the sparsity structure of a csr_matrix is expensive. lil_matrix is more efficient. 
    SparseEfficiencyWarning) 
In [626]: a2 
Out[626]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>' 
    with 6 stored elements in Compressed Sparse Row format> 

Comme il est indiqué dans la question précédente, nous devons éliminer les zéros:

In [628]: a2.eliminate_zeros() 
In [629]: a2 
Out[629]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>' 
    with 2 stored elements in Compressed Sparse Row format> 
Prenant un indice de l'avertissement de la parcimonie essayons le format lil

In [638]: al=a.tolil() 
In [639]: al[mask] 
Out[639]: 
<1x4 sparse matrix of type '<class 'numpy.int32'>' 
    with 2 stored elements in LInked List format> 
In [640]: al[mask]=0 
In [641]: al 
Out[641]: 
<3x3 sparse matrix of type '<class 'numpy.int32'>' 
    with 2 stored elements in LInked List format> 

Il est intéressant de noter que al[mask] est encore clairsemée, alors que a[mask] est dense. Les 2 formats utilisent différentes méthodes d'indexation.

À certains éléments à faible niveau de parcimonie, il pourrait être utile itérer sur les vrais (non nulle) de mask, fixant les conditions correspondantes de a à zéro directement. Je ne vais pas deviner quant aux vitesses relatives de ces méthodes. Cela doit être testé sur des données réalistes.

+0

Comme vous le dites, je ne peux pas appliquer ~ à la matrice parce que la forme de la matrice est trop grande. J'ai trie un [masque] = 0, mais cela a pris plus d'une minute. Je viens d'avoir une idée de l'utilisation de .data/eliminer_zeros pour votre réponse (ajouter à la dernière question). Je vais essayer cela sur des données réelles le lundi. Merci! – hiroto1228

+0

J'ai essayé la nouvelle méthode sur les données réelles, mais cela a pris deux fois plus de temps que la méthode multiplication. – hiroto1228

+0

Je ne suis pas trop surpris. – hpaulj