Una cosa su cui non potrei mai pensare è come funziona Flatten quando fornito con una matrice come secondo argomento, e laiuto di Mathematica non è particolarmente valido su questo argomento.

Tratto da Flatten Mathematica documentazione:

Flatten[list, {{s11, s12, ...}, {s21, s22, ...}, ...}] 

Appiattisce list combinando tutti i livelli $ s_ {ij} $ per rendere ogni livello $ i $ nel risultato.

Qualcuno potrebbe spiegare cosa significa / fa effettivamente?

Risposta

Uno un modo conveniente per pensare a Flatten con il secondo argomento è che esegue qualcosa come Transpose per elenchi irregolari (irregolari). Ecco un semplice esempio:

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

Ciò che accade sono gli elementi che costituiscono Il livello 1 nellelenco originale sono ora componenti al livello 2 nel risultato e viceversa. Questo è esattamente ciò che fa Transpose, ma fatto per elenchi irregolari. Tieni presente, tuttavia, che alcune informazioni sulle posizioni vengono perse qui, quindi non possiamo invertire direttamente loperazione:

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

Per invertirla correttamente, avremmo “dovuto per fare qualcosa del genere:

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 esempio più interessante è quando abbiamo una nidificazione più profonda:

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

Anche qui, possiamo vedere che Flatten ha funzionato efficacemente come (generalizzato) Transpose, scambiando pezzi ai primi 2 livelli Sarà più difficile capire quanto segue:

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

Limmagine seguente illustra questa trasposizione generalizzata:

Illustrazione della trasposizione ciclica generalizzata

Possiamo farlo in due fasi consecutive:

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

Poiché la permutazione {3,1,2} può essere ottenuto come {1,3,2} seguito da {2,1,3}. Un altro modo per vedere come funziona è usa i numeri wh ich indicare la posizione nella struttura della 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}}} *) 

Da qui si può vedere che nella lista più esterna (primo livello), il terzo indice (corrispondente al terzo livello della lista originale) cresce, in ogni lista membri (secondo livello) il primo elemento cresce per elemento (corrispondente al primo livello della lista originale), e infine nelle liste più interne (terzo livello) cresce il secondo indice , corrispondente al secondo livello nellelenco originale. In genere, se lelemento k-esimo della lista passato come secondo elemento è {n}, aumentare lindice k-esimo nella struttura dellelenco risultante corrisponde ad aumentare lindice n-esimo nella struttura originale.

Infine, è possibile combinare diversi livelli per appiattire efficacemente i sottolivelli, in questo modo:

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

Commenti

  • Penso che ‘ sia più facile da capire se i numeri indicano la posizione originale nellelenco annidato. Ad esempio, nellesempio di permutazione a tre numeri, sarebbe Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}} e il risultato sarebbe {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}.
  • @celtschk Grazie, ma non sono convinto. Per me personalmente, è più facile tenere traccia di numeri visivamente distinti e vedere dove finiscono nella nuova struttura. Ma sentiti libero di aggiungere questo alla mia risposta, questo per me va benissimo.
  • Immagino che i nostri modi di intendere siano diversi da questo punto di vista. In realtà solo dopo aver riscritto a modo mio, ho capito completamente la permutazione ciclica (ma poi direttamente, senza la scomposizione in due fasi che hai fatto in seguito). Il punto è che in questo modo, puoi vedere immediatamente quale indice cambia se ti sposti lungo ogni elenco e puoi determinare da dove ha avuto origine il numero nella struttura dellelenco senza nemmeno guardare lelenco originale. In altre parole, rivela più direttamente (almeno per me) la struttura della trasformazione.
  • @celtschk Sì, ho capito il tuo ragionamento. Ma per me è stato più facile capire quali elementi hanno saltato in quali elenchi, piuttosto che guardare gli elementi ‘ indici. Forse ‘ sono solo io, ho sempre gravitato sulla geometria piuttosto che sullalgebra. Penso che entrambi i modi abbiano i loro meriti.
  • @LeonidShifrin Per 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}}, dici Quello che succede è che gli elementi che costituivano il livello 1 nellelenco originale sono ora costituenti livello 2 nel risultato. Non ‘ capisco bene, input e output hanno la stessa struttura di livello, gli elementi sono ancora allo stesso livello. Puoi spiegarlo in generale?

Risposta

Un secondo argomento dellelenco a Flatten serve due scopi. Innanzitutto, specifica lordine in cui gli indici verranno iterati durante la raccolta degli elementi. In secondo luogo, descrive lappiattimento dellelenco nel risultato finale. Esaminiamo ciascuna di queste funzionalità a turno.

Ordine di iterazione

Considera la seguente matrice:

$m = Array[Subscript[m, Row[{##}]]&, {4, 3, 2}]; $m // MatrixForm 

risultato matrice

Possiamo usare unespressione Table per creare una copia della matrice ripetendo tutti i suoi elementi:

$m === Table[$m[[i, j, k]], {i, 1, 4}, {j, 1, 3}, {k, 1, 2}] (* True *) 

Questa identità loperazione non è interessante, ma possiamo trasformare larray scambiando lordine delle variabili di iterazione. Ad esempio, possiamo scambiare i e j iteratori. Ciò equivale a scambiare gli indici di livello 1 e 2 e i loro elementi corrispondenti:

$r = Table[$m[[i, j, k]], {j, 1, 3}, {i, 1, 4}, {k, 1, 2}]; $r // MatrixForm 

matrice risultato

Se guardiamo attentamente, possiamo vedere che ogni elemento originale $m[[i, j, k]] corrisponderà allelemento risultante $r[[j, i, k]] – i primi due indici sono ape n “swapped”.

Flatten ci consente di esprimere unoperazione equivalente a questa Table espressione in modo più succinto:

$r === Flatten[$m, {{2}, {1}, {3}}] (* True *) 

Il secondo argomento dellespressione Flatten specifica esplicitamente lordine degli indici desiderato: gli indici 1, 2, 3 sono modificato per diventare indici 2, 1, 3. Nota come non abbiamo bisogno di specificare un intervallo per ogni dimensione dellarray: una notevole comodità notazionale.

Il seguente Flatten è unoperazione sullidentità poiché non specifica alcuna modifica allordine dellindice:

$m === Flatten[$m, {{1}, {2}, {3}}] (* True *) 

Mentre la seguente espressione riorganizza tutti e tre gli indici: 1, 2 , 3 -> 3, 2, 1

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

risultato matrice

Ancora , possiamo verificare che un elemento originale trovato nellindice [[i, j, k]] si troverà ora in [[k, j, i]] nel risultato.

Se qualche indice viene omesso da un Flatten, vengono trattati come se fossero stati specificati per ultimi e nel loro ordine naturale:

Flatten[$m, {{3}}] === Flatten[$m, {{3}, {1}, {2}}] (* True *) 

Questultimo esempio può essere abbreviato ulteriormente:

Flatten[$m, {3}] === Flatten[$m, {{3}}] (* True *) 

Un elenco di indice vuoto risulta nelloperazione di identità:

$m === Flatten[$m, {}] === Flatten[$m, {1}] === Flatten[$m, {{1}, {2}, {3}}] (* True *) 

Questo si occupa dellordine di iterazione e dello scambio di indici. Ora, esaminiamo …

Appiattimento elenco

Ci si potrebbe chiedere perché negli esempi precedenti abbiamo dovuto specificare ogni indice in una sottolista. Il motivo è che ogni sottolista nella specifica dellindice specifica quali indici devono essere appiattiti insieme nel risultato. Considera ancora la seguente operazione di identità:

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

risultato matrice

Cosa succede se uniamo i primi due indici nella stessa sottolista ?

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

risultato matrice

Possiamo vedere che il risultato originale era un Griglia di coppie 4 x 3, ma il secondo risultato è un semplice elenco di coppie. La struttura più profonda, le coppie, è rimasta intatta. I primi due livelli sono stati appiattiti in un unico livello. Le coppie nel terzo livello della sorgente matrice è rimasta non appiattita.

Potremmo combinare i secondi due indici invece:

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

risultato della matrice

Questo risultato ha lo stesso numero di righe della matrice originale, il che significa che il primo livello non è stato toccato. Ma ogni riga dei risultati ha un elenco semplice di sei elementi presi dalla corrispondente riga originale di tre coppie. Pertanto, i due livelli inferiori sono stati appiattiti.

Possiamo anche combinare tutti e tre gli indici per ottenere un risultato completamente appiattito:

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

risultato matrice

Questo può essere abbreviato:

Flatten[$m, {{1, 2, 3}}] === Flatten[$m, {1, 2, 3}] === Flatten[$m] (* True *) 

Flatten offre anche una notazione abbreviata quando non deve avvenire lo scambio di indice:

$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 *) 

“Ragged” Arrays

Tutti gli esempi finora hanno utilizzato matrici di varie dimensioni. Flatten offre una funzionalità molto potente che lo rende più di unabbreviazione per unespressione Table. Flatten gestirà con garbo il caso in cui le sottoliste a un dato livello abbiano lunghezze diverse. Gli elementi mancanti verranno ignorati silenziosamente. Ad esempio, un array triangolare può essere capovolto:

$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 *) 

…o capovolto e appiattito:

Flatten[$t, {{2, 1}}] (* {1,2,3,4,5,4,6,8,10,9,12,15,16,20,25} *) 

Commenti

  • Questa è una spiegazione fantastica e completa!
  • @ rm-rf Grazie. Immagino che se Flatten fosse generalizzato per accettare una funzione da applicare durante lappiattimento (contrazione) degli indici, sarebbe un ottimo inizio per ” algebra tensoriale in una lattina “.
  • A volte abbiamo bisogno di contrazioni interne. Ora so di poterlo fare utilizzando Flatten[$m, {{1}, {2, 3}}] invece di Map Flatten su un certo livello. Sarebbe bello se Flatten accettasse argomenti negativi per farlo. Quindi questo caso potrebbe essere scritto come Flatten[$m, -2].
  • Perché questa eccellente risposta ha ottenuto meno voti di Leonid ‘: (.
  • @Tangshutao Vedi la seconda FAQ sul mio profilo .

Risposta

Ho imparato molto dalle risposte di WReach “se Leonid” e “vorrei dare un piccolo contributo:

Sembra valga la pena sottolineando che lo scopo principale del secondo argomento a valore di elenco di Flatten è semplicemente quello di appiattire determinati livelli di elenchi (come WReach menziona nel suo List Flattening sezione). Lutilizzo di Flatten come Transpose irregolare sembra un lato -effetto di questo progetto primario, secondo me.

Ad esempio, ieri avevo bisogno di trasformare questo elenco

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

treeform1

in questo:

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

treeform2

Cioè, dovevo schiacciare insieme il 2 ° e il 3 ° livello di elenco.

Lho fatto con

list2 = Flatten[lists, {{1}, {2, 3}}]; 

Risposta

Questa è una vecchia domanda, ma spesso viene posta da un lotto di persone. Oggi, quando stavo cercando di spiegare come funziona, mi sono imbattuto in una spiegazione abbastanza chiara, quindi penso che condividerla qui sarebbe utile per un ulteriore pubblico.

Cosa significa indice?

Per prima cosa chiariamo cosa è index : In Mathematica ogni espressione è un albero, ad esempio, guardiamo in un elenco:

TreeForm@{{1,2},{3,4}} 

illus1

Come navighi in un albero?

Semplice! Parti dalla radice e ad ogni incrocio scegli da che parte andare, ad esempio qui se vuoi raggiungere 2, inizi con la scelta del il primo percorso, quindi scegli il secondo percorso. Scriviamolo come {1,2} che è solo lindice dellelemento 2 in questa espressione.

Come capire Flatten?

Qui prendi in considerazione una semplice domanda, se non ti fornisco unespressione completa, ma invece ti do tutti gli elementi e i loro indici, come costruisci lespressione originale? Ad esempio, qui ti do:

{<|"index" -> {1, 1}, "value" -> 1|>, <|"index" -> {1, 2}, "value" -> 2|>, <|"index" -> {2, 1}, "value" -> 3|>, <|"index" -> {2, 2}, "value" -> 4|>} 

e ti dico che tutte le teste sono List, quindi cosa “s lespressione originale?

Beh, sicuramente puoi ricostruire lespressione originale come {{1,2},{3,4}}, ma come? Probabilmente puoi elencare i seguenti passaggi:

  1. Prima esaminiamo il primo elemento di index, ordiniamo e raccogliamo in base a esso. Quindi sappiamo che il primo lelemento dellintera espressione deve contenere i primi due elementi nellelenco originale …
  2. Quindi continuiamo a guardare il secondo argomento, facciamo lo stesso …
  3. Infine otteniamo lelenco originale come {{1,2},{3,4}}.

Bene, è ragionevole! Quindi cosa succede se ti dico, no, dovresti prima ordinare e raccogliere in base al secondo elemento dellindice e poi raccogliere in base al primo elemento dellindice? Oppure dico che non li raccogliamo due volte, ordiniamo semplicemente in base a entrambi gli elementi ma diamo priorità più alta al primo argomento?

Bene, probabilmente otterresti rispettivamente i due seguenti elenchi, giusto?

  1. {{1,3},{2,4}}
  2. {1,2,3,4}

Bene, controlla da solo, Flatten[{{1,2},{3,4}},{{2},{1}}] e Flatten[{{1,2},{3,4}},{{1,2}}] fanno lo stesso!

Quindi, come intendi il secondo argomento di Flatten ?

  1. Ogni elemento dellelenco allinterno dellelenco principale, ad esempio {1,2}, significa che dovresti RACCOGLIE tutti gli elenchi in base a questi elementi nellindice, in altre parole questi livelli .
  2. Lordine allinterno di un elemento di elenco rappresenta il modo in cui SORT gli elementi raccolti allinterno di un elenco nel passaggio precedente . ad esempio, {2,1} significa che la posizione al secondo livello ha una priorità maggiore rispetto alla posizione al primo livello.

Esempi

Ora facciamo un po di pratica per acquisire familiarità con le regole precedenti.

1. Transpose

Lobiettivo di Transpose su una semplice matrice m * n serve a rendere $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Ma possiamo considerarlo in un altro modo, originariamente ordiniamo il elemento in base al loro indice i, quindi ordinarli in base al loro indice j, ora tutto ciò che dobbiamo fare è cambiare per ordinarli per j index prima, poi i successivo! Quindi il codice diventa:

Flatten[mat,{{2},{1}}] 

Semplice, vero?

2. Tradizionale Flatten

Lobiettivo dellappiattimento tradizionale su una semplice matrice m * n è crea un array 1D invece di una matrice 2D, ad esempio: Flatten[{{1,2},{3,4}}] restituisce {1,2,3,4}. Ciò significa che questa volta non “t raccogliamo elementi, ma solo ordina , prima in base al primo indice e poi al secondo:

Flatten[mat,{{1,2}}] 

3. ArrayFlatten

Parliamo di un caso molto semplice di ArrayFlatten, qui abbiamo un elenco 4D:

{{{{1,2},{5,6}},{{3,4},{7,8}}},{{{9,10},{13,14}},{{11,12},{15,16}}}} 

quindi come possiamo fare una tale conversione per renderlo un elenco 2D?

$ \ left (\ begin {array} {cc} \ left (\ begin {array} {cc} 1 & 2 \\ 5 & 6 \\ \ end {array} \ right) & \ sinistra (\ 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 {array} \ right) \\ \ end {array} \ 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) $

Bene, anche questo è semplice, abbiamo bisogno del gruppo con lindice originale di primo e terzo livello prima, e dovremmo dare al primo indice una priorità più alta in ordinamento. Lo stesso vale per il secondo e il quarto livello:

Flatten[mat,{{1,3},{2,4}}] 

4. “Ridimensiona” unimmagine

Ora abbiamo unimmagine, ad esempio:

img=Image@RandomReal[1,{10,10}] 

Ma è decisamente troppo piccola per noi visualizzazione, quindi vogliamo ingrandirlo estendendo ogni pixel a un enorme pixel di dimensioni 10 * 10.

Per prima cosa proveremo:

ConstantArray[ImageData@img,{10,10}] 

Ma restituisce una matrice 4D con dimensioni {10,10,10,10}. Quindi dovremmo Flatten. Questa volta vogliamo che il terzo argomento abbia invece priorità più alta del primo, quindi una regolazione minore potrebbe funzionare:

Image@Flatten[ConstantArray[ImageData@img,{10,10}],{{3,1},{4,2}}] 

Un confronto:

illus2

Spero che questo possa aiutare!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *