Egy dolog, amire soha nem tudtam átgondolni a fejemet, az az, hogy a Flatten hogyan működik, amikor második argumentumként mátrixot kapott, és a Mathematica súgó nem különösebben jó ebben.

A Flatten Mathematica dokumentáció:

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

Elsimítja list az összes $ s_ {ij} $ szint kombinálásával, hogy minden szint $ i $ legyen az eredményben.

Tudna valaki részletezni, hogy ez valójában mit jelent / jelent?

Válasz

Egy A Flatten gondolkodásának kényelmes módja a második érvvel az, hogy valami olyasmit hajt végre, mint a Transpose rongyos (szabálytalan) listáknál. Itt van egy egyszerű példa:

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

Az történik, hogy az elemek alkotják Az eredeti listában az uted 1 szint mostantól az eredmény 2 szintű alkotóelemei és fordítva. A Transpose pontosan ezt teszi, de a szabálytalan listák esetében. Ne feledje azonban, hogy a pozíciókkal kapcsolatos információk itt elvesznek, ezért nem tudjuk közvetlenül megfordítani a műveletet:

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

A helyes megfordítás érdekében ilyesmit tenni:

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

Érdekesebb példa, ha mélyebben fészkelünk:

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

Itt is láthatjuk, hogy a Flatten hatékonyan működött (általánosítva) Transpose, és az első 2 szinten darabokat váltott . A következőket nehezebb lesz megérteni:

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

A következő kép ezt az általános átültetést szemlélteti:

A ciklikusan általánosított transzponálás illusztrációja

Két egymást követő lépésben tehetjük meg:

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

Mivel a permutáció {3,1,2} {1,3,2} néven kapható, amelyet {2,1,3} követhet. A működésének másik módja az, hogy számokat használjon wh ich jelzi a pozíciót a listaszerkezetben:

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

Ebből látható, hogy a legkülső listában (első szint) a harmadik index (a az eredeti lista harmadik szintje) nő, minden taglistában (második szint) az első elem növekszik elemenként (megfelel az eredeti lista első szintjének), végül a legbelső (harmadik szint) listákban a második index növekszik , amely megfelel az eredeti lista második szintjének. Általában, ha a lista második elemként átadott k-edik eleme {n}, az eredményül kapott listaszerkezetben a k-edik index növelése megfelel az eredeti struktúra.

Végül több szint kombinálható az alszintek hatékony ellaposításához, így:

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

Megjegyzések

  • Szerintem ‘ könnyebben érthető, ha a számok jelzik az eredeti helyet a beágyazott listában. Például a háromszámos permutációs példában ez Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}} lenne, az eredmény pedig {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}.
  • @celtschk Köszönöm, de nem vagyok meggyőződve róla. Nekem személy szerint könnyebb követni a vizuálisan elkülönülő számokat, és megnézni, hova kerülnek az új struktúrában. De nyugodtan add hozzá ezt a válaszomhoz, ez velem teljesen rendben van.
  • Gondolom, megértési módjaink ebben a tekintetben csak mások. Valójában csak az utam átírása után értettem meg teljesen a ciklikus permutációt (de aztán közvetlenül, az utána végzett kétlépéses lebontás nélkül). A lényeg az, hogy így azonnal láthatja, hogy mely index változik, ha az egyes listák mentén halad, és meghatározhatja, hogy a szám struktúrája honnan származik, anélkül, hogy megnézné az eredeti listát. Más szavakkal: közvetlenebben (legalábbis számomra) feltárja az átalakulás szerkezetét et.
  • @celtschk Igen, megértettem az érvelését. De számomra könnyebb volt megérteni, hogy mely elemek milyen listákat ugrottak meg, ahelyett, hogy elemeztem volna az elemeket ‘. Lehet, hogy ‘ csak én vagyok, mindig inkább a geometriára, mint az algebra-ra gravitáltam. Azt hiszem, mindkét oldalnak megvan az előnye.
  • @LeonidShifrin A 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}} esetében azt mondod, hogy mi történik, az az elem, amely az eredeti listában az 1. szintet alkotta, most alkotóelem 2. szint az eredményben. Nem ‘ nem értem, a bemenetnek és a kimenetnek ugyanaz a szintstruktúrája, az elemek továbbra is ugyanazon a szinten vannak. Meg tudnád magyarázni ezt általában?

Válasz

A Flatten cím második argumentuma kettőt szolgál célokra. Először meghatározza az indexek iterációjának sorrendjét az elemek összegyűjtésekor. Másodszor a végeredményben a lista laposodását írja le. Nézzük soronként ezeket a képességeket.

Iterációs sorrend

Vegye figyelembe a következő mátrixot:

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

mátrix eredménye

Használhatjuk Table kifejezés a mátrix másolatának létrehozására az összes elemének ismétlésével:

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

Ez az identitás a művelet nem érdekes, de a tömböt átalakíthatjuk az iterációs változók sorrendjének felcserélésével. Például felcserélhetjük a i és a j iterátorok. Ez az 1. és 2. szintű indexek és azok megfelelő elemeinek felcserélését jelenti:

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

mátrix eredmény

Ha alaposan megnézzük, láthatjuk, hogy minden egyes eredeti elem, $m[[i, j, k]], megfelel a kapott elemnek: $r[[j, i, k]] – az első két index méh n “cserélve”.

Flatten lehetővé teszi számunkra, hogy ezzel az Table kifejezéssel egyenértékűbb műveletet fejezzünk ki tömörebben:

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

A Flatten kifejezés második argumentuma kifejezetten meghatározza a kívánt indexrendet: az 1, 2, 3 indexek 2, 1, 3 indexgé változik. Vegye figyelembe, hogy miért nem kellett tartományt megadnunk a tömb minden dimenziójához – ez jelentős jelölési kényelem.

A következő Flatten egy identitásművelet, mivel nem határoz meg változásokat az index sorrendjében:

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

Míg a következő kifejezés mindhárom indexet átrendezi: 1, 2 , 3 -> 3, 2, 1

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

mátrix eredménye

Ismét , ellenőrizhetjük, hogy az [[i, j, k]] indexnél található eredeti elem megtalálható-e az eredményben a [[k, j, i]] címen.

Ha valamely index elhagyásra kerül egy Flatten kifejezés, úgy kezeljük őket, mintha utoljára és természetes sorrendjükben lett volna megadva:

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

Ez az utolsó példa még rövidítve:

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

Egy üres indexlista eredményezi az identitásműveletet:

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

Ez gondoskodik az iterációs sorrendről és az indexcseréről. Most nézzük meg …

Lista lapozás

Elképzelhető, hogy miért kellett az előző példákban megadnunk az egyes indexeket egy allistában. Ennek oka az, hogy az index-specifikáció minden egyes allistája meghatározza, hogy mely indexeket kell lapítani az eredménybe. Vizsgáljuk meg újra a következő azonossági műveletet:

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

mátrix eredmény

Mi történik, ha az első két indexet egyesítjük ugyanabban az allistában ?

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

mátrix eredménye

Láthatjuk, hogy az eredeti eredmény egy 4 x 3 pár rács, de a második eredmény a párok egyszerű felsorolása. A legmélyebb struktúra, a párok érintetlenül maradtak. Az első két szintet egy szintre simítottuk. A forrás harmadik szintjén lévő párok a mátrix lapítatlan maradt.

Helyettesíthetjük a második két indexet:

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

mátrix eredmény

Ennek az eredménynek ugyanannyi sora van, mint az eredeti mátrixnak, vagyis az első szintet érintetlenül hagyták. De mindegyik eredménysornak hat elemből álló lapos listája van, amely a megfelelő eredeti három pár sorból származik. Így az alsó két szintet ellapítottuk.

Mindhárom indexet kombinálhatjuk is, hogy teljesen lapos eredményt kapjunk:

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

mátrix eredménye

Ez rövidíthető:

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

rövidítés jelölést is kínál, ha nem történik indexcsere:

$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” tömbök

Az eddigi összes példa különböző dimenziójú mátrixokat használt. A Flatten egy nagyon hatékony funkciót kínál, amely nem csupán egy Table kifejezés rövidítését jelenti. A Flatten kecsesen fogja kezelni azt az esetet, amikor az adott szintű allisták hossza eltérő. A hiányzó elemeket csendben figyelmen kívül hagyjuk. Például egy háromszög alakú tömb átfordítható:

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

…vagy lapított és lapított:

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

Megjegyzések

  • Ez egy fantasztikus és alapos magyarázat!
  • @ rm-rf Köszönöm. Úgy gondolom, hogy ha a Flatten általánosítanák, hogy elfogadják az indexek ellapításakor alkalmazandó függvényt, az kiváló kezdet lenne a ” tenzor algebra egy dobozban “.
  • Néha belső összehúzódásokat kell végrehajtanunk. Most már tudom, hogy meg tudom csinálni a Flatten[$m, {{1}, {2, 3}}] használatával a Map Flatten helyett valamilyen szinten. Jó lenne, ha Flatten negatív érveket fogadna el ehhez. Tehát ezt az esetet úgy írhatjuk, hogy Flatten[$m, -2].
  • Miért kapott ez a kiváló válasz kevesebb szavazatot, mint Leonid ‘ s: (.
  • @Tangshutao Lásd a második GYIK-ot a profilomon .

Válasz

Sokat tanultam a WReach és Leonid válaszaiból, és szeretnék egy kis hozzájárulást adni:

Úgy tűnik, érdemes hangsúlyozva, hogy a Flatten listaértékű második argumentumának elsődleges célja csupán a listák bizonyos szintjeinek ellaposítása (ahogy a WReach megemlíti a Lista lapozás szakasz). A Flatten rongyos Transpose használata oldalnak tűnik véleményem szerint ennek az elsődleges tervezésnek a hatása.

Például tegnap át kellett alakítanom ezt a listát

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

treeform1

ebbe:

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

faform2

Vagyis össze kellett törnöm a 2. és a 3. listaszintet.

A

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

Válasz

Ez egy régi kérdés, de gyakran feltesz egy tétel emberek. Ma, amikor megpróbáltam elmagyarázni, hogyan működik ez, egy egészen világos magyarázatra bukkantam, ezért úgy gondolom, hogy a megosztása itt hasznos lehet a további közönség számára.

Mit jelent az index?

Először tegyük világossá, mi a index : A Mathematicában minden kifejezés egy fa, például nézzük meg listán:

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

illus1

Hogyan navigálsz egy fán?

Egyszerű! A gyökérből indul ki, és minden átkelésnél kiválaszthatja, hogy melyik irányba haladjon, például itt, ha el akarja érni a 2 fájlt, akkor kezdje el a

első elérési út, majd válassza a második utat. Írjuk ki “s”{1,2}néven, amely csak a2elem indexe ebben a kifejezésben.

Hogyan lehet megérteni a Flatten?

Itt vegyünk fontolóra egy egyszerű kérdést, ha nem adok neked teljes kifejezést, de ehelyett megadom neked az összes elemet és indexeik, hogyan szerkeszted az eredeti kifejezést? Például itt megadom neked:

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

és mondom, hogy minden fej List, tehát mi van? az eredeti kifejezés?

Nos, biztosan rekonstruálhatja az eredeti kifejezést {{1,2},{3,4}} néven, de hogyan? Valószínűleg felsorolhatja a következő lépéseket:

  1. Először az index első elemét nézzük meg, rendezzük és összegyűjtjük. Ezután tudjuk, hogy a első a teljes kifejezés elemének tartalmaznia kell az első két elemet az eredeti listában …
  2. Ezután folytatjuk a második argumentum vizsgálatát, tegyük ugyanezt …
  3. Végül az eredeti listát {{1,2},{3,4}} néven kapjuk meg.

Nos, ez ésszerű! Szóval mi van, ha azt mondom, hogy nem, akkor először az index második eleme szerint kell rendezni és összegyűjteni, majd az index első eleme alapján összegyűjteni? Vagy azt mondom, hogy nem gyűjtjük őket kétszer, csak mindkét elem szerint válogatunk, de az első argumentumnak nagyobb prioritást adunk?

Nos, valószínűleg a következő két listát kapnád, igaz?

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

Nos, ellenőrizze egyedül, Flatten[{{1,2},{3,4}},{{2},{1}}] és Flatten[{{1,2},{3,4}},{{1,2}}] tegye ugyanezt!

Szóval, hogy érted a Flatten második argumentumát ?

  1. A fő lista belsejében található minden listaelem, például {1,2}, azt jelenti, hogy GATHER az összes lista az index ezen elemei szerint, más szóval ezek az szintek .
  2. A listaelemen belüli sorrend azt ábrázolja, hogyan SORT rendezi az előző lépésben a listán belül összegyűjtött elemeket. . például a {2,1} azt jelenti, hogy a második szint pozíciójának elsőbbsége van, mint az első szint pozíciójának.

Példák

Most legyen némi gyakorlata, hogy ismerje a korábbi szabályokat.

1. Transpose

A Transpose egy egyszerű m * n mátrixon $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $ értéket kap. De tekinthetjük más módon is, eredetileg a elemet először i indexük alapján, majd j indexük szerint rendezzük, most csak annyit kell tennünk, hogy változtatnunk kell a j indexeljen először, majd i következővel! Tehát a kód:

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

Egyszerű, igaz?

2. Hagyományos Flatten

A hagyományos lapítás célja egy egyszerű m * n mátrixon: hozzon létre 1D tömböt 2D mátrix helyett, például: Flatten[{{1,2},{3,4}}] visszatér {1,2,3,4}. Ez azt jelenti, hogy ezúttal nem “t gyűjtünk elemeket, csak rendezze őket, először az első indexük, majd a második szerint:

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

3. ArrayFlatten

Beszéljük meg az ArrayFlatten legegyszerűbb esetét, itt van egy 4D-s lista:

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

tehát hogyan lehet egy ilyen átalakítást 2D listává tenni?

$ \ left (\ begin {array} {cc} \ left (\ begin {array} {cc} 1 & 2 \\ 5 & 6 \\ \ end {tömb} \ jobb) & \ balra (\ begin {array} {cc} 3 & 4 \\ 7 & 8 \\ end {array} \ right) \\ \ left (\ begin {array} {cc} 9 & 10 \\ 13 & 14 \\ \ end {tömb} \ jobb) & \ left (\ begin {array} {cc} 11 & 12 \\ 15 & 16 \\ \ end {array} \ right) \\ \ end {tömb} \ right) \ rightarrow \ left (\ begin {tömb} {cccc} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \\ 13 & 14 & 15 & 16 \\ \ end {array} \ right) $

Nos, ez is egyszerű, először az eredeti első és a harmadik szintű index alapján kell a csoportot megadni, és az első indexnek nagyobb prioritást kell adnunk válogató. Ugyanez vonatkozik a második és a negyedik szintre is:

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

4. Kép átméretezése

Most van egy képünk, például:

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

De határozottan túl kicsi ahhoz, hogy nézetet, ezért nagyobbá akarjuk tenni az egyes pixelek 10 * 10-es méretű hatalmas pixelekké való kiterjesztését.

Először megpróbáljuk:

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

De egy 4D-s mátrixot ad vissza, amelynek dimenziói: {10,10,10,10}. Ezért Flatten meg kell adnunk. Ezúttal azt akarjuk, hogy a harmadik argumentum magasabb prioritást élvezzen az elsőből, így egy kisebb hangolás működne:

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

Összehasonlítás:

illus2

Remélem, hogy ez segíthet!

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük