Más numerosas formas de iterar a través de las diagonales ‘ortogonales’ de una matriz 2D

Tengo el siguiente código que itera a lo largo de las diagonales que son ortogonales a las diagonales que normalmente devuelve np.diagonal . Comienza en la posición (0, 0) y avanza hacia la coordenada inferior derecha.

El código funciona según lo previsto, pero no está muy aturdido con todos sus bucles y es ineficiente al tener que crear muchos arreglos para hacer el truco.

Así que me pregunto si hay una manera mejor de hacer esto, porque no veo cómo me movería en mi arreglo o utilizaría los métodos diagonales de numpy para hacerlo de una manera más agradable (aunque creo que hay algunos trucos en los que fallo. para ver).

 import numpy as np A = np.zeros((4,5)) #Construct a distance array of same size that uses (0, 0) as origo #and evaluates distances along first and second dimensions slightly #differently so that no values in the array is the same D = np.zeros(A.shape) for i in range(D.shape[0]): for j in range(D.shape[1]): D[i, j] = i * (1 + 1.0 / (grid_shape[0] + 1)) + j print D #[[ 0. 1. 2. 3. 4. ] # [ 1.05882353 2.05882353 3.05882353 4.05882353 5.05882353] # [ 2.11764706 3.11764706 4.11764706 5.11764706 6.11764706] # [ 3.17647059 4.17647059 5.17647059 6.17647059 7.17647059]] #Make a flat sorted copy rD = D.ravel().copy() rD.sort() #Just to show how it works, assigning incrementing values #iterating along the 'orthagonal' diagonals starting at (0, 0) position for i, v in enumerate(rD): A[D == v] = i print A #[[ 0 1 3 6 10] # [ 2 4 7 11 14] # [ 5 8 12 15 17] # [ 9 13 16 18 19]] 

Editar

Para aclarar, quiero iterar los elementos a lo largo de toda la A pero hacerlo en el orden que invoca el código anterior (que se muestra en la print final).

No es importante en qué dirección va la iteración a lo largo de las diagonales (si se colocaron 1 y 2 colocadas, y 3 y 5, etc. en A ) solo que las diagonales son ortogonales a la diagonal principal de A (la producida por np.diag(A) ).

La solicitud / motivo de esta pregunta se encuentra en mi pregunta anterior (en la parte de la solución al final de esa pregunta): Construir una cuadrícula 2D a partir de una lista de candidatos potencialmente incompleta

Aquí hay una forma que evita los bucles for de Python.

Primero, echemos un vistazo a nuestras tablas de sum:

 import numpy as np grid_shape = (4,5) N = np.prod(grid_shape) y = np.add.outer(np.arange(grid_shape[0]),np.arange(grid_shape[1])) print(y) # [[0 1 2 3 4] # [1 2 3 4 5] # [2 3 4 5 6] # [3 4 5 6 7]] 

La idea clave es que si visitamos las sums en la tabla de sums en orden, estaríamos iterando a través de la matriz en el orden deseado.

Podemos averiguar los índices asociados con esa orden usando np.argsort :

 idx = np.argsort(y.ravel()) print(idx) # [ 0 1 5 2 6 10 3 7 11 15 4 8 12 16 9 13 17 14 18 19] 

idx es de oro. Esencialmente, es todo lo que necesitas para recorrer una matriz 2D de forma (4,5), ya que una matriz 2D es solo una matriz 1D remodelada.

Si su objective final es generar la matriz A que se muestra arriba al final de su publicación, entonces podría usar argsort nuevamente:

 print(np.argsort(idx).reshape(grid_shape[0],-1)) # [[ 0 1 3 6 10] # [ 2 4 7 11 14] # [ 5 8 12 15 17] # [ 9 13 16 18 19]] 

O, alternativamente, si necesita asignar otros valores a A , quizás esto sería más útil:

 A = np.zeros(grid_shape) A1d = A.ravel() A1d[idx] = np.arange(N) # you can change np.arange(N) to any 1D array of shape (N,) print(A) # [[ 0. 1. 3. 6. 10.] # [ 2. 4. 7. 11. 15.] # [ 5. 8. 12. 16. 18.] # [ 9. 13. 14. 17. 19.]] 

Sé que pediste una forma de iterar a través de tu matriz, pero quería mostrar lo anterior porque generar matrices a través de la asignación de toda la matriz o las llamadas de función numpy (como np.argsort) como se hizo anteriormente probablemente será más rápido que usar un bucle de Python . Pero si necesitas usar un bucle de Python, entonces:

 for i, j in enumerate(idx): A1d[j] = i print(A) # [[ 0. 1. 3. 6. 10.] # [ 2. 4. 7. 11. 15.] # [ 5. 8. 12. 16. 18.] # [ 9. 13. 14. 17. 19.]] 
 >>> D array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]) >>> D[::-1].diagonal(offset=1) array([16, 12, 8, 4]) >>> D[::-1].diagonal(offset=-3) array([0]) >>> np.hstack([D[::-1].diagonal(offset=-x) for x in np.arange(-4,4)])[::-1] array([ 0, 1, 5, 2, 6, 10, 3, 7, 11, 15, 4, 8, 12, 16, 9, 13, 17, 14, 18, 19]) 

Más simple siempre que no sea una matriz grande.

No estoy seguro de si esto es lo que realmente quieres, pero quizás:

 >>> import numpy as np >>> ar = np.random.random((4,4)) >>> ar array([[ 0.04844116, 0.10543146, 0.30506354, 0.4813217 ], [ 0.59962641, 0.44428831, 0.16629692, 0.65330539], [ 0.61854927, 0.6385717 , 0.71615447, 0.13172049], [ 0.05001291, 0.41577457, 0.5579213 , 0.7791656 ]]) >>> ar.diagonal() array([ 0.04844116, 0.44428831, 0.71615447, 0.7791656 ]) >>> ar[::-1].diagonal() array([ 0.05001291, 0.6385717 , 0.16629692, 0.4813217 ]) 

Editar Como solución general, para matrices de formas arbitrarias, puede usar

 import numpy as np shape = tuple([np.random.randint(3,10) for i in range(2)]) ar = np.arange(np.prod(shape)).reshape(shape) out = np.hstack([ar[::-1].diagonal(offset=x) \ for x in np.arange(-ar.shape[0]+1,ar.shape[1]-1)]) print ar print out 

dando, por ejemplo

 [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19] [20 21 22 23 24]] [ 0 5 1 10 6 2 15 11 7 3 20 16 12 8 4 21 17 13 9 22 18 14 23 19]