Jedna věc, kterou bych si nikdy nemohl zabalit, je způsob, jakým Flatten funguje, když opatřena maticí jako druhým argumentem a Mathematica nápověda v tomto případě není zvlášť dobrá.

Převzato z Flatten dokumentace Mathematica :

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

Sloučí list kombinací všech úrovní $ s_ {ij} $, aby každá úroveň $ i $ byla ve výsledku.

Mohl by někdo vysvětlit, co to vlastně znamená / dělá?

Odpovědět

Jeden pohodlný způsob uvažování o Flatten s druhým argumentem je, že provádí něco jako Transpose pro otrhané (nepravidelné) seznamy. Zde je jednoduchý příklad:

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

Co se stane, jsou prvky, které tvoří uted level 1 v původním seznamu jsou nyní ve výsledku součástí na úrovni 2 a naopak. To je přesně to, co Transpose dělá, ale pro nepravidelné seznamy. Všimněte si však, že zde jsou ztraceny některé informace o pozicích, takže nemůžeme operaci přímo obrátit:

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

Abychom ji mohli správně obrátit, měli bychom udělat něco takového:

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

Zajímavějším příkladem je, když máme hlubší vnoření:

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

Tady opět vidíme, že Flatten účinně fungoval jako (zobecněný) Transpose, který na prvních 2 úrovních zaměňoval jednotlivé části . Následující bude těžší pochopit:

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

Následující obrázek ilustruje toto zobecněné provedení:

Ilustrace cyklické generalizované transpozice

Můžeme to udělat ve dvou po sobě následujících krocích:

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

Protože permutace {3,1,2} lze získat jako {1,3,2} následovaný {2,1,3}. Dalším způsobem, jak zjistit, jak to funguje, je používejte čísla označují pozici ve struktuře seznamu:

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

Z toho je vidět, že v nejvzdálenějším seznamu (první úroveň) je třetí index (odpovídající třetí úroveň původního seznamu) roste, v každém seznamu členů (druhá úroveň) roste první prvek na prvek (odpovídá první úrovni původního seznamu) a nakonec v nejvnitřnějších seznamech (třetí úroveň) roste druhý index , což odpovídá druhé úrovni v původním seznamu. Obecně platí, že pokud k-tý prvek seznamu předávaného jako druhý prvek je {n}, růst k-tého indexu ve výsledné struktuře seznamu odpovídá zvýšení n-tého indexu v původní struktura.

Nakonec lze zkombinovat několik úrovní a efektivně tak vyrovnat podúrovně, například:

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

Komentáře

  • Myslím, že je ‚ srozumitelnější, pokud čísla označují původní pozici ve vnořeném seznamu. Například v příkladu permutace se třemi čísly by to bylo Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}} a výsledek by četl {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}.
  • @celtschk Díky, ale nejsem přesvědčen. Pro mě osobně je snazší sledovat vizuálně odlišná čísla a zjistit, kde v nové struktuře skončí. Ale neváhejte a přidejte to k mé odpovědi, to je se mnou naprosto v pořádku.
  • Myslím, že naše způsoby porozumění jsou v tomto ohledu odlišné. Vlastně až po přepsání mé cesty jsem úplně pochopil cyklickou permutaci (ale pak přímo, bez dvoustupňového rozkladu, který jste provedli později). Jde o to, že tímto způsobem můžete okamžitě zjistit, který index se změní, pokud se budete pohybovat po každém seznamu, a můžete určit, odkud číslo pochází ve struktuře seznamu, aniž byste se museli podívat na původní seznam. Jinými slovy, to přímo (alespoň pro mě) odhaluje strukturu transformace.
  • @celtschk Ano, rozuměl jsem vašemu uvažování. Ale pro mě bylo snazší pochopit, které prvky přeskočily, které seznamy, než se dívat na indexy prvků ‚. Může to být ‚ jen se mnou, vždy jsem tíhl spíše k geometrii než k algebře. Myslím, že oba způsoby mají své opodstatnění.
  • @LeonidShifrin Pro 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}} říkáte, co se stane, že prvky, které v původním seznamu představovaly úroveň 1, jsou nyní složkami na úroveň 2 ve výsledku. Nerozumím tomu ‚, vstup a výstup mají stejnou strukturu úrovní, prvky jsou stále na stejné úrovni. Mohl byste to vysvětlit?

Odpověď

Druhý argument seznamu Flatten slouží dvěma účely. Nejprve určuje pořadí, ve kterém budou indexy iterovány při shromažďování prvků. Zadruhé popisuje zploštění seznamu v konečném výsledku. Podívejme se postupně na každou z těchto funkcí.

Iterační řád

Zvažte následující matici:

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

výsledek matice

Můžeme použít a Table výraz pro vytvoření kopie matice iterací přes všechny její prvky:

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

Tato identita operace je nezajímavá, ale pole můžeme transformovat záměnou pořadí iteračních proměnných. Například můžeme vyměnit i a j iterátory. To znamená výměnu indexů úrovně 1 a úrovně 2 a jejich odpovídajících prvků:

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

výsledek matice

Pokud se podíváme pozorně, zjistíme, že každý původní prvek $m[[i, j, k]] bude odpovídat výslednému prvku $r[[j, i, k]] – první dva indexy mají včelu n „vyměněno“.

Flatten nám umožňuje expresněji vyjádřit ekvivalentní operaci s tímto Table výrazem:

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

Druhý argument výrazu Flatten výslovně specifikuje požadované pořadí indexů: indexy 1, 2, 3 jsou změněno na indexy 2, 1, 3. Všimněte si, jak jsme nemuseli specifikovat rozsah pro každou dimenzi pole – významná notační výhoda.

Následující Flatten je operace identity, protože neurčuje žádnou změnu pořadí indexů:

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

Zatímco následující výraz přeuspořádá všechny tři indexy: 1, 2 , 3 -> 3, 2, 1

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

výsledek matice

Znovu , můžeme ověřit, že původní prvek nalezený v indexu [[i, j, k]] bude nyní nalezen v [[k, j, i]] ve výsledku.

Pokud jsou z indexu Flatten výraz, zachází se s nimi, jako by byly zadány naposledy a v jejich přirozeném pořadí:

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

Tento poslední příklad může zkrátit ještě dále:

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

Výsledkem operace prázdného indexu je operace identity:

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

To se postará o iterační pořadí a výměnu indexů. Nyní se podívejme na …

Sloučení seznamu

Někdo by se mohl divit, proč jsme museli v předchozích příkladech určit každý index v podřízeném seznamu. Důvodem je, že každý podřízený seznam ve specifikaci indexu určuje, které indexy mají být ve výsledku sloučeny dohromady. Zvažte znovu následující operaci identity:

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

maticový výsledek

Co se stane, když zkombinujeme první dva indexy do stejného podseznamu ?

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

maticový výsledek

Vidíme, že původní výsledek byl Mřížka párů 4 x 3, ale druhým výsledkem je jednoduchý seznam párů. Nejhlubší struktura, páry, zůstaly nedotčeny. První dvě úrovně byly sloučeny do jedné úrovně. Páry ve třetí úrovni zdroje matice zůstala nevyplněná.

Místo toho můžeme kombinovat druhé dva indexy:

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

výsledek matice

Tento výsledek má stejný počet řádků jako původní matice, což znamená, že první úroveň zůstala nedotčena. Ale každý řádek výsledků má plochý seznam šesti prvků převzatých z odpovídající původní řady tří párů. Dolní dvě úrovně tedy byly sloučeny.

Můžeme také kombinovat všechny tři indexy a získat tak úplně sloučený výsledek:

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

výsledek matice

Lze to zkrátit:

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

Flatten také nabízí zkratkovou notaci, pokud nedojde k žádné výměně indexu:

$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

Všechny příklady dosud používaly matice různých rozměrů. Flatten nabízí velmi výkonnou funkci, která z něj dělá víc než jen zkratku výrazu Table. Flatten elegantně vyřeší případ, kdy mají podlisty na kterékoli dané úrovni různé délky. Chybějící prvky budou tiše ignorovány. Lze například převrátit trojúhelníkové pole:

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

…nebo převrácené a zploštělé:

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

Komentáře

  • Toto je fantastické a důkladné vysvětlení!
  • @ rm-rf Díky. Domnívám se, že pokud by Flatten byly zobecněny, aby přijaly funkci, která se použije při sloučení (uzavírání smluv) indexů, byl by to skvělý začátek “ tenzorová algebra v plechovce „.
  • Někdy musíme provést vnitřní kontrakce. Nyní vím, že to můžu udělat pomocí Flatten[$m, {{1}, {2, 3}}] namísto Map Flatten na nějaké úrovni. Bylo by hezké, kdyby Flatten k tomu přijal negativní argumenty. Tento případ by tedy mohl vypadat jako Flatten[$m, -2].
  • Proč tato vynikající odpověď získala méně hlasů než Leonid ‚ s: (.
  • @Tangshutao Viz druhé nejčastější dotazy týkající se mého profilu .

Odpověď

Od odpovědí WReach a Leonida jsem se hodně naučil a rád bych malým příspěvkem:

Zdá se, že to stojí za to zdůraznění, že primárním záměrem druhého argumentu s hodnotou v seznamu Flatten je pouze vyrovnání určitých úrovní seznamů (jak uvádí WReach ve svém Sloučení seznamu ). Použití Flatten jako otrhané Transpose se jeví jako strana -efekt tohoto primárního designu, podle mého názoru.

Například včera jsem potřeboval tento seznam transformovat

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

treeform1

do tohoto:

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

treeform2

To znamená, že jsem potřeboval rozdrtit 2. a 3. úroveň seznamu společně.

Udělal jsem to pomocí

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

Odpověď

Toto je stará otázka, kterou však často žádá hodně lidí. Dnes, když jsem se pokoušel vysvětlit, jak to funguje, narazil jsem na zcela jasné vysvětlení, takže si myslím, že jeho sdílení zde by bylo užitečné pro další publikum.

Co znamená index?

Nejprve objasníme, co je index : V Mathematice je každý výraz strom, například se podívejme na seznamu:

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

illus1

Jak se pohybujete ve stromu?

Jednoduché! Začnete od kořene a při každém přechodu si vyberete, kterou cestou se vydat, například zde, pokud se chcete dostat 2, začnete výběrem první cesta, poté vyberte druhou cestu. Pojďme to napsat jako {1,2} což je pouze index prvku 2 v tomto výrazu.

Jak porozumět Flatten?

Zde zvažte jednoduchou otázku, pokud vám neposkytnu úplný výraz, ale místo toho vám poskytnu všechny prvky a jejich indexy, jak konstruujete původní výraz? Například vám zde dávám:

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

a řeknu vám, že všechny hlavy jsou List, tak co původní výraz?

No, určitě můžete původní výraz rekonstruovat jako {{1,2},{3,4}}, ale jak? Pravděpodobně můžete uvést následující kroky:

  1. Nejprve se podíváme na první prvek indexu a roztřídíme a shromáždíme jej. Poté víme, že první prvek celého výrazu by měl obsahovat první dva prvky v původním seznamu …
  2. Potom pokračujeme v pohledu na druhý argument, uděláme totéž …
  3. Nakonec dostaneme původní seznam jako {{1,2},{3,4}}.

No, to je rozumné! Co když vám tedy řeknu, že ne, měli byste nejprve seřadit a shromáždit podle druhého prvku indexu a poté se shromáždit podle prvního prvku indexu? Nebo říkám, že je neshromáždíme dvakrát, pouze seřaďte podle obou prvků, ale dáme prvnímu argumentu vyšší prioritu?

No, pravděpodobně byste dostali následující dva seznamy, že?

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

No, zkontrolujte sami, Flatten[{{1,2},{3,4}},{{2},{1}}] a Flatten[{{1,2},{3,4}},{{1,2}}] udělejte totéž!

Takže, jak rozumíte druhému argumentu Flattenova ?

  1. Každý prvek seznamu v hlavním seznamu, například {1,2}, znamená, že byste měli GATHER všechny seznamy podle těchto prvků v indexu, jinými slovy tyto úrovně .
  2. Pořadí uvnitř prvku seznamu představuje způsob, jakým SORT prvky shromážděné v seznamu v předchozím kroku . například {2,1} znamená, že pozice na druhé úrovni má vyšší prioritu než pozice na první úrovni.

Příklady

Nyní si osvojte praxi, jak se seznámit s předchozími pravidly.

1. Transpose

Cíl Transpose na jednoduché matici m * n je vytvořit $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Ale můžeme to považovat za jiný způsob, původně seřadíme prvek podle jejich i indexu a poté je seřaďte podle jejich j indexu, vše, co musíme udělat, je změnit a seřadit je podle j nejdříve index, poté i další! Kód se tedy stane:

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

Jednoduché, že?

2. Tradiční Flatten

Cílem tradičního zploštění na jednoduché matici m * n je vytvořte 1D pole namísto 2D matice, například: Flatten[{{1,2},{3,4}}] vrátí {1,2,3,4}. To znamená, že tentokrát nesbíráme prvky , pouze seřadit je, nejprve podle prvního indexu, potom podle druhého:

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

3. ArrayFlatten

Pojďme diskutovat o nejjednodušším případě ArrayFlatten, zde máme seznam 4D:

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

tak jak můžeme provést takovou konverzi, aby se z ní stal 2D seznam?

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

No, je to také jednoduché, nejprve potřebujeme skupinu podle původního prvního a třetího indexu a prvnímu indexu bychom měli dát vyšší prioritu třídění. Totéž platí pro druhou a čtvrtou úroveň:

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

4. „Změnit velikost“ obrázku

Nyní máme obrázek, například:

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

Ale je to rozhodně příliš malé na to, abychom zobrazení, takže ho chceme zvětšit rozšířením každého pixelu na obrovský pixel velikosti 10 * 10.

Nejprve zkusíme:

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

Ale vrací 4D matici s rozměry {10,10,10,10}. Měli bychom to tedy Flatten. Tentokrát chceme, aby místo toho měl vyšší prioritu třetí argument prvního, takže by drobné ladění fungovalo:

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

Srovnání:

illus2

Doufám, že by to mohlo pomoci!

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *