Jedną rzeczą, której nigdy nie mógłbym omówić, jest to, jak Flatten
działa, gdy z macierzą jako drugim argumentem, a pomoc Mathematica nie jest szczególnie dobra w tym przypadku.
Zaczerpnięte z Flatten
Mathematica dokumentacja:
Flatten[list, {{s11, s12, ...}, {s21, s22, ...}, ...}]
Spłaszcza
list
, łącząc wszystkie poziomy $ s_ {ij} $, tak aby każdy poziom był $ i $ w wyniku.
Czy ktoś mógłby wyjaśnić, co to właściwie oznacza / robi?
Odpowiedź
Jedna wygodny sposób myślenia o Flatten
z drugim argumentem jest taki, że wykonuje on coś w rodzaju Transpose
dla poszarpanych (nieregularnych) list. Oto prosty przykład:
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 się dzieje, gdy elementy tworzą Poziomy 1
na pierwotnej liście są teraz składnikami na poziomie 2
w wyniku i na odwrót. Dokładnie to robi Transpose
, ale robi się to w przypadku nieregularnych list. Zwróć jednak uwagę, że niektóre informacje o pozycjach są tutaj utracone, więc nie możemy bezpośrednio odwrócić operacji:
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}}
Aby to poprawnie odwrócić, musielibyśmy aby zrobić coś takiego:
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}}
Bardziej interesującym przykładem jest głębsze zagnieżdżenie:
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}}}
Tutaj znowu widzimy, że Flatten
skutecznie działało jak (uogólnione) Transpose
, zamieniając elementy na pierwszych 2 poziomach . Trudniej będzie zrozumieć następujące kwestie:
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}}}
Poniższy obraz ilustruje tę uogólnioną transpozycję:
Możemy to zrobić w dwóch kolejnych krokach:
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}}}
Ponieważ permutacja {3,1,2}
można uzyskać jako {1,3,2}
, a następnie {2,1,3}
. Innym sposobem sprawdzenia, jak to działa, jest użyj liczb wh wskazują pozycję w strukturze listy:
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 tego widać, że na najbardziej zewnętrznej liście (pierwszy poziom) trzeci indeks (odpowiadający trzeci poziom oryginalnej listy), na każdej liście członków (drugi poziom) pierwszy element rośnie o element (odpowiadający pierwszemu poziomowi oryginalnej listy), a na końcu na najbardziej wewnętrznych (trzecim poziomie) listach rośnie drugi indeks odpowiadający drugiemu poziomowi na pierwotnej liście. Generalnie, jeśli k-ty element listy przekazany jako drugi element to {n}
, zwiększenie k-tego indeksu w wynikowej strukturze listy odpowiada zwiększeniu n-tego indeksu w Oryginalna struktura.
Wreszcie można połączyć kilka poziomów, aby skutecznie spłaszczyć podpoziomy, na przykład:
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}}
Komentarze
Odpowiedź
Drugi argument listy do Flatten
służy dwóm cele. Po pierwsze, określa kolejność, w jakiej indeksy będą iterowane podczas gromadzenia elementów. Po drugie, opisuje spłaszczanie listy w wyniku końcowym. Przyjrzyjmy się po kolei każdej z tych możliwości.
Kolejność iteracji
Rozważmy następującą macierz:
$m = Array[Subscript[m, Row[{##}]]&, {4, 3, 2}]; $m // MatrixForm
Możemy użyć wyrażenie Table
w celu utworzenia kopii macierzy poprzez iterację po wszystkich jej elementach:
$m === Table[$m[[i, j, k]], {i, 1, 4}, {j, 1, 3}, {k, 1, 2}] (* True *)
Ta tożsamość operacja nie jest interesująca, ale możemy przekształcić tablicę, zamieniając kolejność zmiennych iteracji. Na przykład możemy zamienić i
i j
iteratory. Sprowadza się to do zamiany indeksów poziomu 1 i 2 oraz odpowiadających im elementów:
$r = Table[$m[[i, j, k]], {j, 1, 3}, {i, 1, 4}, {k, 1, 2}]; $r // MatrixForm
Jeśli przyjrzymy się uważnie, zobaczymy, że każdy oryginalny element $m[[i, j, k]]
będzie odpowiadał wynikowemu elementowi $r[[j, i, k]]
– pierwsze dwa indeksy mają pszczoły n „swapped”.
Flatten
pozwala nam wyrazić równoważną operację do tego Table
wyrażenia bardziej efektywnie:
$r === Flatten[$m, {{2}, {1}, {3}}] (* True *)
Drugi argument wyrażenia Flatten
wyraźnie określa żądaną kolejność indeksów: indeksy 1, 2, 3 są zmienione, aby stały się indeksami 2, 1, 3. Zwróć uwagę, że nie musieliśmy określać zakresu dla każdego wymiaru tablicy – znaczna wygoda notacji.
Poniżej Flatten
jest operacją tożsamości, ponieważ nie określa żadnej zmiany kolejności indeksów:
$m === Flatten[$m, {{1}, {2}, {3}}] (* True *)
Podczas gdy poniższe wyrażenie zmienia kolejność wszystkich trzech indeksów: 1, 2 , 3 -> 3, 2, 1
Flatten[$m, {{3}, {2}, {1}}] // MatrixForm
Ponownie , możemy sprawdzić, czy oryginalny element znaleziony pod indeksem [[i, j, k]]
zostanie teraz znaleziony w wyniku [[k, j, i]]
.
Jeśli jakiekolwiek indeksy zostaną pominięte w Flatten
, są traktowane tak, jakby zostały określone jako ostatnie i w ich naturalnej kolejności:
Flatten[$m, {{3}}] === Flatten[$m, {{3}, {1}, {2}}] (* True *)
Ten ostatni przykład może należy jeszcze bardziej skrócić:
Flatten[$m, {3}] === Flatten[$m, {{3}}] (* True *)
Pusta lista indeksów skutkuje operacją tożsamości:
$m === Flatten[$m, {}] === Flatten[$m, {1}] === Flatten[$m, {{1}, {2}, {3}}] (* True *)
To zajmuje się kolejnością iteracji i zamianą indeksów. Teraz spójrzmy na …
Spłaszczanie listy
Można się zastanawiać, dlaczego musieliśmy określić każdy indeks w podliście w poprzednich przykładach. Powodem jest to, że każda podlista w specyfikacji indeksu określa, które indeksy mają być spłaszczone razem w wyniku. Rozważmy ponownie następującą operację tożsamości: >
Flatten[$m, {{1}, {2}, {3}}] // MatrixForm
Co się stanie, jeśli połączymy pierwsze dwa indeksy w tę samą podlistę ?
Flatten[$m, {{1, 2}, {3}}] // MatrixForm
Widzimy, że pierwotny wynik był Siatka 4 x 3 par, ale drugim wynikiem jest prosta lista par. Najgłębsza struktura, pary, pozostały nietknięte. Pierwsze dwa poziomy zostały spłaszczone w jeden poziom. Pary na trzecim poziomie źródła macierz pozostała niespłaszczona.
Zamiast tego moglibyśmy połączyć dwa drugie indeksy:
Flatten[$m, {{1}, {2, 3}}] // MatrixForm
Ten wynik ma taką samą liczbę wierszy jak oryginalna macierz, co oznacza, że pierwszy poziom pozostał nietknięty. Ale każdy wiersz wyników ma płaską listę sześciu elementów pobranych z odpowiedniego oryginalnego wiersza trzech par. W ten sposób dwa dolne poziomy zostały spłaszczone.
Możemy również połączyć wszystkie trzy wskaźniki, aby uzyskać całkowicie spłaszczony wynik:
Flatten[$m, {{1, 2, 3}}]
Można to w skrócie:
Flatten[$m, {{1, 2, 3}}] === Flatten[$m, {1, 2, 3}] === Flatten[$m] (* True *)
Flatten
oferuje również skróconą notację, gdy nie ma mieć miejsca zamiana indeksów:
$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 *)
Tablice „poszarpane”
Wszystkie dotychczasowe przykłady wykorzystywały macierze o różnych wymiarach. Flatten
oferuje bardzo rozbudowaną funkcję, dzięki której jest czymś więcej niż tylko skrótem dla wyrażenia Table
. Flatten
z wdziękiem poradzi sobie z przypadkiem, gdy podlisty na dowolnym poziomie mają różne długości. Brakujące elementy zostaną po cichu zignorowane. Na przykład tablicę trójkątną można odwrócić:
$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 *)
…lub odwrócone i spłaszczone:
Flatten[$t, {{2, 1}}] (* {1,2,3,4,5,4,6,8,10,9,12,15,16,20,25} *)
Komentarze
- To fantastyczne i dokładne wyjaśnienie!
- @ rm-rf Dzięki. Wydaje mi się, że jeśli
Flatten
zostały uogólnione tak, aby akceptowały funkcję do zastosowania przy spłaszczaniu (kurczeniu) indeksów, byłby to doskonały początek ” algebra tensora w puszce „. - Czasami musimy wykonać skurcze wewnętrzne. Teraz wiem, że mogę to zrobić za pomocą
Flatten[$m, {{1}, {2, 3}}]
zamiast Map Flatten na jakimś poziomie. Byłoby miło, gdybyFlatten
zaakceptował negatywne argumenty, aby to zrobić. Więc ten przypadek może wyglądać następująco:Flatten[$m, -2]
. - Dlaczego ta doskonała odpowiedź otrzymała mniej głosów niż Leonid ' s: (.
- @Tangshutao Zobacz drugie często zadawane pytania dotyczące mojego profilu .
Odpowiedź
Wiele się nauczyłem z odpowiedzi WReach „i Leonida” i chciałbym wnieść niewielki wkład:
Wydaje się, że warto podkreślając, że głównym celem drugiego argumentu z wartością listy w Flatten
jest jedynie spłaszczenie pewnych poziomów list (jak wspomina WReach w swoim Spłaszczanie listy sekcja). Używanie Flatten
jako poszarpanego Transpose
wydaje się być bokiem -efekt tego pierwotnego projektu, moim zdaniem.
Na przykład wczoraj musiałem przekształcić tę listę
lists = { {{{1, 0}, {1, 1}}, {{2, 0}, {2, 4}}, {{3, 0}}}, {{{1, 2}, {1, 3}}, {{2, Sqrt[2]}}, {{3, 4}}} (*, more lists... *) };
do tego:
list2 = { {{1, 0}, {1, 1}, {2, 0}, {2, 4}, {3, 0}}, {{1, 2}, {1, 3}, {2, Sqrt[2]}, {3, 4}} (*, more lists... *) }
To znaczy, musiałem zmiażdżyć razem drugi i trzeci poziom listy.
Zrobiłem to z
list2 = Flatten[lists, {{1}, {2, 3}}];
Odpowiedź
To stare pytanie, ale często zadawane przez partię ludzi. Dzisiaj, kiedy próbowałem wyjaśnić, jak to działa, znalazłem dość jasne wyjaśnienie, więc myślę, że udostępnienie go tutaj byłoby pomocne dla dalszych odbiorców.
Co oznacza indeks?
Najpierw wyjaśnijmy, czym jest indeks : W programie Mathematica każde wyrażenie jest na przykład drzewem na liście:
TreeForm@{{1,2},{3,4}}
Jak poruszasz się po drzewie?
Proste! Zaczynasz od korzenia i przy każdym skrzyżowaniu wybierasz, którą drogą iść, na przykład tutaj, jeśli chcesz dotrzeć do 2
, zaczynasz od wybrania pierwsza ścieżka, a następnie wybierz ścieżkę drugą . Zapiszmy to jako {1,2}
, który jest właśnie indeksem elementu 2
w tym wyrażeniu.
Jak rozumieć Flatten
?
W tym miejscu rozważ proste pytanie, jeśli nie podam pełnego wyrażenia, ale zamiast tego podaję wszystkie elementy i ich indeksy, jak konstruujesz oryginalne wyrażenie? Na przykład, tutaj daję ci:
{<|"index" -> {1, 1}, "value" -> 1|>, <|"index" -> {1, 2}, "value" -> 2|>, <|"index" -> {2, 1}, "value" -> 3|>, <|"index" -> {2, 2}, "value" -> 4|>}
i mówię, że wszystkie głowy są List
, więc co z tego oryginalne wyrażenie?
Cóż, z pewnością możesz zrekonstruować oryginalne wyrażenie jako {{1,2},{3,4}}
, ale jak? Prawdopodobnie możesz wymienić następujące kroki:
- Najpierw patrzymy na pierwszy element indeksu, sortujemy i zbieramy według niego. Wtedy wiemy, że pierwszy element całego wyrażenia powinien zawierać pierwsze dwa elementy z oryginalnej listy …
- Następnie kontynuujemy przyglądanie się drugiemu argumentowi, robimy to samo …
- Na koniec otrzymujemy oryginalną listę jako
{{1,2},{3,4}}
.
Cóż, to rozsądne! A co, jeśli powiem ci, że nie, powinieneś najpierw posortować i zebrać według drugiego elementu indeksu, a następnie zebrać według pierwszego elementu indeksu? Albo mówię, że nie zbieramy ich dwa razy, po prostu sortujemy według obu elementów, ale nadajemy pierwszemu argumentowi wyższy priorytet?
Cóż, prawdopodobnie dostaniesz odpowiednio dwie poniższe listy, prawda?
-
{{1,3},{2,4}}
-
{1,2,3,4}
Cóż, sprawdź sam, Flatten[{{1,2},{3,4}},{{2},{1}}]
i Flatten[{{1,2},{3,4}},{{1,2}}]
zrób to samo!
A więc, jak rozumiesz drugi argument funkcji Spłaszcz ?
- Każdy element listy na liście głównej, na przykład
{1,2}
, oznacza, że powinieneś ZBIERZ wszystkie listy według tych elementów w indeksie, innymi słowy te poziomy . - Kolejność wewnątrz elementu listy reprezentuje sposób SORTOWANIA elementów zgromadzonych na liście w poprzednim kroku . na przykład
{2,1}
oznacza, że pozycja na drugim poziomie ma wyższy priorytet niż pozycja na pierwszym poziomie.
Przykłady
Teraz przećwiczmy zapoznanie się z poprzednimi regułami.
1. Transpose
Cel Transpose
na prostej macierzy m * n ma stworzyć $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Ale możemy to rozważyć w inny sposób, pierwotnie sortujemy element według ich indeksu i
, a następnie posortuj je według indeksu j
, teraz wszystko, co musimy zrobić, to zmienić, aby posortować je według j
najpierw indeksuj, a potem i
dalej! Tak więc kod staje się:
Flatten[mat,{{2},{1}}]
Proste, prawda?
2. Tradycyjne Flatten
Celem tradycyjnego spłaszczania na prostej macierzy m * n jest utwórz tablicę 1D zamiast macierzy 2D, na przykład: Flatten[{{1,2},{3,4}}]
zwraca {1,2,3,4}
. Oznacza to, że tym razem nie „t zbieramy elementy, a jedynie sortuj je, najpierw według pierwszego indeksu, a następnie według drugiego:
Flatten[mat,{{1,2}}]
3. ArrayFlatten
Omówmy najprostszy przypadek ArrayFlatten, tutaj mamy listę 4D:
{{{{1,2},{5,6}},{{3,4},{7,8}}},{{{9,10},{13,14}},{{11,12},{15,16}}}}
więc jak możemy wykonać taką konwersję, żeby była to lista 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) $
Cóż, to też jest proste, potrzebujemy najpierw grupy według oryginału i indeksu trzeciego poziomu, a pierwszemu indeksowi powinniśmy nadać wyższy priorytet w sortowanie. To samo dotyczy drugiego i czwartego poziomu:
Flatten[mat,{{1,3},{2,4}}]
4. „Zmień rozmiar” obrazu
Teraz mamy obraz, na przykład:
img=Image@RandomReal[1,{10,10}]
Ale jest on zdecydowanie za mały, abyśmy widoku, więc chcemy go powiększyć, rozszerzając każdy piksel do dużego piksela o rozmiarze 10 * 10.
Najpierw spróbujemy:
ConstantArray[ImageData@img,{10,10}]
Ale zwraca macierz 4D z wymiarami {10,10,10,10}. Powinniśmy więc ją Flatten
. Tym razem chcemy, aby trzeci argument miał wyższy priorytet pierwszego, więc drobne dostrojenie będzie działać:
Image@Flatten[ConstantArray[ImageData@img,{10,10}],{{3,1},{4,2}}]
Porównanie:
Mam nadzieję, że to pomoże!
Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}}
, a wynik miałby postać{{{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}}
, mówisz, że to, co się dzieje, to te elementy, które stanowiły poziom 1 na pierwotnej liście, są teraz składnikami poziom 2 w wyniku. Nie ' całkiem rozumiem, dane wejściowe i wyjściowe mają tę samą strukturę poziomu, elementy są nadal na tym samym poziomie. Czy możesz to ogólnie wyjaśnić?