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:
-
Permuta los ejes (con
numpy.transpose
onumpy.moveaxis
onumpy.rollaxis
si el pedido de permute necesario es enrollado onumpy.swapaxes
si solo se necesitan intercambiar dos ejes) y -
Reformar
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:
- Python Reforma la matriz 3d en 2d
- remodelar una matriz usando python / numpy
- Fusionar bloques de matriz no superpuestos
- Conversión de una matriz Numpy 3D a una matriz 2D
- cómo remodelar un vector de longitud N a una matriz 3x (N / 3) en numpy usando remodelar
- Construir imagen a partir de la lista 4D
- Remodelación / combinación de varias submatrices en una matriz en un espacio multidimensional
- Entrelaza varias matrices 2D pequeñas en una más grande
- ¿Cómo recuperar cada sección en 3X3?
- Reformar la matriz 3D Numpy a una matriz 2D
- Iterar en submatrices a través de una matriz más grande
- Reorganizando una matriz numpy 2D en 3D
- Numpy cambia de forma de (3, 512, 660, 4) a (3,2048,660,1)
- Numpy: rotar submatriz m de M
- Dividir una matriz numpy 3D en bloques 3D
- Conversión de matriz 3D a matrices 2D en cascada
- Reorganizar la matriz numpy
- Numpy: remodelar la matriz a lo largo de un eje especificado
- Cómo construir una matriz 2d a partir de matrices 2d
- ¿Cómo formar una matriz a partir de submatrices?
- Python: remodelar series de imágenes en 3D a series de píxeles
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.