En ting jeg aldri kunne viklet hodet rundt er hvordan Flatten utstyrt med en matrise som det andre argumentet, og Mathematica hjelpen er ikke spesielt bra på denne.

Hentet fra Flatten Mathematica dokumentasjon:

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

Flater list ved å kombinere alle nivåer $ s_ {ij} $ for å gjøre hvert nivå $ i $ i resultatet.

Kan noen utdype hva dette egentlig betyr / gjør?

Svar

En praktisk måte å tenke på Flatten med det andre argumentet er at den utfører noe sånt som Transpose for raggede (uregelmessige) 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}} 

Det som skjer er at elementene som utgjør uted nivå 1 i den opprinnelige listen er nå bestanddeler på nivå 2 i resultatet, og omvendt. Dette er nøyaktig hva Transpose gjør, men gjøres for uregelmessige lister. Vær imidlertid oppmerksom på at noe informasjon om posisjoner går tapt her, så vi kan ikke direkte invertere operasjonen:

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 å få den reversert riktig, ville vi ha å gjøre noe 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 mer interessant eksempel er når vi har dypere hekking:

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 fungerte som (generalisert) Transpose, og byttet ut brikker på de to første nivåene Følgende vil være vanskeligere å 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 bilde illustrerer dette generaliserte transponert:

Illustrasjon av syklisk generalisert transponering

Vi kan gjøre det i to påfølgende trinn:

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 permutasjonen {3,1,2} kan fås som {1,3,2} etterfulgt av {2,1,3}. En annen måte å se hvordan det fungerer er å bruk tall wh som indikerer posisjonen 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}}} *) 

Fra dette kan man se at i den ytterste listen (første nivå), den tredje indeksen (tilsvarende tredje nivå av den opprinnelige listen) vokser, i hver medlemsliste (andre nivå) vokser det første elementet per element (tilsvarer det første nivået av den opprinnelige listen), og til slutt i den innerste (tredje nivå) listene, vokser den andre indeksen , tilsvarende det andre nivået i den opprinnelige listen. Generelt sett, hvis det k-th elementet i listen som er gitt som det andre elementet er {n}, tilsvarer økning av den k-th indeksen i den resulterende listestrukturen å øke den n-th indeksen i opprinnelig struktur.

Til slutt kan man kombinere flere nivåer for effektivt å flate undernivåene, slik:

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 tror det ‘ er lettere å forstå hvis tallene indikerer den opprinnelige plasseringen i den nestede listen. For eksempel, i permutasjonseksemplet med tre tall, ville det være Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}} og resultatet ville lese {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}.
  • @celtschk Takk, men jeg er ikke overbevist. For meg personlig er det lettere å spore visuelt forskjellige tall og se hvor de havner i den nye strukturen. Men legg gjerne dette til svaret mitt, dette er helt greit for meg.
  • Jeg antar at våre måter å forstå er bare forskjellige i så måte. Egentlig først etter å ha skrevet om på min måte, forsto jeg den sykliske permutasjonen helt (men da direkte, uten den to-trinns nedbrytningen du gjorde etterpå). Poenget er at på denne måten kan du umiddelbart se hvilken indeks som endres hvis du beveger deg langs hver liste, og du kan bestemme hvor nummeret stammer fra listestrukturen fra uten å se på originallisten. Med andre ord, det mer direkte (i det minste for meg) avslører transformasjonen struktur .
  • @celtschk Ja, jeg forstod resonnementet ditt. Men for meg var det lettere å forstå hvilke elementer som hoppet hvilke lister, i stedet for å se på elementer ‘ indekser. Kan være det ‘ bare jeg, jeg graviterte alltid til geometri i stedet for algebra. Jeg tror begge veier har sine fordeler.
  • @ 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}} sier du Det som skjer er at elementene som utgjorde nivå 1 i den opprinnelige listen, nå er bestanddeler på nivå 2 i resultatet. Jeg forstår ikke ‘, input og output har samme nivåstruktur, elementene er fortsatt på samme nivå. Kan du forklare dette i det store og hele?

Svar

Et annet listeargument til Flatten serverer to formål. Først spesifiserer den rekkefølgen indeksene skal gjentas når elementer samles. For det andre beskriver den listeflatning i det endelige resultatet. La oss se på hver av disse funksjonene etter tur.

Iterasjonsrekkefølge

Vurder følgende matrise:

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

matriseresultat

Vi kan bruke et Table uttrykk for å lage en kopi av matrisen ved å itere over alle elementene:

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

Denne identiteten operasjonen er uinteressant, men vi kan transformere matrisen ved å bytte rekkefølgen på iterasjonsvariablene. For eksempel kan vi bytte i og j iteratorer. Dette tilsvarer bytte av nivå 1 og nivå 2 indekser og deres tilhørende elementer:

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

matriseresultat

Hvis vi ser nøye på, kan vi se at hvert originale element $m[[i, j, k]] vil bli funnet å svare til det resulterende elementet $r[[j, i, k]] – de to første indeksene har bie n «byttet ut».

Flatten lar oss uttrykke en tilsvarende operasjon til dette Table uttrykket mer kortfattet: / p>

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

Det andre argumentet til Flatten uttrykk spesifiserer spesifikt ønsket indeksrekkefølge: indeks 1, 2, 3 er endret til å bli indekser 2, 1, 3. Legg merke til hvordan vi ikke trengte å spesifisere et område for hver dimensjon i matrisen – en betydelig notasjonskomfort.

Følgende Flatten er en identitetsoperasjon siden den ikke spesifiserer noen endring i indeksrekkefølgen:

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

Mens følgende uttrykk ordner alle tre indeksene på nytt: 1, 2 , 3 -> 3, 2, 1

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

matriseresultat

Igjen , kan vi bekrefte at et originalt element funnet i indeksen [[i, j, k]] nå vil bli funnet på [[k, j, i]] i resultatet.

Hvis noen indekser er utelatt fra en Flatten uttrykk, de behandles som om de hadde blitt spesifisert sist og i sin naturlige rekkefølge:

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

Dette siste eksemplet kan forkortes ytterligere:

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

En tom indeksliste resulterer i identitetsoperasjonen:

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

Det tar seg av iterasjonsrekkefølge og indeksbytte. La oss se på …

Listeutflatning

Man kan lure på hvorfor vi måtte spesifisere hver indeks i en underliste i de foregående eksemplene. Årsaken er at hver underliste i indeksspesifikasjonen spesifiserer hvilke indekser som skal flates sammen i resultatet. Tenk igjen på følgende identitetsoperasjon:

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

matriseresultat

Hva skjer hvis vi kombinerer de to første indeksene i samme underliste ?

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

matriseresultat

Vi kan se at det opprinnelige resultatet var et 4 x 3 rutenett av par, men det andre resultatet er en enkel parliste. Den dypeste strukturen, parene, ble uberørt. De to første nivåene er flatet ut til ett nivå. Parene i det tredje nivået av kilden matrise forble ikke flat.

Vi kunne kombinere de to andre indeksene i stedet:

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

matriseresultat

Dette resultatet har samme antall rader som den opprinnelige matrisen, noe som betyr at det første nivået ble uberørt. Men hver resultatrad har en flat liste med seks elementer hentet fra den tilsvarende opprinnelige raden med tre par. Dermed er de to nedre nivåene blitt flatt.

Vi kan også kombinere alle tre indeksene for å få et fullstendig flatt resultat:

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

matriseresultat

Dette kan forkortes:

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

Flatten tilbyr også en kort beskrivelse når ingen indeksbytte skal finne 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 eksemplene så langt har brukt matriser av forskjellige dimensjoner. Flatten tilbyr en veldig kraftig funksjon som gjør det til mer enn bare en forkortelse for et Table -uttrykk. Flatten vil elegant behandle saken der underlister på et gitt nivå har forskjellige lengder. Manglende elementer blir stille ignorert. For eksempel kan en trekantet matrise 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 flatt:

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 Takk. Jeg regner med at hvis Flatten ble generalisert til å akseptere en funksjon som skal brukes når flate (kontraherende) indekser, ville det være en utmerket start på » tensoralgebra i en boks «.
  • Noen ganger trenger vi å gjøre interne sammentrekninger. Nå vet jeg at jeg kan gjøre det ved å bruke Flatten[$m, {{1}, {2, 3}}] i stedet for Map Flatten over et eller annet nivå. Det ville være fint om Flatten aksepterte negative argumenter for å gjøre det. Så denne saken kan være å skrive som Flatten[$m, -2].
  • Hvorfor dette utmerkede svaret fikk færre stemmer enn Leonid ‘ s: (.
  • @Tangshutao Se andre vanlige spørsmål om -profilen .

Svar

Jeg lærte mye av WReachs og Leonids svar, og jeg vil gjerne gi et lite bidrag:

Det virker verdt understreker at den primære hensikten med det listeverdige andre argumentet til Flatten bare er å flate visse nivåer av lister (som WReach nevner i sin Listeutflatning seksjon). Bruk av Flatten som en fillete Transpose virker som en side -effekten av dette primære designet, etter min mening.

For eksempel trengte jeg for eksempel i går denne transformasjonen

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

treform1

til 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 si at jeg trengte å knuse andre og tredje listenivå sammen.

Jeg gjorde det med

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

Svar

Dette er et gammelt spørsmål, men ofte spurt av et parti av mennesker. I dag da jeg prøvde å forklare hvordan dette fungerer, kom jeg over en ganske klar forklaring, så jeg tror det å dele det her vil være nyttig for videre publikum.

Hva betyr indeks?

La oss først gjøre klart hva indeks : I Mathematica er hvert uttrykk et tre, for eksempel, la oss se på en liste:

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

illus1

Hvordan navigerer du i et tre?

Enkelt! Du starter fra roten og velger ved hver kryssing hvilken vei du vil gå, for eksempel her hvis du vil nå 2, begynner du med å velge først bane, velg deretter andre bane. La oss skrive det ut som {1,2} som er bare indeksen til elementet 2 i dette uttrykket.

Hvordan forstå Flatten?

Her bør du vurdere et enkelt spørsmål hvis jeg ikke gir deg et fullstendig uttrykk, men i stedet gir jeg deg alle elementene og deres indekser, hvordan konstruerer du det originale uttrykket? Her gir jeg deg for eksempel:

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

og forteller deg at alle hoder er List, så hva er det det opprinnelige uttrykket?

Vel, du kan sikkert rekonstruere det opprinnelige uttrykket som {{1,2},{3,4}}, men hvordan? Du kan sannsynligvis liste følgende trinn:

  1. Først ser vi på det første elementet i indeksen og sorterer og samler etter det. Så vet vi at første element i hele uttrykket skal inneholde de to første elementene i den opprinnelige listen …
  2. Så fortsetter vi å se på det andre argumentet, gjør det samme …
  3. Endelig får vi den opprinnelige listen som {{1,2},{3,4}}.

Vel, det er rimelig! Så hva om jeg sier deg, nei, du bør først sortere og samle etter det andre elementet i indeksen og deretter samle etter det første elementet i indeksen? Eller jeg sier at vi ikke samler dem to ganger, vi sorterer bare etter begge elementene, men gir det første argumentet høyere prioritet?

Vel, du vil sannsynligvis få de følgende to listene, ikke sant?

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

Vel, sjekk selv, Flatten[{{1,2},{3,4}},{{2},{1}}] og Flatten[{{1,2},{3,4}},{{1,2}}] gjør det samme!

Så hvordan du forstår det andre argumentet til Flatten ?

  1. Hvert listeelement i hovedlisten, for eksempel {1,2}, betyr at du bør GATHER alle listene i henhold til disse elementene i indeksen, med andre ord disse nivåer .
  2. Bestillingen inne i et listeelement representerer hvordan du SORT elementene samlet i en liste i forrige trinn . for eksempel betyr {2,1} at posisjonen på andre nivå har høyere prioritet enn posisjonen på første nivå.

Eksempler

La oss nå ha litt øvelse for å bli kjent med tidligere regler.

1. Transpose

Målet med Transpose på en enkel m * n-matrise er å lage $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Men vi kan vurdere det på en annen måte, opprinnelig sorterer vi element etter deres i indeks først, og deretter sortere dem etter j indeksen. Nå trenger vi bare å endre for å sortere dem etter j indeks først og deretter med i neste! Så koden blir:

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

Enkelt, ikke sant?

2. Tradisjonelt Flatten

Målet med tradisjonell flatning på en enkel m * n-matrise er å lag en 1D-matrise i stedet for en 2D-matrise, for eksempel: Flatten[{{1,2},{3,4}}] returnerer {1,2,3,4}. Dette betyr at vi ikke «t samler elementer denne gangen, vi bare sorter dem, først etter deres første indeks og deretter med den andre:

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

3. ArrayFlatten

La oss diskutere et mest enkelt tilfelle av 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 gjøre en slik konvertering for å gjø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øyre) \\ \ left (\ begin {array} {cc} 9 & 10 \\ 13 & 14 \\ \ end {array} \ høyre) & \ 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) $

Vel, dette er også enkelt, vi trenger gruppen etter den opprinnelige første og tredje nivåindeksen først, og vi bør gi den første indeksen høyere prioritet i sortering. Det samme gjelder andre og fjerde nivå:

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

4. «Endre størrelse» på et bilde

Nå har vi et bilde, for eksempel:

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

Men det er definitivt for lite til at vi kan visning, så vi vil gjøre den større ved å utvide hver piksel til en stor piksel på 10 * 10 størrelse.

Først skal vi prøve:

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

Men den returnerer en 4D-matrise med dimensjonene {10,10,10,10}. Så vi bør Flatten den. Denne gangen vil vi at det tredje argumentet i stedet skal ha høyere prioritet av den første, så en mindre innstilling ville fungere:

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

En sammenligning:

illus2

Håper dette kan hjelpe!

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *