En ting, jeg aldrig kunne pakke mit hoved rundt, er, hvordan Flatten fungerer, når forsynet med en matrix som det andet argument, og Mathematica hjælp er ikke særlig god på denne.

Hentet fra Flatten Mathematica dokumentation:

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

Flader list ved at kombinere alle niveauer $ s_ {ij} $ for at gøre hvert niveau $ i $ i resultatet.

Kunne nogen uddybe, hvad dette egentlig betyder / gør?

Svar

En praktisk måde at tænke på Flatten med det andet argument er, at det udfører noget i retning af Transpose for skrøbelige (uregelmæssige) lister. Her er en enkel eksempel:

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

Hvad der sker er de elementer, der udgør uded niveau 1 i den oprindelige liste er nu bestanddele på niveau 2 i resultatet og omvendt. Dette er præcis, hvad Transpose gør, men gøres for uregelmæssige lister. Bemærk dog, at nogle oplysninger om positioner går tabt her, så vi kan ikke direkte vende operationen:

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

For at få det vendt korrekt, ville vi have at gøre noget som dette:

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

Et mere interessant eksempel er når vi har dybere indlejring:

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

Også her kan vi se, at Flatten effektivt fungerede som (generaliseret) Transpose og udvekslede stykker på de første 2 niveauer Følgende vil være sværere at forstå:

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

Følgende billede illustrerer denne generaliserede transponering:

Illustration af cyklisk generaliseret transponering

Vi kan gøre det i to på hinanden følgende trin:

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

Siden permutationen {3,1,2} kan fås som {1,3,2} efterfulgt af {2,1,3}. En anden måde at se, hvordan det fungerer, er at brug tal wh der angiver placeringen i listestrukturen:

Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {221, 222, 223}}}, {{3}, {1}, {2}}] (* ==> {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}} *) 

Herfra kan man se, at i den yderste liste (første niveau) er det tredje indeks (svarende til tredje niveau af den oprindelige liste) vokser, i hver medlemsliste (andet niveau) vokser det første element pr. element (svarende til det første niveau på den oprindelige liste), og til sidst i det inderste (tredje niveau) lister vokser det andet indeks svarende til det andet niveau i den oprindelige liste. Generelt, hvis det k-th element af listen, der er sendt som andet element, er {n}, svarer vækst af k-th indekset i den resulterende listestruktur til at øge det n-indeks i oprindelig struktur.

Endelig kan man kombinere flere niveauer for effektivt at flade underniveauerne ud, som sådan:

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

Kommentarer

  • Jeg synes, det ‘ er lettere at forstå, hvis tallene angiver den oprindelige position i den indlejrede liste. F.eks. I permutationseksemplet med tre tal ville det være Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}} og resultatet ville læse {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}.
  • @celtschk Tak, men jeg er ikke overbevist. For mig personligt er det lettere at spore visuelt forskellige tal og se, hvor de ender i den nye struktur. Men du er velkommen til at tilføje dette til mit svar, det er helt fint for mig.
  • Jeg gætter på, at vores måder at forstå er bare forskellige i den henseende. Faktisk først efter omskrivning på min måde forstod jeg fuldstændigt den cykliske permutation (men så direkte uden den to-trins nedbrydning, du gjorde bagefter). Pointen er, at på denne måde kan du med det samme se, hvilket indeks der ændres, hvis du bevæger dig langs hver liste, og du kan bestemme, hvor nummeret stammer fra listestrukturen, uden selv at se på den oprindelige liste. Med andre ord afslører det mere direkte (i det mindste for mig) transformationen struktur .
  • @celtschk Ja, jeg forstod din ræsonnement. Men for mig var det lettere at forstå, hvilke elementer der sprang, hvilke lister, snarere end at se på elementer ‘ indekser. Måske er det ‘ bare mig, jeg graviterede altid til geometri snarere end algebra. Jeg tror begge veje har deres fortjeneste.
  • @ LeonidShifrin For 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}} siger du Hvad der sker er, at elementer, der udgjorde niveau 1 i den oprindelige liste, nu er bestanddele på niveau 2 i resultatet. Jeg forstår ikke ‘, input og output har den samme niveaustruktur, elementerne er stadig på det samme niveau. Kan du forklare det i det store og hele?

Svar

Et andet listeargument til Flatten tjener to formål. For det første specificerer den rækkefølgen, i hvilken indekser skal gentages, når elementer samles. For det andet beskriver det listeudfladning i det endelige resultat. Lad os se på hver af disse muligheder efter tur.

Iterationsrækkefølge

Overvej følgende matrix:

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

matrixresultat

Vi kan bruge et Table udtryk for at oprette en kopi af matricen ved at gentage det over alle dets elementer:

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

Denne identitet operation er uinteressant, men vi kan transformere arrayet ved at bytte rækkefølgen på iterationsvariablerne. For eksempel kan vi bytte i og j iteratorer. Dette svarer til at bytte indekserne niveau 1 og niveau 2 og deres tilsvarende elementer:

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

matrixresultat

Hvis vi kigger nøje, kan vi se, at hvert originale element $m[[i, j, k]] viser sig at svare til det resulterende element $r[[j, i, k]] – de to første indekser har bi n “swapped”.

Flatten giver os mulighed for mere eksplicit at udtrykke en ækvivalent operation til dette Table -udtryk:

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

Det andet argument i Flatten udtrykket angiver eksplicit den ønskede indeksrækkefølge: indeks 1, 2, 3 er ændret til at blive indeks 2, 1, 3. Bemærk, hvordan vi ikke behøvede at specificere et område for hver dimension af arrayet – en betydelig notational bekvemmelighed.

Følgende Flatten er en identitetshandling, da den ikke angiver nogen ændring i indeksrækkefølgen:

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

Mens følgende udtryk omarrangerer alle tre indekser: 1, 2 , 3 -> 3, 2, 1

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

matrixresultat

Igen , kan vi kontrollere, at et originalt element, der findes i indekset [[i, j, k]], nu findes på [[k, j, i]] i resultatet.

Hvis nogen indekser er udeladt fra et Flatten udtryk, de behandles som om de var angivet sidst og i deres naturlige rækkefølge:

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

Dette sidste eksempel kan forkortes yderligere:

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

En tom indeksliste resulterer i identitetshandling:

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

Det tager sig af iterationsrækkefølge og indeksbytte. Lad os nu se på …

Listeudfladning

Man kan undre sig over, hvorfor vi skulle specificere hvert indeks i en underliste i de foregående eksempler. Årsagen er, at hver underliste i indeksspecifikationen specificerer, hvilke indekser der skal flades sammen i resultatet. Overvej igen følgende identitetshandling:

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

matrixresultat

Hvad sker der, hvis vi kombinerer de to første indeks i den samme underliste ?

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

matrixresultat

Vi kan se, at det oprindelige resultat var et 4 x 3 gitter med par, men det andet resultat er en simpel liste over par. Den dybeste struktur, parene, blev efterladt uberørt. De to første niveauer er blevet fladt ud til et enkelt niveau. Parene i det tredje niveau af kilden matrix forblev ikke flad.

Vi kunne kombinere de to anden indeks i stedet:

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

matrixresultat

Dette resultat har samme antal rækker som den oprindelige matrix, hvilket betyder at det første niveau blev efterladt uberørt. Men hver resultatrække har en flad liste med seks elementer taget fra den tilsvarende originale række med tre par. Således er de to nederste niveauer blevet fladt.

Vi kan også kombinere alle tre indekser for at få et fuldstændigt fladt resultat:

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

matrixresultat

Dette kan forkortes:

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

Flatten tilbyder også en stenografisk notation, når ingen indeksbytning skal finde sted:

$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

Alle eksemplerne hidtil har brugt matricer i forskellige dimensioner. Flatten tilbyder en meget kraftig funktion, der gør det mere end bare en forkortelse for et Table -udtryk. Flatten håndterer yndefuldt sagen, hvor underlister på et givet niveau har forskellige længder. Manglende elementer ignoreres stille. For eksempel kan et trekantet array vendes:

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

…eller vendt og fladt:

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

Kommentarer

  • Dette er en fantastisk og grundig forklaring!
  • @ rm-rf Tak. Jeg regner med, at hvis Flatten blev generaliseret til at acceptere en funktion, der skulle anvendes, når fladning (kontraherende) indekser, ville det være en glimrende start på ” tensoralgebra i en dåse “.
  • Nogle gange er vi nødt til at lave interne sammentrækninger. Nu ved jeg, at jeg kan gøre det ved hjælp af Flatten[$m, {{1}, {2, 3}}] i stedet for Map Flatten over et eller andet niveau. Det ville være rart, hvis Flatten accepterede negative argumenter for at gøre det. Så denne sag kunne være at skrive som Flatten[$m, -2].
  • Hvorfor dette fremragende svar fik færre stemmer end Leonid ‘ s: (.
  • @Tangshutao Se den anden FAQ om min profil .

Svar

Jeg lærte meget af WReachs og Leonids svar, og jeg vil gerne give et lille bidrag:

Det virker værd understrege, at den primære hensigt med det listeværdige andet argument for Flatten kun er at flade visse niveauer af lister (som WReach nævner i sin Listeudfladning . Brug af Flatten som en klodset Transpose virker som en side -effekt af dette primære design, efter min mening.

For eksempel var jeg i går nødt til at omdanne denne liste

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

treform1

i denne:

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

treform2

Det vil sige, jeg havde brug for at knuse det 2. og 3. liste-niveau sammen.

Jeg gjorde det med

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

Svar

Dette er et gammelt spørgsmål, men ofte stillet af et parti af mennesker. I dag, da jeg prøvede at forklare, hvordan dette fungerer, stødte jeg på en ganske klar forklaring, så jeg tror, at deling af det her ville være nyttigt for et yderligere publikum.

Hvad betyder indeks?

Lad os først gøre klart, hvad indeks : I Mathematica er hvert udtryk et træ, lad os f.eks. Se på en liste:

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

illus1

Hvordan navigerer du i et træ?

Enkelt! Du starter fra roden og vælger ved hver krydsning hvilken vej du vil gå, for eksempel her, hvis du vil nå 2, begynder du med at vælge først sti, vælg derefter anden sti. Lad os skrive det ud som {1,2} hvilket er det eneste indeks for elementet 2 i dette udtryk.

Hvordan forstå Flatten?

Overvej her et simpelt spørgsmål, hvis jeg ikke giver dig et komplet udtryk, men i stedet giver jeg dig alle elementerne og deres indekser, hvordan konstruerer du det originale udtryk? For eksempel giver jeg dig her:

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

og fortæller dig, at alle hoveder er List, så hvad er det det originale udtryk?

Nå, du kan helt sikkert rekonstruere det originale udtryk som {{1,2},{3,4}}, men hvordan? Du kan sandsynligvis angive følgende trin:

  1. Først ser vi på det første element i indekset og sorterer og samler efter det. Så ved vi, at første element i hele udtrykket skal indeholde de første to -elementer i den oprindelige liste …
  2. Så fortsætter vi med at se på det andet argument, gør det samme …
  3. Endelig får vi den oprindelige liste som {{1,2},{3,4}}.

Nå, det er rimeligt! Så hvad hvis jeg siger dig, nej, du skal først sortere og samle efter det andet element i indekset og derefter samle efter det første element i indekset? Eller jeg siger, at vi ikke samler dem to gange, vi sorterer bare efter begge elementer, men giver det første argument højere prioritet?

Nå, du får sandsynligvis henholdsvis følgende to liste, ikke?

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

Nå, tjek selv, Flatten[{{1,2},{3,4}},{{2},{1}}] og Flatten[{{1,2},{3,4}},{{1,2}}] gør det samme!

Så hvordan du forstår det andet argument fra Flatten ?

  1. Hvert listeelement i hovedlisten, for eksempel {1,2}, betyder at du skal GATHER alle listerne efter disse elementer i indekset, med andre ord disse niveauer .
  2. Rækkefølgen inde i et listeelement repræsenterer, hvordan du SORT elementerne samlet i en liste i forrige trin . for eksempel betyder {2,1} positionen på det andet niveau har højere prioritet end positionen på det første niveau.

Eksempler

Lad os nu øve os på at være fortrolige med tidligere regler.

1. Transpose

Målet med Transpose på en simpel m * n-matrix er at lave $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Men vi kan overveje det på en anden måde, oprindeligt sorterer vi element efter deres i indeks først og derefter sortere dem efter deres j indeks. Nu skal vi bare ændre at sortere dem efter j indeks først og derefter med i næste! Så koden bliver:

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

Enkelt, ikke?

2. Traditionelt Flatten

Målet med traditionel fladning på en simpel m * n-matrix er at Opret et 1D-array i stedet for en 2D-matrix, for eksempel: Flatten[{{1,2},{3,4}}] returnerer {1,2,3,4}. Det betyder, at vi ikke “t samler elementer denne gang, vi kun sorter dem, først efter deres første indeks og derefter efter det andet:

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

3. ArrayFlatten

Lad os diskutere et mest simpelt tilfælde af ArrayFlatten, her har vi en 4D-liste:

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

så hvordan kan vi gøre en sådan konvertering for at gøre det til en 2D-liste?

$ \ left (\ begin {array} {cc} \ left (\ begin {array} {cc} 1 & 2 \\ 5 & 6 \\ \ end {array} \ right) & \ venstre (\ begin {array} {cc} 3 & 4 \\ 7 & 8 \\ \ end {array} \ højre) \\ \ left (\ begin {array} {cc} 9 & 10 \\ 13 & 14 \\ \ end {array} \ højre) & \ 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) $

Nå, dette er også enkelt, vi har brug for gruppen efter det oprindelige første og tredje niveau indeks først, og vi skal give det første indeks højere prioritet i sortering. Det samme gælder det andet og det fjerde niveau:

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

4. “Ændr størrelse” på et billede

Nu har vi et billede, for eksempel:

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

Men det er bestemt for lille til, at vi kan se, så vi vil gøre det større ved at udvide hver pixel til en stor pixel på 10 * 10 størrelse.

Først skal vi prøve:

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

Men det returnerer en 4D-matrix med dimensionerne {10,10,10,10}. Så vi skal Flatten det. Denne gang vil vi have, at det tredje argument skal have højere prioritet i stedet af den første, så en mindre indstilling fungerer:

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

En sammenligning:

illus2

Håber dette kan hjælpe!

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *