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ę:

Ilustracja cyklicznej transpozycji uogólnionej

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

  • Myślę, że ' jest łatwiejsze do zrozumienia, jeśli liczby wskazują pierwotną pozycję na zagnieżdżonej liście. Na przykład w przykładzie permutacji z trzema liczbami byłoby to 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}}}.
  • @celtschk Dzięki, ale nie jestem przekonany. Dla mnie osobiście łatwiej jest śledzić wizualnie różne liczby i zobaczyć, gdzie trafiają w nowej strukturze. Ale nie krępuj się, dodając to do mojej odpowiedzi, nie mam nic przeciwko.
  • Myślę, że nasze sposoby rozumienia są po prostu inne pod tym względem. Właściwie dopiero po przepisaniu mojej drogi całkowicie zrozumiałem cykliczną permutację (ale potem bezpośrednio, bez dwuetapowego rozkładu, który zrobiłeś później). Chodzi o to, że w ten sposób możesz natychmiast zobaczyć, który indeks się zmieni, jeśli przejdziesz wzdłuż każdej listy, i możesz określić, skąd pochodzi numer w strukturze listy, nawet nie patrząc na oryginalną listę. Innymi słowy, bardziej bezpośrednio (przynajmniej dla mnie) ujawnia strukturę transformacji.
  • @celtschk Tak, zrozumiałem twoje rozumowanie. Ale dla mnie łatwiej było zrozumieć, które elementy przeskoczyły na które listy, zamiast patrzeć na indeksy elementów '. Może to ' to tylko ja, zawsze kierowałem się geometrią, a nie algebrą. Myślę, że oba sposoby mają swoje zalety.
  • @LeonidShifrin Dla 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ć?

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 

wynik macierzy

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 

wynik macierzy

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 

wynik macierzy

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 

wynik macierzy

Co się stanie, jeśli połączymy pierwsze dwa indeksy w tę samą podlistę ?

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

wynik macierzy

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 

wynik macierzy

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

wynik macierzy

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, gdyby Flatten 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... *) }; 

treeform1

do tego:

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

treeform2

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

illus1

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:

  1. 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 …
  2. Następnie kontynuujemy przyglądanie się drugiemu argumentowi, robimy to samo …
  3. 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. {{1,3},{2,4}}
  2. {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 ?

  1. 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 .
  2. 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:

illus2

Mam nadzieję, że to pomoże!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *