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í:
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
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
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
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
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
Co se stane, když zkombinujeme první dva indexy do stejného podseznamu ?
Flatten[$m, {{1, 2}, {3}}] // MatrixForm
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
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}}]
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é, kdybyFlatten
k tomu přijal negativní argumenty. Tento případ by tedy mohl vypadat jakoFlatten[$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... *) };
do tohoto:
list2 = { {{1, 0}, {1, 1}, {2, 0}, {2, 4}, {3, 0}}, {{1, 2}, {1, 3}, {2, Sqrt[2]}, {3, 4}} (*, more lists... *) }
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}}
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:
- 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 …
- Potom pokračujeme v pohledu na druhý argument, uděláme totéž …
- 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,3},{2,4}}
-
{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 ?
- 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ě . - 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í:
Doufám, že by to mohlo pomoci!
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}}}
.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?