Como posso converter de forma eficiente uma matriz esparsa em uma matriz esparsa simpática?
Eu tenho uma matriz A com as seguintes propriedades.
<1047x1047 sparse matrix of type '<class 'numpy.float64'>'
with 888344 stored elements in Compressed Sparse Column format>
A tem esse conteúdo.
array([[ 1.00000000e+00, -5.85786642e-17, -3.97082034e-17, ...,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 6.82195979e-17, 1.00000000e+00, -4.11166786e-17, ...,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[-4.98202332e-17, 1.13957868e-17, 1.00000000e+00, ...,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
...,
[ 4.56847824e-15, 1.32261454e-14, -7.22890998e-15, ...,
1.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[-9.11597396e-15, -2.28796167e-14, 1.26624823e-14, ...,
0.00000000e+00, 1.00000000e+00, 0.00000000e+00],
[ 1.80765584e-14, 1.93779820e-14, -1.36520100e-14, ...,
0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
Agora estou tentando criar uma matriz sparse sympy a partir desta matriz sparse scipy.
from sympy.matrices import SparseMatrix
A = SparseMatrix(A)
Mas recebo esta mensagem de erro.
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().
Estou confuso porque esta matriz não tem entradas lógicas.
Obrigado por qualquer ajuda!
Respostas
O erro
Quando você receber um erro que você não entende, reserve um tempo para olhar o traceback. Ou pelo menos mostre para nós!
In [288]: M = sparse.random(5,5,.2, 'csr')
In [289]: M
Out[289]:
<5x5 sparse matrix of type '<class 'numpy.float64'>'
with 5 stored elements in Compressed Sparse Row format>
In [290]: print(M)
(1, 1) 0.17737340878962138
(2, 2) 0.12362174819457106
(2, 3) 0.24324155883057885
(3, 0) 0.7666429046432961
(3, 4) 0.21848551209470246
In [291]: SparseMatrix(M)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-291-cca56ea35868> in <module>
----> 1 SparseMatrix(M)
/usr/local/lib/python3.6/dist-packages/sympy/matrices/sparse.py in __new__(cls, *args, **kwargs)
206 else:
207 # handle full matrix forms with _handle_creation_inputs
--> 208 r, c, _list = Matrix._handle_creation_inputs(*args)
209 self.rows = r
210 self.cols = c
/usr/local/lib/python3.6/dist-packages/sympy/matrices/matrices.py in _handle_creation_inputs(cls, *args, **kwargs)
1070 if 0 in row.shape:
1071 continue
-> 1072 elif not row:
1073 continue
1074
/usr/local/lib/python3.6/dist-packages/scipy/sparse/base.py in __bool__(self)
281 return self.nnz != 0
282 else:
--> 283 raise ValueError("The truth value of an array with more than one "
284 "element is ambiguous. Use a.any() or a.all().")
285 __nonzero__ = __bool__
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().
Um entendimento completo requer a leitura do sympy
código, mas uma olhada rápida indica que ele está tentando lidar com sua entrada como uma "matriz completa" e olha as linhas. O erro não é o resultado de você fazer operações lógicas nas entradas, mas isso sympy
é fazer um teste lógico em sua matriz esparsa. Ele está tentando verificar se a linha está vazia (para poder ignorá-la).
SparseMatrix
docs podem não ser os mais claros, mas a maioria dos exemplos mostra um dicionário de pontos ou uma matriz simples de TODOS os valores mais a forma ou uma lista irregular de listas. Eu suspeito que ele está tentando tratar sua matriz dessa forma, olhando para ela linha por linha.
Mas a linha de em M
si é uma matriz esparsa:
In [295]: [row for row in M]
Out[295]:
[<1x5 sparse matrix of type '<class 'numpy.float64'>'
with 0 stored elements in Compressed Sparse Row format>,
<1x5 sparse matrix of type '<class 'numpy.float64'>'
with 1 stored elements in Compressed Sparse Row format>,
...]
E tentar verificar se essa linha está vazia not row
produz este erro:
In [296]: not [row for row in M][0]
...
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().
Portanto, claramente SparseMatrix
não é possível lidar com uma scipy.sparse
matriz como ela está (pelo menos não no formato csr
ou csc
e provavelmente não nos outros. Além disso, scipy.sparse
não é mencionado em nenhum lugar nos SparseMatrix
documentos!
de matriz densa
Converter a matriz esparsa em seu equivalente denso funciona:
In [297]: M.A
Out[297]:
array([[0. , 0. , 0. , 0. , 0. ],
[0. , 0.17737341, 0. , 0. , 0. ],
[0. , 0. , 0.12362175, 0.24324156, 0. ],
[0.7666429 , 0. , 0. , 0. , 0.21848551],
[0. , 0. , 0. , 0. , 0. ]])
In [298]: SparseMatrix(M.A)
Out[298]:
⎡ 0 0 0 0 0 ⎤
...⎦
Ou uma lista de listas:
SparseMatrix(M.A.tolist())
de dict
O dok
formato armazena uma matriz esparsa como um dict
, que então pode ser
In [305]: dict(M.todok())
Out[305]:
{(3, 0): 0.7666429046432961,
(1, 1): 0.17737340878962138,
(2, 2): 0.12362174819457106,
(2, 3): 0.24324155883057885,
(3, 4): 0.21848551209470246}
O que funciona bem como uma entrada:
SparseMatrix(5,5,dict(M.todok()))
Não sei o que é mais eficiente. Geralmente, ao trabalhar sympy
conosco (ou pelo menos eu), não se preocupe com a eficiência. Basta fazê-lo funcionar é o suficiente. A eficiência é mais relevante numpy/scipy
onde os arrays podem ser grandes e o uso de métodos numpy compilados rápidos faz uma grande diferença na velocidade.
Finalmente - numpy
e sympy
não estão integrados. Isso também se aplica às versões esparsas. sympy
é construído em Python, não numpy
. Portanto, as entradas na forma de listas e dictos fazem mais sentido.
from sympy.matrices import SparseMatrix
import scipy.sparse as sps
A = sps.random(100, 10, format="dok")
B = SparseMatrix(100, 10, dict(A.items()))
Da perspectiva de quem gosta de estruturas de memória eficientes, isso é como olhar para o abismo. Mas vai funcionar.
Esta é uma versão simplificada do seu erro.
from scipy import sparse
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])
A = sparse.csc_matrix((data, (row, col)), shape=(3, 3))
O mesmo A
ocorre com uma matriz esparsa com 6 elementos:
<3x3 sparse matrix of type '<class 'numpy.intc'>'
with 6 stored elements in Compressed Sparse Column format>
Chamá SparseMatrix()
-lo retorna o mesmo tipo de erro que você. Você pode querer converter A
em matriz numpy primeiro:
>>> SparseMatrix(A.todense())
Matrix([
[1, 0, 2],
[0, 0, 3],
[4, 5, 6]])