python arrays numpy multidimensional-array reshape

python - Intuición e idea detrás de la remodelación de la matriz 4D a la matriz 2D en NumPy



arrays multidimensional-array (3)

Mientras implementaba un Kronecker-product por razones pedagógicas (sin usar el obvio y fácilmente disponible np.kron() ), obtuve una matriz de 4 dimensiones como resultado intermedio, que tengo que remodelar para obtener el resultado final.

Pero, todavía no puedo entender cómo remodelar estas matrices de altas dimensiones. Tengo esta matriz 4D :

array([[[[ 0, 0], [ 0, 0]], [[ 5, 10], [15, 20]]], [[[ 6, 12], [18, 24]], [[ 7, 14], [21, 28]]]])

Esto tiene forma (2, 2, 2, 2) y me gustaría darle una nueva forma (4,4) . Uno podría pensar que esto es obvio que ver con

np.reshape(my4darr, (4,4))

Pero, la reforma anterior no me da el resultado esperado que es:

array([[ 0, 5, 0, 10], [ 6, 7, 12, 14], [ 0, 15, 0, 20], [18, 21, 24, 28]])

Como puede ver, todos los elementos en el resultado esperado están presentes en la matriz 4D . Simplemente no puedo acostumbrarme a hacer la remodelación correctamente según sea necesario. Además de la respuesta, sería muy útil alguna explicación de cómo hacer la reshape de tales matrices de alta dimensión. ¡Gracias!


Idea general para nd a nd transformación

La idea con tal transformación de nd a nd es usar solo dos cosas:

Permutar ejes: para obtener el orden de manera que la versión plana corresponda a la versión plana de la salida. Entonces, si de alguna manera terminas usándolo dos veces, mira de nuevo porque no deberías.

Reformar: para dividir los ejes o llevar la salida final a la forma deseada. La división de los ejes se necesita principalmente al comienzo, cuando la entrada es de baja intensidad y se necesita dividir en bloques. De nuevo, no deberías necesitar esto más de dos veces.

Por lo tanto, generalmente tendríamos tres pasos:

[ Reshape ] ---> [ Permute axes ] ---> [ Reshape ] Create more axes Bring axes Merge axes into correct order

Método de seguimiento

La forma más segura de resolver, dada la entrada y la salida, es a través de lo que se podría llamar el método de seguimiento inverso, es decir, dividir los ejes de la entrada (al pasar de menor a mayor) y dividir los ejes de la salida ( cuando se pasa de nd más grande a nd más pequeño). La idea con la división es traer el número de dims del nd más pequeño igual que el nd más grande. Luego, estudie los pasos de la salida y compárelos con la entrada para obtener el orden de permuta requerido. Finalmente, podría ser necesaria una remodelación (forma predeterminada u orden C) al final, si la final es más pequeña nd menor, para fusionar los ejes.

Si tanto la entrada como la salida tienen el mismo número de dims, entonces tendríamos que dividir ambas, dividirlas en bloques y estudiar sus zancadas entre sí. En tales casos, deberíamos tener el parámetro de entrada adicional de tamaños de bloque, pero eso probablemente esté fuera de tema.

Ejemplo

Usemos este caso específico para demostrar cómo aplicar esas estrategias. Aquí, la entrada es 4D , mientras que la salida es 2D . Entonces, lo más probable es que no necesitemos remodelar para dividir. Por lo tanto, debemos comenzar con permutar ejes. Como la salida final no es 4D , sino 2D , necesitaríamos una reforma al final.

Ahora, la entrada aquí es:

In [270]: a Out[270]: array([[[[ 0, 0], [ 0, 0]], [[ 5, 10], [15, 20]]], [[[ 6, 12], [18, 24]], [[ 7, 14], [21, 28]]]])

El resultado esperado es:

In [271]: out Out[271]: array([[ 0, 5, 0, 10], [ 6, 7, 12, 14], [ 0, 15, 0, 20], [18, 21, 24, 28]])

Además, esta es una transformación de nd mayor a menor, por lo que el método de seguimiento hacia atrás implicaría, dividir la salida y estudiar sus strides y hacer coincidir los valores correspondientes en la entrada:

axis = 3 --- --> axis = 1 ------> axis=2| axis=0| [ 0, 5, 0, 10], | [ 6, 7, 12, 14], v | [ 0, 15, 0, 20], v [18, 21, 24, 28]])

Por lo tanto, el orden permutado necesario es (2,0,3,1) :

In [275]: a.transpose((2, 0, 3, 1)) Out[275]: array([[[[ 0, 5], [ 0, 10]], [[ 6, 7], [12, 14]]], [[[ 0, 15], [ 0, 20]], [[18, 21], [24, 28]]]])

Luego, simplemente cambie la forma a la forma esperada:

In [276]: a.transpose((2, 0, 3, 1)).reshape(4,4) Out[276]: array([[ 0, 5, 0, 10], [ 6, 7, 12, 14], [ 0, 15, 0, 20], [18, 21, 24, 28]])

Más ejemplos

Desenterré mi historia y encontré pocas Q&As basadas en transformaciones nd a nd . Estos podrían servir como otros casos de ejemplo, aunque con una explicación menor (en su mayoría). Como se mencionó anteriormente, a lo sumo dos reshapes y como máximo una swapaxes / transpose hicieron el trabajo en todas partes. Se enumeran a continuación:


Parece que estás buscando una transpose seguida de una reshape .

x.transpose((2, 0, 3, 1)).reshape(np.prod(x.shape[:2]), -1) array([[ 0, 5, 0, 10], [ 6, 7, 12, 14], [ 0, 15, 0, 20], [18, 21, 24, 28]])

Para ayudarlo a comprender por qué se necesita una transposición, analicemos su salida con forma incorrecta (obtenida por una sola llamada de reshape ) para comprender por qué es incorrecta.

Una versión simple en 2D reformada de este resultado (sin ninguna transposición) se ve así:

x.reshape(4, 4) array([[ 0, 0, 0, 0], [ 5, 10, 15, 20], [ 6, 12, 18, 24], [ 7, 14, 21, 28]])

Ahora considere esta salida con respecto a su salida esperada:

array([[ 0, 5, 0, 10], [ 6, 7, 12, 14], [ 0, 15, 0, 20], [18, 21, 24, 28]])

Notarás que tu resultado real se obtiene mediante un recorrido tipo Z de tu salida con forma incorrecta:

start | /| /| /| |/ | / |/ | / / / / / / | /| / | /| |/ |/ |/ | end

Esto implica que debe moverse sobre la matriz en diferentes pasos para obtener su resultado real . En conclusión, una simple remodelación no es suficiente. Debe transponer la matriz original, de tal manera que estos elementos tipo Z se hagan contiguos entre sí, de modo que una llamada de remodelación posterior le proporcione el resultado que necesita.

Para entender cómo transponer correctamente, debe rastrear los elementos a lo largo de la entrada y descubrir qué ejes necesita saltar para llegar a cada uno en la salida. La transposición sigue en consecuencia. La respuesta de Divakar explica muy bien esto.


La respuesta de Divarkar es excelente , aunque a veces es más fácil para mí simplemente verificar todos los casos posibles que se transpose y reshape cubierta.

Por ejemplo, el siguiente código

n, m = 4, 2 arr = np.arange(n*n*m*m).reshape(n,n,m,m) for permut in itertools.permutations(range(4)): arr2 = (arr.transpose(permut)).reshape(n*m, n*m) print(permut, arr2[0])

me da todo lo que uno puede obtener de una matriz de 4 dimensiones usando transpose + reshape . Como sé cómo debería verse la salida, solo elegiré la permutación que me mostró la respuesta correcta. Si no obtuve lo que quería, entonces la transpose + reshape no es lo suficientemente general como para cubrir mi caso y tengo que hacer algo más complicado.