En sak som jag aldrig kunde linda mitt huvud är hur Flatten fungerar när försedd med en matris som det andra argumentet, och Mathematica -hjälpen är inte särskilt bra för den här.

Hämtad från Flatten Mathematica dokumentation:

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

Plattar list genom att kombinera alla nivåer $ s_ {ij} $ för att göra varje nivå $ i $ i resultatet.

Kan någon utarbeta vad detta egentligen betyder / gör?

Svar

En bekvämt sätt att tänka på Flatten med det andra argumentet är att den utför något som Transpose för trasiga (oregelbundna) listor. Här är en enkel exempel:

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

Vad som händer är de element som utgör uted nivå 1 i originallistan är nu beståndsdelar på nivå 2 i resultatet, och vice versa. Detta är exakt vad Transpose gör, men görs för oregelbundna listor. Observera dock att viss information om positioner går förlorad här, så vi kan inte direkt invertera 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}} 

För att få den omvänd korrekt skulle vi ha att göra något så här:

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

Ett mer intressant exempel är när vi har djupare häckning:

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

Även här kan vi se att Flatten effektivt fungerade som (generaliserat) Transpose, och bytte delar på de första två nivåerna Följande blir svårare att förstå:

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öljande bild illustrerar denna generaliserade transponering:

Illustration av cyklisk generaliserad transponering

Vi kan göra det i två på varandra följande steg:

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

Eftersom permutationen {3,1,2} kan erhållas som {1,3,2} följt av {2,1,3}. Ett annat sätt att se hur det fungerar är att använd siffror wh som anger positionen i liststrukturen:

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

Från detta kan man se att det tredje indexet (motsvarande det i den yttersta listan (första nivån) tredje nivån i originallistan) växer, i varje medlemslista (andra nivån) växer det första elementet per element (motsvarande den första nivån i originallistan), och slutligen i de innersta (tredje nivåerna) listorna växer det andra indexet motsvarande den andra nivån i originallistan. Om k-elementet i listan som skickas som andra element är {n}, motsvarar i allmänhet växande k-index i den resulterande liststrukturen en ökning av det n-index i ursprunglig struktur.

Slutligen kan man kombinera flera nivåer för att effektivt platta ut undernivåerna, så här:

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

  • Jag tror att det ’ är lättare att förstå om siffrorna anger den ursprungliga positionen i den kapslade listan. I permutationsexemplet med tre siffror skulle det till exempel vara Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}} och resultatet skulle läsa {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}.
  • @celtschk Tack, men jag är inte övertygad. För mig personligen är det lättare att spåra visuellt distinkta siffror och se var de hamnar i den nya strukturen. Men lägg gärna till detta i mitt svar, det här är helt bra för mig.
  • Jag antar att våra sätt att förstå är helt annorlunda i det avseendet. Egentligen först efter att jag skrev om mitt sätt förstod jag helt den cykliska permutationen (men sedan direkt, utan den tvåstegsnedbrytning du gjorde efteråt). Poängen är att på detta sätt kan du se omedelbart vilket index som ändras om du flyttar längs varje lista och du kan bestämma var numret härstammar från liststrukturen utan att ens titta på originallistan. Med andra ord avslöjar det mer direkt (åtminstone för mig) transformationens struktur .
  • @celtschk Ja, jag förstod ditt resonemang. Men för mig var det lättare att förstå vilka element som hoppade vilka listor, snarare än att titta på element ’ index. Kan det vara ’ bara jag, jag graviterade alltid till geometri snarare än algebra. Jag tror att båda sätten har sina fördelar.
  • @ LeonidShifrin För 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}} säger du. Vad som händer är att element som utgjorde nivå 1 i originallistan nu är beståndsdelar på nivå 2 i resultatet. Jag förstår inte ’, ingång och utgång har samma nivåstruktur, elementen är fortfarande på samma nivå. Kan du förklara det i stort?

Svar

Ett andra listargument till Flatten tjänar två syften. Först specificerar den i vilken ordning index ska upprepas när element samlas. För det andra beskriver den listutplattning i slutresultatet. Låt oss titta på var och en av dessa funktioner i tur och ordning.

Iterationsordning

Tänk på följande matris:

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

matrisresultat

Vi kan använda ett Table -uttryck för att skapa en kopia av matrisen genom att itera över alla dess element:

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

Denna identitet operation är ointressant, men vi kan transformera arrayen genom att byta ordning på iterationsvariablerna. Vi kan till exempel byta ut i och j iteratorer. Detta innebär att byta nivå 1 och nivå 2 index och deras motsvarande element:

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

matrisresultat

Om vi tittar noga kan vi se att varje originalelement $m[[i, j, k]] kommer att visa sig motsvara det resulterande elementet $r[[j, i, k]] – de två första indexen har bi n ”bytt”.

Flatten tillåter oss att uttrycka en motsvarande operation till detta Table uttryck mer kortfattat:

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

Det andra argumentet för Flatten uttrycket anger uttryckligen önskad indexordning: index 1, 2, 3 är ändrad för att bli index 2, 1, 3. Observera hur vi inte behövde ange ett intervall för varje dimension i matrisen – en betydande notational bekvämlighet.

Följande Flatten är en identitetsåtgärd eftersom den inte anger någon ändring av indexordning:

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

Medan följande uttryck ordnar om alla tre index: 1, 2 , 3 -> 3, 2, 1

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

matrisresultat

Återigen kan vi verifiera att ett originalelement som finns i index [[i, j, k]] nu kommer att hittas vid [[k, j, i]] i resultatet.

Om några index utelämnas från ett Flatten uttryck, de behandlas som om de hade specificerats senast och i sin naturliga ordning:

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

Det här sista exemplet kan förkortas ytterligare:

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

En tom indexlista resulterar i identitetsåtgärden:

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

Det tar hand om iterationsordning och indexbyte. Låt oss nu titta på …

Lista ut plattning

Man kan undra varför vi var tvungna att specificera varje index i en underlista i de tidigare exemplen. Anledningen är att varje sublista i indexspecifikationen anger vilka index som ska planas samman i resultatet. Tänk igen på följande identitetsoperation:

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

matrisresultat

Vad händer om vi kombinerar de två första indexen i samma underlista ?

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

matrisresultat

Vi kan se att det ursprungliga resultatet var ett 4 x 3 rutnät med par, men det andra resultatet är en enkel parlista. Den djupaste strukturen, paren, lämnades orörd. De två första nivåerna har plattats ut till en enda nivå. Paren i tredje nivå av källan matris förblev oplattad.

Vi kunde kombinera de två andra indexen istället:

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

matrisresultat

Detta resultat har samma antal rader som den ursprungliga matrisen, vilket innebär att den första nivån lämnades orörd. Men varje resultatrad har en platt lista med sex element som tagits från motsvarande originalrad med tre par. Således har de nedre två nivåerna planats ut.

Vi kan också kombinera alla tre index för att få ett helt plattat resultat:

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

matrisresultat

Detta kan förkortas:

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

Flatten erbjuder också en kortfattad notation när ingen indexbyte ska äga rum:

$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

Alla exemplen hittills har använt matriser av olika dimensioner. Flatten erbjuder en mycket kraftfull funktion som gör det mer än bara en förkortning för ett Table -uttryck. Flatten hanterar på ett elegant sätt fall där underlistor på en viss nivå har olika längder. Saknade element ignoreras tyst. Till exempel kan en triangulär array vändas:

$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 vänd och platt:

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

Kommentarer

  • Detta är en fantastisk och grundlig förklaring!
  • @ rm-rf Tack. Jag tänker att om Flatten generaliserades för att acceptera en funktion som ska tillämpas vid plana (kontraherande) index, skulle det vara en utmärkt start för ” tensoralgebra i en burk ”.
  • Ibland behöver vi göra interna sammandragningar. Nu vet jag att jag kan göra det med Flatten[$m, {{1}, {2, 3}}] istället för Map Flatten över en viss nivå. Det vore trevligt om Flatten accepterade negativa argument för att göra det. Så det här fallet kan vara att skriva som Flatten[$m, -2].
  • Varför detta utmärkta svar fick färre röster än Leonid ’ s: (.
  • @Tangshutao Se den andra FAQ om min -profil .

Svar

Jag lärde mig mycket av WReachs och Leonids svar och jag skulle vilja ge ett litet bidrag:

Det verkar värt betonar att den primära avsikten med det listvärderade andra argumentet för Flatten bara är att plana ut vissa nivåer av listor (som WReach nämner i sin Lista utplattning avsnitt). Att använda Flatten som en trasig Transpose verkar vara en sida -effekten av denna primära design, enligt min mening.

Till exempel, igår behövde jag förvandla den här listan

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

treform1

i den här:

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

treform2

Det vill säga att jag behövde krossa den andra och tredje listnivån tillsammans.

Jag gjorde det med

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

Svar

Detta är en gammal fråga, men ofta ställd av ett -parti av människor. Idag när jag försökte förklara hur detta fungerar kom jag över en ganska tydlig förklaring, så jag tror att det skulle vara till hjälp för ytterligare publik att dela det här.

Vad betyder index?

Låt oss först tydliggöra vad index : I Mathematica är varje uttryck ett träd, till exempel, låt oss se i en lista:

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

illus1

Hur du navigerar i ett träd?

Enkelt! Du börjar från roten och väljer vid varje korsning vilken väg du ska gå, till exempel här om du vill nå 2, börjar du med att välja först sökväg och välj sedan andra sökväg. Låt oss skriva ut det som {1,2} vilket är bara indexet för elementet 2 i detta uttryck.

Hur man förstår Flatten?

Tänk här på en enkel fråga om jag inte ger dig ett fullständigt uttryck, utan istället ger dig alla element och deras index, hur konstruerar du det ursprungliga uttrycket? Här ger jag dig till exempel:

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

och säger att alla huvuden är List, så vad är det det ursprungliga uttrycket?

Du kan säkert rekonstruera det ursprungliga uttrycket som {{1,2},{3,4}}, men hur? Du kan antagligen lista följande steg:

  1. Först tittar vi på det första elementet i index och sorterar och samlar efter det. Sedan vet vi att första element i hela uttrycket ska innehålla de första två elementen i originallistan …
  2. Sedan fortsätter vi att titta på det andra argumentet, gör detsamma …
  3. Slutligen får vi den ursprungliga listan som {{1,2},{3,4}}.

Tja, det är rimligt! Så vad händer om jag säger till dig, nej, du bör först sortera och samla på det andra elementet i indexet och sedan samla på det första elementet i indexet? Eller jag säger att vi inte samlar dem två gånger, vi sorterar bara efter båda elementen men ger det första argumentet högre prioritet?

Tja, du skulle förmodligen få följande två listor, eller hur?

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

Tja, kolla själv, Flatten[{{1,2},{3,4}},{{2},{1}}] och Flatten[{{1,2},{3,4}},{{1,2}}] gör samma sak!

Så, hur du förstår Flatten andra argument ?

  1. Varje listelement i huvudlistan, till exempel {1,2}, betyder att du bör GATHER alla listor enligt dessa element i indexet, med andra ord dessa nivåer .
  2. Ordningen i ett listelement representerar hur du SORT elementen samlade i en lista i föregående steg . till exempel {2,1} betyder att positionen på den andra nivån har högre prioritet än positionen på den första nivån.

Exempel

Nu ska vi ha lite övning för att känna till tidigare regler.

1. Transpose

Målet med Transpose på en enkel m * n-matris är att göra $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Men vi kan betrakta det på ett annat sätt, ursprungligen sorterar vi element efter deras i index först och sedan sortera dem efter deras j index, nu är allt vi behöver göra att ändra för att sortera dem efter j index först sedan av i nästa! Så koden blir:

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

Enkelt, eller hur?

2. Traditionellt Flatten

Målet med traditionell plattning på en enkel m * n-matris är att skapa en 1D-array istället för en 2D-matris, till exempel: Flatten[{{1,2},{3,4}}] returnerar {1,2,3,4}. Det betyder att vi inte ”t samlar in element den här gången, vi sortera dem, först med deras första index och sedan med det andra:

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

3. ArrayFlatten

Låt oss diskutera ett mycket enkelt fall av ArrayFlatten, här har vi en 4D-lista:

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

så hur kan vi göra en sådan konvertering för att göra den till en 2D-lista?

$ \ left (\ begin {array} {cc} \ left (\ begin {array} {cc} 1 & 2 \\ 5 & 6 \\ \ end {array} \ right) & \ vänster (\ begin {array} {cc} 3 & 4 \\ 7 & 8 \\ \ end {array} \ höger) \\ \ left (\ begin {array} {cc} 9 & 10 \\ 13 & 14 \\ \ end {array} \ höger) & \ 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åväl, det här är också enkelt, vi behöver gruppen efter det ursprungliga första och tredje nivåindexet först, och vi bör ge det första indexet högre prioritet i sortering. Detsamma gäller den andra och fjärde nivån:

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

4. ”Ändra storlek på en bild

Nu har vi en bild, till exempel:

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

Men den är definitivt för liten för att vi ska kunna se, så vi vill göra den större genom att utöka varje pixel till en stor pixel på 10 * 10.

Först ska vi försöka:

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

Men den returnerar en 4D-matris med måtten {10,10,10,10}. Så vi ska Flatten den. Den här gången vill vi att det tredje argumentet istället tar högre prioritet av den första, så en mindre inställning skulle fungera:

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

En jämförelse:

illus2

Hoppas det kan hjälpa!

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *