Estoy tratando de deshacerme del ciclo for y en su lugar hacer una multiplicación de matriz-matriz para disminuir el tiempo de procesamiento cuando la matriz weights es muy grande:

import numpy as np
sequence = [np.random.random(10), np.random.random(10), np.random.random(10)]

weights = np.array([[0.1,0.3,0.6],[0.5,0.2,0.3],[0.1,0.8,0.1]])
Cov_matrix = np.matrix(np.cov(sequence))
results = []
for w in weights:
    result = np.matrix(w)*Cov_matrix*np.matrix(w).T
    results.append(result.A)

Dónde:

Cov_matrix es una 3x3 matriz
weights es una matriz de n longitud con matrices n 1x3 en ella.

¿Hay alguna manera de multiplicar / mapear weights a Cov_matrix y omitir el ciclo for? No estoy muy familiarizado con todas las funciones de numpy.

0
Shyam PATEL 16 oct. 2018 a las 22:56

2 respuestas

La mejor respuesta

Me gustaría reiterar lo que ya se dijo en otra respuesta: la clase np.matrix tiene muchas más desventajas que ventajas en estos días, y sugiero pasar al uso de la clase np.array solo. La multiplicación de matrices de matrices se puede escribir fácilmente usando el operador @, por lo que la notación es en la mayoría de los casos tan elegante como para la clase matrix (y las matrices no tienen varias restricciones que las matrices).

Con eso fuera del camino, lo que necesita se puede hacer en términos de una llamada a np.einsum. Necesitamos contraer ciertos índices de tres matrices mientras mantenemos un solo índice en dos matrices. Es decir, queremos realizar w_{ij} * Cov_{jk} * w.T_{ki} con una suma sobre j, k, lo que nos da una matriz con i índices. La siguiente llamada a einsum servirá:

res = np.einsum('ij,jk,ik->i', weights, Cov_matrix, weights)

Tenga en cuenta que lo anterior le dará una única matriz 1d, mientras que originalmente tenía una lista de matrices con forma (1,1). Sospecho que el resultado anterior tendrá aún más sentido. Además, tenga en cuenta que omití la transposición en el segundo argumento weights, y es por eso que los índices de suma correspondientes aparecen como ik en lugar de ki. Esto debería ser un poco más rápido.

Para demostrar que lo anterior da el mismo resultado:

In [8]: results # original
Out[8]: [array([[0.02803215]]), array([[0.02280609]]), array([[0.0318784]])]

In [9]: res # einsum
Out[9]: array([0.02803215, 0.02280609, 0.0318784 ])
1
Andras Deak 16 oct. 2018 a las 20:48

Se puede lograr lo mismo trabajando con los pesos como una matriz y luego mirando los elementos diagonales del resultado. A saber:

np.diag(weights.dot(Cov_matrix).dot(weights.transpose()))

Que da:

array([0.03553664, 0.02394509, 0.03765553])

Esto hace más cálculos de los necesarios (calcula fuera de las diagonales), por lo que tal vez alguien sugiera un método más eficiente.

Nota: Sugeriría alejarse lentamente de np.matrix y en su lugar trabajar con np.array. Se necesita un poco de tiempo para acostumbrarse a no poder hacer A*b, pero pagará dividendos a largo plazo. Aquí hay una discusión relacionada.

1
Andras Deak 16 oct. 2018 a las 20:40