Python >> Tutoriel Python >  >> Python Tag >> SciPy

Symétrisation des matrices creuses scipy

Ok, cela double le nombre d'instructions d'affectation, mais dans l'ensemble, quelle est la pénalité ?

lil est le format le plus efficace pour l'affectation indexée, mais j'ai exploré dans d'autres articles des alternatives. Si je me souviens bien, affectation directe à data et rows attributs d'un lil est plus rapide, bien que cela soit principalement utile lors du remplissage de lignes entières à la fois.

Un dok est également relativement rapide, même si j'ai trouvé cette affectation à un dictionnaire ordinaire, suivie d'une mise à jour du dok était plus rapide. (Un dok est une sous-classe de dictionnaire).

Mais si vous allez le coo route - construction de listes de data , rows et cols valeurs, créant à la fois i,j et j,i termes à la fois n'est pas coûteux. C'est encore mieux si vous pouvez définir un tas de valeurs à la fois, au lieu d'itérer sur tous les i,j .

Ainsi, la création efficace d'une matrice symétrique n'est qu'un sous-ensemble du problème de définition de matrice efficace.

Je ne suis au courant d'aucune fonction de symétrisation dans le paquet clairsemé. Je me demande si l'une des fonctions d'algèbre linéaire a des dispositions symétriques. Je soupçonne que les gestionnaires les plus efficaces supposent simplement que la matrice est un triangle supérieur ou inférieur, sans valeurs symétriques explicites.

Il est possible que vous puissiez créer une matrice tri supérieure, puis copier les valeurs dans la matrice inférieure. Dans le cas dense, le moyen le plus simple consiste simplement à additionner la matrice et sa transposée (et éventuellement à soustraire la diagonale). Mais la sommation de matrice clairsemée est quelque peu inefficace, donc ce n'est peut-être pas le meilleur. Mais je n'ai fait aucun test.

============

La somme de transposition au moins ne me donne aucun avertissement d'efficacité :

In [383]: M=sparse.lil_matrix((10,10),dtype=int)
In [384]: 
In [384]: for i in range(10):
     ...:     for j in range(i,10):
     ...:         v=np.random.randint(0,10)
     ...:         if v>5:
     ...:             M[i,j]=v
     ...:             
In [385]: M
Out[385]: 
<10x10 sparse matrix of type '<class 'numpy.int32'>'
    with 22 stored elements in LInked List format>
In [386]: M.A
Out[386]: 
array([[0, 7, 7, 0, 9, 0, 7, 0, 0, 9],
       [0, 0, 7, 8, 0, 8, 0, 0, 9, 0],
       [0, 0, 0, 7, 0, 0, 9, 0, 8, 0],
       [0, 0, 0, 0, 0, 0, 6, 0, 6, 6],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 8, 9, 0, 8],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 8, 8],
       [0, 0, 0, 0, 0, 0, 0, 0, 6, 8],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

somme de la transposition (moins la diagonale dupliquée) :

In [389]: M+M.T-sparse.diags(M.diagonal(),dtype=int)
Out[389]: 
<10x10 sparse matrix of type '<class 'numpy.int32'>'
    with 43 stored elements in Compressed Sparse Row format>
In [390]: _.A
Out[390]: 
array([[0, 7, 7, 0, 9, 0, 7, 0, 0, 9],
       [7, 0, 7, 8, 0, 8, 0, 0, 9, 0],
       [7, 7, 0, 7, 0, 0, 9, 0, 8, 0],
       [0, 8, 7, 0, 0, 0, 6, 0, 6, 6],
       [9, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 8, 0, 0, 0, 0, 8, 9, 0, 8],
       [7, 0, 9, 6, 0, 8, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 9, 0, 0, 8, 8],
       [0, 9, 8, 6, 0, 0, 0, 8, 6, 8],
       [9, 0, 0, 6, 0, 8, 0, 8, 8, 0]], dtype=int32)

approche de double affectation :

In [391]: M=sparse.lil_matrix((10,10),dtype=int)
In [392]: for i in range(10):
     ...:     for j in range(i,10):
     ...:         v=np.random.randint(0,10)
     ...:         if v>5:
     ...:             M[i,j]=v
     ...:             M[j,i]=v

Je n'ai fait aucun chronométrage.

Un coo approche :

In [398]: data,rows,cols=[],[],[]
In [399]: for i in range(10):
     ...:     for j in range(i,10):
     ...:         v=np.random.randint(0,10)
     ...:         if v>5:
     ...:             if i==j:
     ...:                 # prevent diagonal duplication
     ...:                 data.append(v)
     ...:                 rows.append(i)
     ...:                 cols.append(j)
     ...:             else:
     ...:                 data.extend((v,v))
     ...:                 rows.extend((i,j))
     ...:                 cols.extend((j,i))
     ...:                 
In [400]: sparse.coo_matrix((data,(rows,cols)),shape=(10,10)).A
Out[400]: 
array([[0, 8, 0, 6, 8, 9, 9, 0, 0, 0],
       [8, 7, 0, 0, 0, 6, 0, 8, 0, 0],
       [0, 0, 0, 0, 0, 0, 9, 9, 7, 9],
       [6, 0, 0, 0, 7, 0, 0, 0, 0, 6],
       [8, 0, 0, 7, 0, 0, 8, 0, 0, 0],
       [9, 6, 0, 0, 0, 0, 6, 0, 0, 0],
       [9, 0, 9, 0, 8, 6, 8, 0, 0, 0],
       [0, 8, 9, 0, 0, 0, 0, 6, 0, 6],
       [0, 0, 7, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 9, 6, 0, 0, 0, 6, 0, 9]])

===============

Il peut être un peu plus rapide de créer la matrice tricoo supérieure et de l'étendre vers le bas avec une concaténation de liste (ou de tableau)

In [401]: data,rows,cols=[],[],[]
In [402]: for i in range(10):
     ...:     for j in range(i,10):
     ...:         v=np.random.randint(0,10)
     ...:         if v>5:
     ...:            data.append(v)
     ...:            rows.append(i)
     ...:            cols.append(j)

In [408]: sparse.coo_matrix((data,(rows,cols)),shape=(10,10)).A
Out[408]: 
array([[8, 0, 0, 9, 8, 7, 0, 7, 9, 0],
       [0, 7, 6, 0, 0, 7, 0, 0, 9, 0],
       [0, 0, 9, 8, 0, 9, 6, 0, 0, 6],
...]])

In [409]: data1=data+data
In [410]: rows1=rows+cols
In [411]: cols1=cols+rows
In [412]: sparse.coo_matrix((data1,(rows1,cols1)),shape=(10,10)).A

Cela duplique la diagonale, que je dois traiter d'une manière ou d'une autre (les indices coo en double sont additionnés). Mais cela donne une idée de la façon dont coo les entrées de style peuvent être rassemblées dans des blocs plus grands.


Oui, il existe certainement un moyen plus efficace et plus simple. La réponse de hpaulj devrait fonctionner si vous créez une matrice, mais si vous en avez déjà une, vous pouvez le faire :

rows, cols = sparse_matrix.nonzero()
sparse_matrix[cols, rows] = sparse_matrix[rows, cols]

Cela devrait fonctionner pour tous les types de matrices creuses de scipy sauf coo_matrix.

Edit :noté coo_matrix.