2012-03-31 156 views
27

¿cómo puedo cambiar los valores de la diagonal de una matriz en numpy?cambiando los valores de la diagonal de una matriz en numpy

Comprobé Numpy modify ndarray diagonal, pero la función allí no está implementada en numpy v 1.3.0.

digamos que tenemos un np.array X y yo queremos poner todos los valores de la diagonal a 0.

+1

¿Qué versión de numpy estás utilizando? 'np.diag_indices_from' fue agregado en v1.4 – JoshAdel

+0

sí, tienes razón, actualmente estoy usando python v 1.3.0 – pacodelumberg

+0

@LangerHansIslands Espero que te refieras numpy 1.3, no python 1.3 (que salió a mediados de los años noventa). .: p) – Dougal

Respuesta

2
def replaceDiagonal(matrix, replacementList): 
    for i in range(len(replacementList)): 
     matrix[i][i] = replacementList[i] 

donde el tamaño es n en una matriz de n x n.

+3

O 'n = len (replacement_list); matrix [: n,: n] = replacement_list'. Esto hace el ciclo en C en lugar de hacerlo en Python, por lo que será mucho más rápido. – Dougal

+0

@Dougal: Impresionante, no lo sabía. ¿Puedes publicarlo como respuesta? –

+0

Claro, [acaba de hacer] (http://stackoverflow.com/a/9959707/344821). – Dougal

12

Si está utilizando una versión de numpy que no tiene fill_diagonal (el right way para establecer la diagonal a una constante) o diag_indices_from, usted puede hacer esto con bastante facilidad con la gama rebanar:

# assuming a 2d square array 
n = mat.shape[0] 
mat[range(n), range(n)] = 0 

esto es mucho más rápido que un bucle explícito en Python, debido a que el bucle sucede en C y es potencialmente vectorizado.

Una cosa buena de esto es que también puede llenar una diagonal con una lista de elementos, en lugar de un valor constante (como diagflat, pero para modificar una matriz existente en lugar de crear una nueva). Por ejemplo, esto hará que la diagonal de la matriz a 0, 1, 2, ...:

# again assuming 2d square array 
n = mat.shape[0] 
mat[range(n), range(n)] = range(n) 

Si necesita soportar más formas de matriz, esto es más complicado (por eso fill_diagonal es agradable. ..):

m[list(zip(*map(range, m.shape)))] = 0 

(La llamada list sólo es necesario en Python 3, donde zip un iterador)

+0

Para que quede claro para los lectores futuros, 'mat [: n,: n] = 0' establece _el conjunto/matriz_ entero en 0, no solo los elementos diagonales. La versión 'zip' hace el diag. – gorlum0

+0

@ gorlum0 Vaya - gracias por señalar eso. Acabo de editar para arreglarlo (el 'zip' no es realmente necesario allí)'. – Dougal

+0

Rangos fríos, incluso desnudos. Estas cosas implícitas son difíciles de conocer. – gorlum0

9

Aquí es otra buena manera de hacer esto.. Si quieres una vista unidimensional de uso diagonal principal de la matriz:

A.ravel()[:A.shape[1]**2:A.shape[1]+1] 

Para el uso superdiagonal i'th:

A.ravel()[i:max(0,A.shape[1]-i)*A.shape[1]:A.shape[1]+1] 

Para el uso subdiagonal i'th:

A.ravel()[A.shape[1]*i:A.shape[1]*(i+A.shape[1]):A.shape[1]+1] 

O en general, para la diagonal i donde la diagonal principal es 0, las subdiagonales son negativas y las superdiagonales son positivas, use:

A.ravel()[max(i,-A.shape[1]*i):max(0,(A.shape[1]-i))*A.shape[1]:A.shape[1]+1] 

Estos son vistas y no copias, por lo que se ejecutarán más rápido para la extracción de una diagonal, pero los cambios realizados en el nuevo objeto de matriz se aplicará a la matriz original. En mi máquina, estos funcionan más rápido que la función fill_diagonal al establecer la diagonal principal en una constante, pero puede no ser siempre el caso. También se pueden usar para asignar una matriz de valores a una diagonal en lugar de solo una constante.

Notas: para matrices pequeñas, puede ser más rápido utilizar el atributo flat de la matriz NumPy. Si la velocidad es un problema importante, podría valer la pena hacer que A.shape[1] sea una variable local. Además, si la matriz no es contigua, ravel() devolverá una copia, por lo tanto, para asignar valores a una división con franjas, será necesario dividir creativamente la matriz original utilizada para generar la sección con franjas (si es contigua) o para usar el atributo flat.

Además, originalmente se planeó que en NumPy 1.10 y posteriormente el método 'diagonal' de matrices devolverá una vista en lugar de una copia. Ese cambio aún no se ha realizado, pero espero que en algún momento este truco para obtener una vista ya no sea necesario. Ver http://docs.scipy.org/doc/numpy-dev/reference/generated/numpy.diagonal.html

+0

¡Agradable y hacky, me gusta! La única advertencia es que creo que obtendrías el comportamiento 'wrap = True' descrito en [' np.fill_diagonal' docs] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.fill_diagonal.html) Probablemente puedas resolver eso agregando un valor de parada adecuado a tus rebanadas. – Jaime

+0

Gracias, buena captura. Lo edité para arreglar eso y algunas otras cosas más. – IanH

1
>>> a = numpy.random.rand(2,2) 
>>> a 
array([[ 0.41668355, 0.07982691], 
     [ 0.60790982, 0.0314224 ]]) 
>>> a - numpy.diag(numpy.diag(a)) 
array([[ 0.  , 0.07982691], 
     [ 0.60790982, 0.  ]]) 
4

mínima. Código.

X[np.diag_indices_from(X)] = 0. 

screenshot

1

Puede hacer lo siguiente.

Suponiendo que su matriz es matriz 4 * 4.

indices_diagonal = np.diag_indices(4) 

yourarray[indices_diagonal] = Val 
Cuestiones relacionadas