Una cosa que nunca pude entender es cómo funciona Flatten
cuando provisto de una matriz como segundo argumento, y la ayuda de Mathematica no es particularmente buena en este caso.
Tomado de Flatten
documentación de Mathematica :
Flatten[list, {{s11, s12, ...}, {s21, s22, ...}, ...}]
Aplana
list
combinando todos los niveles $ s_ {ij} $ para hacer que cada nivel sea $ i $ en el resultado.
¿Podría alguien explicar lo que esto realmente significa / hace?
Respuesta
Uno Una forma conveniente de pensar en Flatten
con el segundo argumento es que realiza algo como Transpose
para listas irregulares. Aquí hay una simple ejemplo:
In[63]:= Flatten[{{1,2,3},{4,5},{6,7},{8,9,10}},{{2},{1}}] Out[63]= {{1,4,6,8},{2,5,7,9},{3,10}}
Lo que sucede es que los elementos que constituyen El nivel uted 1
de la lista original ahora son componentes del nivel 2
en el resultado, y viceversa. Esto es exactamente lo que hace Transpose
, pero para listas irregulares. Sin embargo, tenga en cuenta que aquí se pierde parte de la información sobre las posiciones, por lo que no podemos invertir directamente la operación:
In[65]:= Flatten[{{1,4,6,8},{2,5,7,9},{3,10}},{{2},{1}}] Out[65]= {{1,2,3},{4,5,10},{6,7},{8,9}}
Para invertirla correctamente, «tendríamos para hacer algo como esto:
In[67]:= Flatten/@Flatten[{{1,4,6,8},{2,5,7,9},{3,{},{},10}},{{2},{1}}] Out[67]= {{1,2,3},{4,5},{6,7},{8,9,10}}
Un ejemplo más interesante es cuando tenemos un anidamiento más profundo:
In[68]:= Flatten[{{{1,2,3},{4,5}},{{6,7},{8,9,10}}},{{2},{1},{3}}] Out[68]= {{{1,2,3},{6,7}},{{4,5},{8,9,10}}}
Aquí nuevamente, podemos ver que Flatten
funcionó efectivamente como (generalizado) Transpose
, intercambiando piezas en los primeros 2 niveles . Lo siguiente será más difícil de entender:
In[69]:= Flatten[{{{1, 2, 3}, {4, 5}}, {{6, 7}, {8, 9, 10}}}, {{3}, {1}, {2}}] Out[69]= {{{1, 4}, {6, 8}}, {{2, 5}, {7, 9}}, {{3}, {10}}}
La siguiente imagen ilustra esta transposición generalizada:
Podemos hacerlo en dos pasos consecutivos:
In[72]:= step1 = Flatten[{{{1,2,3},{4,5}},{{6,7},{8,9,10}}},{{1},{3},{2}}] Out[72]= {{{1,4},{2,5},{3}},{{6,8},{7,9},{10}}} In[73]:= step2 = Flatten[step1,{{2},{1},{3}}] Out[73]= {{{1,4},{6,8}},{{2,5},{7,9}},{{3},{10}}}
Desde la permutación {3,1,2}
se puede obtener como {1,3,2}
seguido de {2,1,3}
. Otra forma de ver cómo funciona es usar números wh ich indican la posición en la estructura de la lista:
Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {221, 222, 223}}}, {{3}, {1}, {2}}] (* ==> {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}} *)
De aquí, se puede ver que en la lista más externa (primer nivel), el tercer índice (correspondiente al tercer nivel de la lista original) crece, en cada lista de miembros (segundo nivel) el primer elemento crece por elemento (correspondiente al primer nivel de la lista original), y finalmente en las listas más internas (tercer nivel), el segundo índice crece , correspondiente al segundo nivel de la lista original. Generalmente, si el k-ésimo elemento de la lista que se pasa como segundo elemento es {n}
, aumentar el k-ésimo índice en la estructura de lista resultante corresponde a aumentar el n-ésimo índice en el estructura original.
Finalmente, uno puede combinar varios niveles para aplanar efectivamente los subniveles, así:
In[74]:= Flatten[{{{1,2,3},{4,5}},{{6,7},{8,9,10}}},{{2},{1,3}}] Out[74]= {{1,2,3,6,7},{4,5,8,9,10}}
Comentarios
Respuesta
Un segundo argumento de lista para Flatten
sirve dos propósitos. Primero, especifica el orden en el que se iterarán los índices al recopilar elementos. En segundo lugar, describe el aplanamiento de la lista en el resultado final. Analicemos cada una de estas funciones.
Orden de iteración
Considere la siguiente matriz:
$m = Array[Subscript[m, Row[{##}]]&, {4, 3, 2}]; $m // MatrixForm
Podemos usar una Table
expresión para crear una copia de la matriz iterando sobre todos sus elementos:
$m === Table[$m[[i, j, k]], {i, 1, 4}, {j, 1, 3}, {k, 1, 2}] (* True *)
Esta identidad La operación no es interesante, pero podemos transformar la matriz cambiando el orden de las variables de iteración. Por ejemplo, podemos intercambiar i
y j
iteradores. Esto equivale a intercambiar los índices de nivel 1 y nivel 2 y sus elementos correspondientes:
$r = Table[$m[[i, j, k]], {j, 1, 3}, {i, 1, 4}, {k, 1, 2}]; $r // MatrixForm
Si miramos con atención, podemos ver que cada elemento original $m[[i, j, k]]
se corresponderá con el elemento resultante $r[[j, i, k]]
– los dos primeros índices tienen abeja n «swapped».
Flatten
nos permite expresar una operación equivalente a esta Table
expresión más sucintamente:
$r === Flatten[$m, {{2}, {1}, {3}}] (* True *)
El segundo argumento de la expresión Flatten
especifica explícitamente el orden de índice deseado: los índices 1, 2, 3 son alterado para convertirse en índices 2, 1, 3. Tenga en cuenta que no necesitamos especificar un rango para cada dimensión de la matriz, lo que es una conveniencia de notación significativa.
El siguiente Flatten
es una operación de identidad ya que no especifica ningún cambio en el orden del índice:
$m === Flatten[$m, {{1}, {2}, {3}}] (* True *)
Considerando que la siguiente expresión reordena los tres índices: 1, 2 , 3 -> 3, 2, 1
Flatten[$m, {{3}, {2}, {1}}] // MatrixForm
De nuevo , podemos verificar que un elemento original encontrado en el índice [[i, j, k]]
ahora se encontrará en [[k, j, i]]
en el resultado.
Si se omiten índices de un Flatten
expresión, se tratan como si se hubieran especificado en último lugar y en su orden natural:
Flatten[$m, {{3}}] === Flatten[$m, {{3}, {1}, {2}}] (* True *)
Este último ejemplo puede abreviarse aún más:
Flatten[$m, {3}] === Flatten[$m, {{3}}] (* True *)
Una lista de índice vacía da como resultado la operación de identidad:
$m === Flatten[$m, {}] === Flatten[$m, {1}] === Flatten[$m, {{1}, {2}, {3}}] (* True *)
Eso se encarga del orden de iteración y el intercambio de índices. Ahora, echemos un vistazo a …
Aplanamiento de listas
Uno podría preguntarse por qué tuvimos que especificar cada índice en una sublista en los ejemplos anteriores. La razón es que cada sublista en la especificación del índice especifica qué índices se aplanarán juntos en el resultado. Considere nuevamente la siguiente operación de identidad:
Flatten[$m, {{1}, {2}, {3}}] // MatrixForm
¿Qué sucede si combinamos los dos primeros índices en la misma sublista? ?
Flatten[$m, {{1, 2}, {3}}] // MatrixForm
Podemos ver que el resultado original era un Cuadrícula de pares de 4 x 3, pero el segundo resultado es una lista simple de pares. La estructura más profunda, los pares, se dejaron intactos. Los dos primeros niveles se han aplanado en un solo nivel. Los pares en el tercer nivel de la fuente la matriz no se aplanó.
Podríamos combinar los dos segundos índices en su lugar:
Flatten[$m, {{1}, {2, 3}}] // MatrixForm
aña Este resultado tiene el mismo número de filas que la matriz original, lo que significa que el primer nivel se dejó intacto. Pero cada fila de resultados tiene una lista plana de seis elementos tomados de la fila original correspondiente de tres pares. Por lo tanto, los dos niveles inferiores se han aplanado.
También podemos combinar los tres índices para obtener un resultado completamente plano:
Flatten[$m, {{1, 2, 3}}]
Esto se puede abreviar:
Flatten[$m, {{1, 2, 3}}] === Flatten[$m, {1, 2, 3}] === Flatten[$m] (* True *)
Flatten
también ofrece una notación abreviada cuando no se va a realizar ningún intercambio de índice:
$n = Array[n[##]&, {2, 2, 2, 2, 2}]; Flatten[$n, {{1}, {2}, {3}, {4}, {5}}] === Flatten[$n, 0] (* True *) Flatten[$n, {{1, 2}, {3}, {4}, {5}}] === Flatten[$n, 1] (* True *) Flatten[$n, {{1, 2, 3}, {4}, {5}}] === Flatten[$n, 2] (* True *) Flatten[$n, {{1, 2, 3, 4}, {5}}] === Flatten[$n, 3] (* True *)
Matrices «irregulares»
Hasta ahora, todos los ejemplos han utilizado matrices de varias dimensiones. Flatten
ofrece una característica muy poderosa que la convierte en algo más que una abreviatura de una expresión Table
. Flatten
manejará con elegancia el caso en el que las sublistas en cualquier nivel dado tengan diferentes longitudes. Los elementos faltantes se ignorarán en silencio. Por ejemplo, una matriz triangular se puede voltear:
$t = Array[# Range[#]&, {5}]; $t // TableForm (* 1 2 4 3 6 9 4 8 12 16 5 10 15 20 25 *) Flatten[$t, {{2}, {1}}] // TableForm (* 1 2 3 4 5 4 6 8 10 9 12 15 16 20 25 *)
aña …o volteado y aplanado:
Flatten[$t, {{2, 1}}] (* {1,2,3,4,5,4,6,8,10,9,12,15,16,20,25} *)
Comentarios
- ¡Esta es una explicación fantástica y completa!
- @ rm-rf Gracias. Me imagino que si
Flatten
se generalizara para aceptar una función para aplicar al aplanar (contraer) índices, sería un excelente comienzo para » álgebra tensorial en una lata «. - A veces necesitamos hacer contracciones internas. Ahora sé que puedo hacerlo usando
Flatten[$m, {{1}, {2, 3}}]
en lugar de Map Flatten en algún nivel. Sería bueno siFlatten
aceptara argumentos negativos para hacer eso. Entonces, este caso podría escribirse comoFlatten[$m, -2]
. - Por qué esta excelente respuesta obtuvo menos votos que Leonid ‘ s: (.
- @Tangshutao Ver la segunda pregunta frecuente en mi perfil .
Respuesta
Aprendí mucho de las respuestas de WReach y Leonid y me gustaría hacer una pequeña contribución:
Parece que vale la pena enfatizando que la intención principal del segundo argumento con valores de lista de Flatten
es simplemente aplanar ciertos niveles de listas (como WReach menciona en su Aplanar lista sección). Usar Flatten
como un Transpose
irregular parece un lado -efecto de este diseño primario, en mi opinión.
Por ejemplo, ayer necesitaba transformar esta lista
lists = { {{{1, 0}, {1, 1}}, {{2, 0}, {2, 4}}, {{3, 0}}}, {{{1, 2}, {1, 3}}, {{2, Sqrt[2]}}, {{3, 4}}} (*, more lists... *) };
en este:
list2 = { {{1, 0}, {1, 1}, {2, 0}, {2, 4}, {3, 0}}, {{1, 2}, {1, 3}, {2, Sqrt[2]}, {3, 4}} (*, more lists... *) }
Es decir, necesitaba aplastar los niveles de lista 2 y 3 juntos.
Lo hice con
list2 = Flatten[lists, {{1}, {2, 3}}];
Respuesta
Esta es una pregunta antigua, pero un lot de personas. Hoy, cuando estaba tratando de explicar cómo funciona esto, encontré una explicación bastante clara, así que creo que compartirla aquí sería útil para más público.
¿Qué significa índice?
Primero dejemos en claro lo que es index : En Mathematica cada expresión es un árbol, por ejemplo, veamos en una lista:
TreeForm@{{1,2},{3,4}}
¿Cómo se navega en un árbol?
¡Simple! Empieza desde la raíz y en cada cruce elige qué camino tomar, por ejemplo, aquí si desea llegar a 2
, comience eligiendo primera ruta, luego elija la segunda ruta. Vamos a «escribirlo como {1,2}
que es solo el índice del elemento 2
en esta expresión.
¿Cómo entender Flatten
?
Aquí considere una pregunta simple, si no le proporciono una expresión completa, sino que le doy todos los elementos y sus índices, ¿cómo construyes la expresión original? Por ejemplo, aquí les doy:
{<|"index" -> {1, 1}, "value" -> 1|>, <|"index" -> {1, 2}, "value" -> 2|>, <|"index" -> {2, 1}, "value" -> 3|>, <|"index" -> {2, 2}, "value" -> 4|>}
y les digo que todas las cabezas son List
, entonces, ¿qué «s ¿la expresión original?
Bueno, seguramente puedes reconstruir la expresión original como {{1,2},{3,4}}
, pero ¿cómo? Probablemente puedas enumerar los siguientes pasos:
- Primero miramos el primer elemento del índice y ordenamos y reunimos por él. Luego sabemos que el primero el elemento de la expresión completa debe contener los primeros dos elementos de la lista original …
- Luego continuamos mirando el segundo argumento, hacemos lo mismo …
- Finalmente obtenemos la lista original como
{{1,2},{3,4}}
.
¡Bueno, eso es razonable! Entonces, ¿qué pasa si le digo que no, primero debe ordenar y recopilar por el segundo elemento del índice y luego reunir por el primer elemento del índice? ¿O digo que no los recopilamos dos veces, solo los ordenamos por ambos elementos pero le damos mayor prioridad al primer argumento?
Bueno, probablemente obtendría la siguiente lista de dos respectivamente, ¿verdad?
-
{{1,3},{2,4}}
-
{1,2,3,4}
Bueno, compruébalo tú mismo, Flatten[{{1,2},{3,4}},{{2},{1}}]
y Flatten[{{1,2},{3,4}},{{1,2}}]
hacen lo mismo.
Entonces, ¿cómo entiendes el segundo argumento de Flatten? ?
- Cada elemento de la lista dentro de la lista principal, por ejemplo,
{1,2}
, significa que debe RECOGE todas las listas según estos elementos en el índice, es decir, estos niveles . - El orden dentro de un elemento de lista representa cómo CLASIFICAR los elementos reunidos dentro de una lista en el paso anterior . por ejemplo,
{2,1}
significa que la posición en el segundo nivel tiene mayor prioridad que la posición en el primer nivel.
Ejemplos
Ahora tengamos algo de práctica para familiarizarnos con las reglas anteriores.
1. Transpose
El objetivo de Transpose
en una matriz m * n simple es hacer $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Pero podemos considerarlo de otra manera, originalmente ordenamos elemento por su índice i
primero, luego ordénelos por su índice j
, ahora todo lo que tenemos que hacer es cambiar para ordenarlos por j
indexa primero y luego i
después. Así que el código se convierte en:
Flatten[mat,{{2},{1}}]
Simple, ¿verdad?
2. Tradicional Flatten
El objetivo del aplanado tradicional en una matriz simple m * n es cree una matriz 1D en lugar de una matriz 2D, por ejemplo: Flatten[{{1,2},{3,4}}]
devuelve {1,2,3,4}
. Esto significa que esta vez no « recopilamos elementos, solo ordenarlos , primero por su primer índice y luego por el segundo:
Flatten[mat,{{1,2}}]
3. ArrayFlatten
Discutamos el caso más simple de ArrayFlatten, aquí tenemos una lista 4D:
{{{{1,2},{5,6}},{{3,4},{7,8}}},{{{9,10},{13,14}},{{11,12},{15,16}}}}
entonces, ¿cómo podemos hacer tal conversión para convertirla en una lista 2D?
$ \ left (\ begin {array} {cc} \ left (\ begin {array} {cc} 1 & 2 \\ 5 & 6 \\ \ end {array} \ right) & \ izquierda (\ begin {array} {cc} 3 & 4 \\ 7 & 8 \\ \ end {array} \ right) \\ \ left (\ begin {array} {cc} 9 & 10 \\ 13 & 14 \\ \ end {array} \ right) & \ left (\ begin {array} {cc} 11 & 12 \\ 15 & 16 \\ \ end {matriz} \ right) \\ \ end {matriz} \ right) \ rightarrow \ left (\ begin {array} {cccc} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \\ 13 & 14 & 15 & 16 \\ \ end {array} \ right) $
Bueno, esto también es simple, primero necesitamos el grupo por el primer índice original y el tercer nivel, y debemos darle al primer índice mayor prioridad en clasificación. Lo mismo ocurre con el segundo y cuarto nivel:
Flatten[mat,{{1,3},{2,4}}]
4. «Cambiar el tamaño» de una imagen
Ahora tenemos una imagen, por ejemplo:
img=Image@RandomReal[1,{10,10}]
Pero definitivamente es demasiado pequeña para nosotros vista, por lo que queremos agrandarlo extendiendo cada píxel a un píxel enorme de tamaño 10 * 10.
Primero intentaremos:
ConstantArray[ImageData@img,{10,10}]
Pero devuelve una matriz 4D con dimensiones {10,10,10,10}. Así que deberíamos Flatten
. Esta vez queremos que el tercer argumento tenga mayor prioridad. del primero, por lo que un ajuste menor funcionaría:
Image@Flatten[ConstantArray[ImageData@img,{10,10}],{{3,1},{4,2}}]
Una comparación:
¡Espero que esto pueda ayudar!
Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}}
y el resultado sería{{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}
.In[63]:= Flatten[{{1,2,3},{4,5},{6,7},{8,9,10}},{{2},{1}}] Out[63]= {{1,4,6,8},{2,5,7,9},{3,10}}
, dice Lo que sucede es que los elementos que constituían el nivel 1 en la lista original ahora son constituyentes nivel 2 en el resultado. No ‘ entiendo muy bien, la entrada y la salida tienen la misma estructura de nivel, los elementos todavía están en el mismo nivel. ¿Podría explicar esto en general?