Een ding dat ik nooit zou kunnen ronddraaien, is hoe Flatten
werkt wanneer voorzien van een matrix als het tweede argument, en de Mathematica -hulp is hier niet bijzonder goed in.
Genomen uit de Flatten
Mathematica documentatie:
Flatten[list, {{s11, s12, ...}, {s21, s22, ...}, ...}]
Vlakt
list
af door alle niveaus $ s_ {ij} $ te combineren om elk niveau $ i $ in het resultaat te maken.
Kan iemand uitweiden over wat dit feitelijk betekent / doet?
Antwoord
Een handige manier om Flatten
met het tweede argument te bedenken, is dat het iets als Transpose
uitvoert voor onoverzichtelijke (onregelmatige) lijsten. Hier is een eenvoudige voorbeeld:
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}}
Wat er gebeurt zijn de elementen die uted niveau 1
in de oorspronkelijke lijst zijn nu componenten op niveau 2
in het resultaat, en vice versa. Dit is precies wat Transpose
doet, maar dan gedaan voor onregelmatige lijsten. Merk echter op dat hier wat informatie over posities verloren gaat, dus we kunnen de operatie niet direct omkeren:
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}}
Om het correct te laten omkeren, zouden we om zoiets als dit te doen:
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}}
Een interessanter voorbeeld is wanneer we dieper nesten hebben:
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}}}
Ook hier kunnen we zien dat Flatten
effectief werkte als (gegeneraliseerd) Transpose
, waarbij stukken werden uitgewisseld op de eerste 2 niveaus . Het volgende zal moeilijker te begrijpen zijn:
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}}}
De volgende afbeelding illustreert deze gegeneraliseerde transpositie:
We kunnen het in twee opeenvolgende stappen doen:
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}}}
Sinds de permutatie {3,1,2}
kan worden verkregen als {1,3,2}
gevolgd door {2,1,3}
. Een andere manier om te zien hoe het werkt, is door gebruik nummers wh ich geef de positie in de lijststructuur aan:
Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {221, 222, 223}}}, {{3}, {1}, {2}}] (* ==> {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}} *)
Hieruit kan men zien dat in de buitenste lijst (eerste niveau) de derde index (die overeenkomt met de derde niveau van de oorspronkelijke lijst) groeit, in elke ledenlijst (tweede niveau) groeit het eerste element per element (overeenkomend met het eerste niveau van de oorspronkelijke lijst), en tenslotte in de binnenste (derde niveau) lijsten groeit de tweede index , overeenkomend met het tweede niveau in de oorspronkelijke lijst. Over het algemeen, als het k-de element van de lijst dat als tweede element is doorgegeven {n}
is, komt het vergroten van de k-de index in de resulterende lijststructuur overeen met het verhogen van de n-de index in de originele structuur.
Ten slotte kan men verschillende niveaus combineren om de subniveaus effectief af te vlakken, zoals:
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}}
Reacties
Antwoord
Een tweede lijstargument voor Flatten
dient twee doeleinden. Ten eerste specificeert het de volgorde waarin indices worden herhaald bij het verzamelen van elementen. Ten tweede beschrijft het het afvlakken van de lijst in het eindresultaat. Laten we eens naar elk van deze mogelijkheden kijken.
Herhalingsopdracht
Beschouw de volgende matrix:
$m = Array[Subscript[m, Row[{##}]]&, {4, 3, 2}]; $m // MatrixForm
We kunnen gebruiken een Table
expressie om een kopie van de matrix te maken door alle elementen ervan te herhalen:
$m === Table[$m[[i, j, k]], {i, 1, 4}, {j, 1, 3}, {k, 1, 2}] (* True *)
Deze identiteit bewerking is oninteressant, maar we kunnen de array transformeren door de volgorde van de iteratievariabelen om te wisselen. We kunnen bijvoorbeeld i
en j
omwisselen iteratoren. Dit komt neer op het verwisselen van de niveau 1- en niveau 2-indices en hun overeenkomstige elementen:
$r = Table[$m[[i, j, k]], {j, 1, 3}, {i, 1, 4}, {k, 1, 2}]; $r // MatrixForm
Als we goed kijken, kunnen we zien dat elk origineel element $m[[i, j, k]]
zal overeenkomen met het resulterende element $r[[j, i, k]]
– de eerste twee indices hebben bee n “swapped”.
Flatten
stelt ons in staat om een gelijkwaardige bewerking uit te drukken als deze Table
uitdrukking beknopter:
$r === Flatten[$m, {{2}, {1}, {3}}] (* True *)
Het tweede argument van de Flatten
expressie specificeert expliciet de gewenste indexvolgorde: indices 1, 2, 3 zijn gewijzigd om indices 2, 1, 3 te worden. Merk op dat we niet voor elke dimensie van de array een bereik hoefden te specificeren – een aanzienlijk gemak van notatie.
Het volgende Flatten
is een identiteitsbewerking omdat het geen wijziging van de indexvolgorde specificeert:
$m === Flatten[$m, {{1}, {2}, {3}}] (* True *)
Terwijl de volgende uitdrukking alle drie de indices opnieuw rangschikt: 1, 2 , 3 -> 3, 2, 1
Flatten[$m, {{3}, {2}, {1}}] // MatrixForm
Nogmaals , kunnen we verifiëren dat een origineel element gevonden in de index [[i, j, k]]
nu zal worden gevonden op [[k, j, i]]
in het resultaat.
Als er indices worden weggelaten uit een Flatten
expressie, worden ze behandeld alsof ze het laatst waren opgegeven en in hun natuurlijke volgorde:
Flatten[$m, {{3}}] === Flatten[$m, {{3}, {1}, {2}}] (* True *)
Dit laatste voorbeeld kan nog verder afgekort:
Flatten[$m, {3}] === Flatten[$m, {{3}}] (* True *)
Een lege indexlijst resulteert in de identiteitsbewerking:
$m === Flatten[$m, {}] === Flatten[$m, {1}] === Flatten[$m, {{1}, {2}, {3}}] (* True *)
Dat zorgt voor de volgorde van de iteratie en het wisselen van indexen. Laten we nu eens kijken naar …
Lijst afvlakken
Je zou je kunnen afvragen waarom we in de vorige voorbeelden elke index in een sublijst moesten specificeren. De reden is dat elke sublijst in de indexspecificatie specificeert welke indices in het resultaat moeten worden afgevlakt. Overweeg nogmaals de volgende identiteitsbewerking:
Flatten[$m, {{1}, {2}, {3}}] // MatrixForm
Wat gebeurt er als we de eerste twee indices combineren in dezelfde sublijst ?
Flatten[$m, {{1, 2}, {3}}] // MatrixForm
We kunnen zien dat het oorspronkelijke resultaat een 4 x 3 raster met paren, maar het tweede resultaat is een eenvoudige lijst met paren. De diepste structuur, de paren, bleven onaangeroerd. De eerste twee niveaus zijn afgevlakt tot een enkel niveau. De paren in het derde niveau van de bron matrix bleef niet afgevlakt.
We zouden in plaats daarvan de tweede twee indices kunnen combineren:
Flatten[$m, {{1}, {2, 3}}] // MatrixForm
Dit resultaat heeft hetzelfde aantal rijen als de originele matrix, wat betekent dat het eerste niveau onaangeroerd bleef. Maar elke resultaatrij heeft een platte lijst van zes elementen uit de corresponderende originele rij van drie paren. De onderste twee niveaus zijn dus afgevlakt.
We kunnen ook alle drie de indices combineren om een volledig afgevlakt resultaat te krijgen:
Flatten[$m, {{1, 2, 3}}]
Dit kan worden afgekort:
Flatten[$m, {{1, 2, 3}}] === Flatten[$m, {1, 2, 3}] === Flatten[$m] (* True *)
Flatten
biedt ook een verkorte notatie wanneer er geen indexwisseling moet plaatsvinden:
$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
Alle voorbeelden hebben tot dusver matrices van verschillende afmetingen gebruikt. Flatten
biedt een zeer krachtige functie waardoor het meer is dan alleen een afkorting voor een Table
uitdrukking. Flatten
zal netjes omgaan met het geval waarin sublijsten op een bepaald niveau verschillende lengtes hebben. Ontbrekende elementen worden stilletjes genegeerd. Een driehoekige array kan bijvoorbeeld worden omgedraaid:
$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 *)
…of omgedraaid en afgevlakt:
Flatten[$t, {{2, 1}}] (* {1,2,3,4,5,4,6,8,10,9,12,15,16,20,25} *)
Reacties
- Dit is een fantastische en grondige uitleg!
- @ rm-rf Bedankt. Ik denk dat als
Flatten
zou worden gegeneraliseerd om een functie te accepteren die moet worden toegepast bij het afvlakken (samentrekken) van indices, dat een uitstekende start zou zijn voor ” tensoralgebra in een blik “. - Soms moeten we interne contracties doen. Nu weet ik dat ik het kan doen met
Flatten[$m, {{1}, {2, 3}}]
in plaats van Map Flatten over een bepaald niveau. Het zou mooi zijn alsFlatten
negatieve argumenten accepteert om dat te doen. Dus dit geval zou kunnen worden geschreven alsFlatten[$m, -2]
. - Waarom dit uitstekende antwoord minder stemmen kreeg dan Leonid ‘ s: (.
- @Tangshutao Zie de tweede FAQ over mijn profiel .
Antwoord
Ik heb veel geleerd van WReach “s en Leonid” s antwoorden en ik “zou graag een kleine bijdrage willen leveren:
Het lijkt de moeite waard te benadrukken dat de primaire bedoeling van het lijstwaardige tweede argument van Flatten
slechts is om bepaalde niveaus van lijsten af te vlakken (zoals WReach vermeldt in zijn Lijst afvlakken sectie). Het gebruik van Flatten
als een haveloze Transpose
lijkt een kant -effect van dit primaire ontwerp, naar mijn mening.
Gisteren moest ik bijvoorbeeld deze lijst transformeren
lists = { {{{1, 0}, {1, 1}}, {{2, 0}, {2, 4}}, {{3, 0}}}, {{{1, 2}, {1, 3}}, {{2, Sqrt[2]}}, {{3, 4}}} (*, more lists... *) };
in deze:
list2 = { {{1, 0}, {1, 1}, {2, 0}, {2, 4}, {3, 0}}, {{1, 2}, {1, 3}, {2, Sqrt[2]}, {3, 4}} (*, more lists... *) }
Dat wil zeggen, ik moest de 2e en 3e lijstniveaus samen verpletteren.
Ik deed het met
list2 = Flatten[lists, {{1}, {2, 3}}];
Antwoord
Dit is een oude vraag, maar vaak gesteld door een partij van mensen. Toen ik vandaag probeerde uit te leggen hoe dit werkt, kwam ik een vrij duidelijke uitleg tegen, dus ik denk dat het nuttig zou zijn om het hier te delen voor een groter publiek.
Wat betekent index?
Laten we eerst duidelijk maken wat index is: in Mathematica is elke uitdrukking een boom, laten we bijvoorbeeld kijken op een lijst:
TreeForm@{{1,2},{3,4}}
Hoe navigeer je in een boom?
Eenvoudig! U begint bij de wortel en kiest bij elke kruising welke kant u op wilt. Als u bijvoorbeeld 2
wilt bereiken, begint u met het kiezen van de eerste pad en kies vervolgens het tweede pad. Laten we het opschrijven als {1,2}
, wat de enige index is van element 2
in deze uitdrukking.
Hoe Flatten
te begrijpen?
Beschouw hier een eenvoudige vraag, als ik u geen volledige uitdrukking geef, maar in plaats daarvan geef ik u alle elementen en hun indexen, hoe construeer je de oorspronkelijke uitdrukking? Hier geef ik je bijvoorbeeld:
{<|"index" -> {1, 1}, "value" -> 1|>, <|"index" -> {1, 2}, "value" -> 2|>, <|"index" -> {2, 1}, "value" -> 3|>, <|"index" -> {2, 2}, "value" -> 4|>}
en zeg je dat alle hoofden List
zijn, dus wat “s de originele uitdrukking?
Nou, je kunt de originele uitdrukking zeker reconstrueren als {{1,2},{3,4}}
, maar hoe? Je kunt waarschijnlijk de volgende stappen noemen:
- Eerst kijken we naar het eerste indexelement en sorteren en verzamelen we het. Dan weten we dat de eerste element van de hele expressie moet de eerste twee elementen in de originele lijst bevatten …
- Dan gaan we verder met het bekijken van het tweede argument, doe hetzelfde …
- Ten slotte krijgen we de originele lijst als
{{1,2},{3,4}}
.
Nou, dat is redelijk! Dus wat als ik je vertel, nee, je moet eerst sorteren en verzamelen op het tweede element van de index en dan verzamelen op het eerste element van de index? Of ik zeg dat we ze niet twee keer verzamelen, we sorteren gewoon op beide elementen maar geven het eerste argument een hogere prioriteit?
Nou, je zou waarschijnlijk respectievelijk de volgende twee lijsten krijgen, toch?
-
{{1,3},{2,4}}
-
{1,2,3,4}
Nou, controleer zelf, Flatten[{{1,2},{3,4}},{{2},{1}}]
en Flatten[{{1,2},{3,4}},{{1,2}}]
doen hetzelfde!
Dus, hoe begrijpt u het tweede argument van Flatten? ?
- Elk lijstelement in de hoofdlijst, bijvoorbeeld
{1,2}
, betekent dat u VERZAMEL alle lijsten volgens deze elementen in de index, met andere woorden deze niveaus . - De volgorde binnen een lijstelement geeft aan hoe u SORTEERT de elementen die in de vorige stap in een lijst zijn verzameld .
{2,1}
betekent bijvoorbeeld dat de positie op het tweede niveau een hogere prioriteit heeft dan de positie op het eerste niveau.
Voorbeelden
Laten we nu eens oefenen om vertrouwd te raken met eerdere regels.
1. Transpose
Het doel van Transpose
op een eenvoudige m * n-matrix is om $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $ te maken. Maar we kunnen het op een andere manier beschouwen, oorspronkelijk sorteren we de element op hun i
index eerst en vervolgens sorteren op hun j
index, nu hoeven we alleen maar te veranderen om ze te sorteren op j
indexeer eerst door i
daarna! De code wordt dus:
Flatten[mat,{{2},{1}}]
Simpel toch?
2. Traditioneel Flatten
Het doel van traditionele afvlakking op een eenvoudige m * n-matrix is om maak een 1D-array in plaats van een 2D-matrix, bijvoorbeeld: Flatten[{{1,2},{3,4}}]
retourneert {1,2,3,4}
. Dit betekent dat we deze keer “t geen elementen verzamelen, we alleen sorteer ze, eerst op hun eerste index en dan op de tweede:
Flatten[mat,{{1,2}}]
3. ArrayFlatten
Laten we een heel eenvoudig geval van ArrayFlatten bespreken, hier hebben we een 4D-lijst:
{{{{1,2},{5,6}},{{3,4},{7,8}}},{{{9,10},{13,14}},{{11,12},{15,16}}}}
dus hoe kunnen we zon conversie doen om er een 2D-lijst van te maken?
$ \ 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) $
Nou, dit is ook eenvoudig, we hebben de groep eerst nodig bij de oorspronkelijke index op het eerste en op het derde niveau, en we zouden de eerste index een hogere prioriteit moeten geven in sorteren. Hetzelfde geldt voor het tweede en het vierde niveau:
Flatten[mat,{{1,3},{2,4}}]
4. “Formaat wijzigen” van een afbeelding
Nu hebben we een afbeelding, bijvoorbeeld:
img=Image@RandomReal[1,{10,10}]
Maar het is beslist te klein voor ons om weergave, dus we willen het groter maken door elke pixel uit te breiden tot een 10 * 10 enorme pixel.
Eerst zullen we proberen:
ConstantArray[ImageData@img,{10,10}]
Maar het retourneert een 4D-matrix met dimensies {10,10,10,10}. Dus we moeten het Flatten
gebruiken. Deze keer willen we dat het derde argument een hogere prioriteit krijgt van de eerste, dus een kleine afstemming zou werken:
Image@Flatten[ConstantArray[ImageData@img,{10,10}],{{3,1},{4,2}}]
Een vergelijking:
Ik hoop dat dit kan helpen!
Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}}
zijn en het resultaat zou{{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}}
zijn.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}}
zegt u. Wat er gebeurt, is dat elementen die op niveau 1 in de oorspronkelijke lijst stonden, nu niveau 2 in het resultaat. Ik begrijp het ‘ niet helemaal, invoer en uitvoer hebben dezelfde niveaustructuur, elementen bevinden zich nog steeds op hetzelfde niveau. Kun je dit in het algemeen uitleggen?