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:

Ilustración de transposición cíclica 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

  • Creo que ‘ es más fácil de entender si los números indican la posición original en la lista anidada. Por ejemplo, en el ejemplo de permutación de tres números, sería 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}}}.
  • @celtschk Gracias, pero no estoy convencido. Para mí personalmente, es más fácil rastrear números visualmente distintos y ver dónde terminan en la nueva estructura. Pero siéntase libre de agregar esto a mi respuesta, esto está perfectamente bien para mí.
  • Creo que nuestras formas de entender son diferentes en ese sentido. En realidad, solo después de reescribir a mi manera, entendí completamente la permutación cíclica (pero luego directamente, sin la descomposición de dos pasos que hiciste después). El punto es que de esta manera, puede ver inmediatamente qué índice cambia si se mueve a lo largo de cada lista, y puede determinar dónde se originó el número en la estructura de la lista sin siquiera mirar la lista original. En otras palabras, revela más directamente (al menos para mí) la estructura de la transformación.
  • @celtschk Sí, entendí tu razonamiento. Pero para mí, fue más fácil entender qué elementos saltaron qué listas, en lugar de mirar los índices de elementos ‘. Puede ser ‘ solo yo, siempre me incliné por la geometría en lugar del álgebra. Creo que ambas formas tienen sus méritos.
  • @LeonidShifrin Para 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?

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 

resultado de la matriz

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 

resultado de matriz

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 

resultado de matriz

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 

resultado de la matriz

¿Qué sucede si combinamos los dos primeros índices en la misma sublista? ?

Flatten[$m, {{1, 2}, {3}}] // MatrixForm 

resultado de la matriz

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 

resultado de matriz

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}}] 

resultado de la matriz

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 si Flatten aceptara argumentos negativos para hacer eso. Entonces, este caso podría escribirse como Flatten[$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... *) }; 

treeform1

en este:

list2 = { {{1, 0}, {1, 1}, {2, 0}, {2, 4}, {3, 0}}, {{1, 2}, {1, 3}, {2, Sqrt[2]}, {3, 4}} (*, more lists... *) } 

treeform2

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}} 

illus1

¿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:

  1. 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 …
  2. Luego continuamos mirando el segundo argumento, hacemos lo mismo …
  3. 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. {{1,3},{2,4}}
  2. {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? ?

  1. 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 .
  2. 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:

illus2

¡Espero que esto pueda ayudar!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *