Un lucru pe care nu l-aș putea înfășura niciodată este modul în care funcționează Flatten când furnizat cu o matrice ca al doilea argument, iar ajutorul Mathematica nu este deosebit de bun în acest sens.

Luat din Flatten Documentare Mathematica :

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

Aplatizează list combinând toate nivelurile $ s_ {ij} $ pentru a face ca fiecare nivel să fie $ i $ în rezultat.

Ar putea cineva să ofere detalii despre ceea ce înseamnă / face de fapt acest lucru?

Răspunde

One un mod convenabil de a vă gândi la Flatten cu al doilea argument este că efectuează ceva de genul Transpose pentru listele zdrențuite (neregulate). Iată un simplu exemplu:

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

Ce se întâmplă sunt acele elemente care constituie nivelul divizat 1 din lista originală sunt acum elemente constitutive la nivelul 2 în rezultat și invers. Este exact ceea ce face Transpose, dar este făcut pentru liste neregulate. Rețineți totuși că unele informații despre poziții sunt pierdute aici, deci nu putem inversa direct operația:

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

Pentru a fi inversate corect, ar fi să facem așa ceva:

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 exemplu mai interesant este atunci când avem cuiburi mai profunde:

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

Aici din nou, putem vedea că Flatten a funcționat efectiv ca (generalizat) Transpose, schimbând piese la primele 2 niveluri . Următoarele vor fi mai greu de înțeles:

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

Următoarea imagine ilustrează această transpunere generalizată:

Ilustrație a transpunerii generalizate ciclice

O putem face în doi pași consecutivi:

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

De la permutarea {3,1,2} poate fi obținut ca {1,3,2} urmat de {2,1,3}. Un alt mod de a vedea cum funcționează este să folosiți numere wh ich indică poziția în structura listei:

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

Din aceasta, se poate vedea că în lista cea mai exterioară (primul nivel), al treilea index (corespunzător al treilea nivel al listei originale) crește, în fiecare listă de membri (al doilea nivel) crește primul element pe element (corespunzător primului nivel al listei originale) și, în cele din urmă, în listele cele mai interioare (al treilea nivel), al doilea index crește , corespunzător celui de-al doilea nivel din lista originală. În general, dacă al k-lea element al listei trecut ca al doilea element este {n}, creșterea indicelui k-lea în structura listei rezultate corespunde creșterii indexului n structură originală.

În cele din urmă, se pot combina mai multe niveluri pentru a aplatiza în mod eficient sub-nivelurile, astfel:

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

Comentarii

  • Cred că ‘ este mai ușor de înțeles dacă numerele indică poziția inițială în lista imbricată. De exemplu, în exemplul de permutare cu trei numere, ar fi Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}}, iar rezultatul ar citi {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}.
  • @celtschk Mulțumesc, dar nu sunt convins. Pentru mine personal, este mai ușor să urmăriți numere distincte din punct de vedere vizual și să vedeți unde ajung în noua structură. Dar nu ezitați să adăugați acest lucru la răspunsul meu, este perfect pentru mine.
  • Cred că modurile noastre de înțelegere sunt doar diferite în acest sens. De fapt, numai după ce mi-am rescris calea, am înțeles complet permutarea ciclică (dar apoi direct, fără descompunerea în doi pași pe care ați făcut-o după aceea). Ideea este că în acest fel, puteți vedea imediat ce indice se modifică dacă vă deplasați de-a lungul fiecărei liste și puteți determina de unde a provenit numărul din structura listei, fără a privi chiar lista originală. Cu alte cuvinte, mai direct (cel puțin mie) dezvăluie structura transformării.
  • @celtschk Da, am înțeles raționamentul tău. Dar pentru mine, a fost mai ușor să înțeleg ce elemente au sărit pe care liste, mai degrabă decât să privească elementele ‘. Poate că este ‘ doar eu, am gravitat întotdeauna mai degrabă la geometrie decât la algebră. Cred că ambele moduri au meritele lor.
  • @LeonidShifrin Pentru 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}}, spuneți că ceea ce se întâmplă este că elementele care au constituit nivelul 1 în lista originală sunt acum elemente constitutive la nivelul 2 în rezultat. Nu ‘ nu înțeleg destul, intrarea și ieșirea au aceeași structură de nivel, elementele sunt încă la același nivel. Ați putea explica acest lucru în general?

Răspuns

Un al doilea argument list pentru Flatten servește două scopuri. În primul rând, specifică ordinea în care indicii vor fi iterați atunci când se adună elemente. În al doilea rând, descrie aplatizarea listei în rezultatul final. Să vedem la rândul lor fiecare dintre aceste funcții.

Ordinea de iterație

Luați în considerare următoarea matrice:

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

rezultatul matricei

Putem folosi o expresie Table pentru a crea o copie a matricei prin iterarea tuturor elementelor sale:

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

Această identitate operația este neinteresantă, dar putem transforma tabloul schimbând ordinea variabilelor de iterație. De exemplu, putem schimba i și j iteratori. Aceasta înseamnă schimbarea indicilor de nivel 1 și de nivel 2 și a elementelor corespunzătoare ale acestora:

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

rezultatul matricei

Dacă ne uităm cu atenție, putem vedea că fiecare element original $m[[i, j, k]] va fi găsit pentru a corespunde elementului rezultat $r[[j, i, k]] – primii doi indici au albine n „swapped”.

Flatten ne permite să exprimăm o operație echivalentă cu această expresie Table mai succint:

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

Al doilea argument al expresiei Flatten specifică în mod explicit ordinea indexului dorită: indicii 1, 2, 3 sunt modificat pentru a deveni indici 2, 1, 3. Rețineți că nu a fost nevoie să specificăm un interval pentru fiecare dimensiune a matricei – o comoditate notatională semnificativă.

Următorul Flatten este o operațiune de identitate, deoarece nu specifică nicio modificare a ordinii indexului:

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

Întrucât următoarea expresie reorganizează toți cei trei indici: 1, 2 , 3 -> 3, 2, 1

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

rezultatul matricei

Din nou , putem verifica dacă un element original găsit la index [[i, j, k]] va fi acum găsit la [[k, j, i]] în rezultat.

Dacă indicii sunt omiși dintr-un Flatten expresie, acestea sunt tratate ca și când ar fi fost specificate ultima și în ordinea lor naturală:

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

Acest ultim exemplu poate să fie abreviat și mai departe:

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

O listă de indici goale are ca rezultat operațiunea de identitate:

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

Aceasta se ocupă de ordinea de iterație și de schimbarea indexului. Acum, să vedem …

List aplatizare

Ne-am putea întreba de ce a trebuit să specificăm fiecare index dintr-o sublistă în exemplele anterioare. Motivul este că fiecare sublistă din specificația indexului specifică ce indici trebuie aplatizați împreună în rezultat. Luați în considerare din nou următoarea operație de identitate:

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

rezultatul matricei

Ce se întâmplă dacă combinăm primii doi indici în aceeași sublistă ?

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

rezultatul matricei

Putem vedea că rezultatul inițial a fost un Grila de perechi de 4 x 3, dar al doilea rezultat este o listă simplă de perechi. Cea mai profundă structură, perechile, au fost lăsate neatinse. Primele două niveluri au fost aplatizate într-un singur nivel. Perechile din al treilea nivel al sursei matricea a rămas neatinsă.

În schimb, am putea combina al doilea doi indici:

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

rezultatul matricei

Acest rezultat are același număr de rânduri ca matricea originală, ceea ce înseamnă că primul nivel a fost lăsat neatins. Dar fiecare rând de rezultate are o listă plană de șase elemente preluate din rândul original corespunzător de trei perechi. Astfel, cele două niveluri inferioare au fost aplatizate.

De asemenea, putem combina toți cei trei indici pentru a obține un rezultat complet aplatizat:

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

rezultatul matricei

Aceasta poate fi abreviată:

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

Flatten oferă, de asemenea, o notație de tip stenogramă atunci când nu va avea loc schimbarea indexului:

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

Matrice „Ragged”

Toate exemplele de până acum au folosit matrici de diferite dimensiuni. Flatten oferă o caracteristică foarte puternică, care o face mai mult decât o abreviere pentru o expresie Table. Flatten va gestiona cu grație cazul în care sublistele de la un nivel dat au lungimi diferite. Elementele lipsă vor fi ignorate în liniște. De exemplu, un tablou triunghiular poate fi răsturnat:

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

…sau răsturnat și aplatizat:

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

Comentarii

  • Aceasta este o explicație fantastică și amănunțită!
  • @ rm-rf Mulțumesc. Cred că dacă Flatten ar fi generalizat pentru a accepta o funcție de aplicat la aplatizarea (contractarea) indicilor, ar fi un început excelent pentru ” algebra tensorială într-o cutie „.
  • Uneori trebuie să facem contracții interne. Acum știu că o pot face folosind Flatten[$m, {{1}, {2, 3}}] în loc de Map Flatten pe un anumit nivel. Ar fi frumos dacă Flatten ar accepta argumente negative pentru a face acest lucru. Deci, acest caz ar putea fi scris ca Flatten[$m, -2].
  • De ce acest răspuns excelent a obținut mai puține voturi decât Leonid ‘ s: (.
  • @Tangshutao Consultați a doua întrebare frecventă pe profilul meu .

Răspuns

Am învățat multe din răspunsurile WReach și Leonid și aș vrea să aduc o mică contribuție:

Se pare că merită subliniind că intenția principală a celui de-al doilea argument al listei Flatten este doar de a aplatiza anumite niveluri de liste (așa cum menționează WReach în Listă aplatizarea secțiune). Utilizarea Flatten ca Transpose zdrențuită pare o parte -efectul acestui design primar, după părerea mea.

De exemplu, ieri trebuia să transform această listă

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

treeform1

în acesta:

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

treeform2

Adică, trebuia să zdrobesc al doilea și al treilea nivel de listă împreună.

Am făcut-o cu

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

Răspuns

Aceasta este o întrebare veche, dar frecvent adresată de un lot de oameni. Astăzi, când încercam să explic cum funcționează acest lucru, am dat peste o explicație destul de clară, așa că cred că împărtășirea acesteia ar fi utilă pentru un public suplimentar.

Ce înseamnă indexul?

Mai întâi, să „clarificăm ce este index : în Mathematica fiecare expresie este un copac, de exemplu, să arătăm la o listă:

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

illus1

Cum navigați într-un copac?

Simplu! Începeți de la rădăcină și la fiecare traversare alegeți ce cale să mergeți, de exemplu, aici, dacă doriți să ajungeți la 2, începeți cu alegerea mai întâi calea , apoi alegeți calea a doua . Să-l scriem ca {1,2} care este doar indexul elementului 2 din această expresie.

Cum să înțelegeți Flatten?

Aici luați în considerare o întrebare simplă, dacă nu vă ofer o expresie completă, dar în schimb vă dau toate elementele și indicii lor, cum construiți expresia originală? De exemplu, aici vă dau:

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

și vă spun că toate capetele sunt List, deci ce s expresia originală?

Ei bine, cu siguranță puteți reconstrui expresia originală ca {{1,2},{3,4}}, dar cum? Probabil puteți enumera următorii pași:

  1. Mai întâi ne uităm la primul element de index și sortăm și adunăm după el. Apoi știm că primul elementul întregii expresii trebuie să conțină primele două elemente din lista originală …
  2. Apoi continuăm să ne uităm la al doilea argument, să facem același lucru …
  3. În cele din urmă obținem lista originală ca {{1,2},{3,4}}.

Ei bine, este rezonabil! Deci, dacă vă spun, nu, ar trebui mai întâi să sortați și să strângeți după al doilea element al indexului și apoi să strângeți după primul element al indexului? Sau spun că nu le adunăm de două ori, doar sortăm după ambele elemente, dar acordăm priorității mai mari primului argument?

Ei bine, probabil că veți obține respectivele două liste, nu?

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

Ei bine, verificați singur, Flatten[{{1,2},{3,4}},{{2},{1}}] și Flatten[{{1,2},{3,4}},{{1,2}}] faceți la fel!

Deci, cum înțelegeți al doilea argument al lui Flatten ?

  1. Fiecare element de listă din lista principală, de exemplu, {1,2}, înseamnă că ar trebui să ADUNĂ toate listele conform acestor elemente din index, cu alte cuvinte aceste niveluri .
  2. Ordinea din interiorul unui element de listă reprezintă modul în care SORT elementele adunate într-o listă în pasul anterior . de exemplu, {2,1} înseamnă că poziția de la al doilea nivel are prioritate mai mare decât poziția de la primul nivel.

Exemple

Acum, să avem o anumită practică pentru a ne familiariza cu regulile anterioare.

1. Transpose

Scopul Transpose pe o matrice simplă m * n este de a face $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Dar o putem considera într-un alt mod, inițial sortăm element după indexul i mai întâi, apoi sortează-le după indexul j, acum tot ce trebuie să facem este să ne schimbăm pentru a le sorta după j index mai întâi apoi de i apoi! Deci codul devine:

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

Simplu, nu?

2. Tradițional Flatten

Scopul aplatizării tradiționale pe o matrice simplă m * n este să creați o matrice 1D în loc de o matrice 2D, de exemplu: Flatten[{{1,2},{3,4}}] returnează {1,2,3,4}. Aceasta înseamnă că de data aceasta nu „adunăm” elemente adunăm , doar sortați-le , mai întâi după primul lor index, apoi cu al doilea:

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

3. ArrayFlatten

Să discutăm despre un caz simplu al ArrayFlatten, aici avem o listă 4D:

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

deci cum putem face o astfel de conversie pentru a face din aceasta o listă 2D?

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

Ei bine, și acest lucru este simplu, avem nevoie mai întâi de grupul după primul index original și cel de al treilea nivel și ar trebui să acordăm primului index o prioritate mai mare în triere. Același lucru este valabil și pentru al doilea și al patrulea nivel:

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

4. „Redimensionați” o imagine

Acum avem o imagine, de exemplu:

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

Dar este cu siguranță prea mic pentru noi vizualizare, așa că dorim să-l mărim extinzând fiecare pixel la un pixel imens de 10 * 10.

Mai întâi vom încerca:

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

Dar returnează o matrice 4D cu dimensiunile {10,10,10,10}. Deci ar trebui să Flatten. De data aceasta dorim ca al treilea argument să aibă prioritate mai mare din prima, deci o reglare minoră ar funcționa:

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

O comparație:

illus2

Sper că acest lucru vă poate ajuta!

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *