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:

Illustratie van cyclische 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

  • Ik denk dat het ‘ gemakkelijker te begrijpen is als de cijfers de oorspronkelijke positie in de geneste lijst aangeven. In het voorbeeld van een permutatie met drie cijfers zou het bijvoorbeeld 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.
  • @celtschk Bedankt, maar ik ben niet overtuigd. Voor mij persoonlijk is het gemakkelijker om visueel verschillende nummers te volgen en te zien waar ze in de nieuwe structuur terechtkomen. Maar voel je vrij om dit aan mijn antwoord toe te voegen, dit is prima voor mij.
  • Ik denk dat onze manier van begrijpen in dat opzicht gewoon anders is. Eigenlijk begreep ik pas na het herschrijven van mijn manier de cyclische permutatie volledig (maar dan direct, zonder de tweestaps ontbinding die je daarna deed). Het punt is dat je op deze manier direct kunt zien welke index verandert als je langs elke lijst beweegt, en je kunt bepalen waar het nummer vandaan komt in de lijststructuur zonder zelfs maar naar de originele lijst te kijken. Met andere woorden, het onthult directer (althans voor mij) de structuur van de transformatie.
  • @celtschk Ja, ik begreep je redenering. Maar voor mij was het gemakkelijker om te begrijpen welke elementen uit welke lijsten zijn gesprongen, in plaats van naar elementen ‘ indices te kijken. Misschien is het ‘ alleen mij, ik ben altijd aangetrokken tot geometrie in plaats van algebra. Ik denk dat beide manieren hun verdiensten hebben.
  • @LeonidShifrin Voor 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?

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 

matrixresultaat

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 

matrixresultaat

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 

matrixresultaat

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 

matrixresultaat

Wat gebeurt er als we de eerste twee indices combineren in dezelfde sublijst ?

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

matrixresultaat

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 

matrixresultaat

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

matrixresultaat

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 als Flatten negatieve argumenten accepteert om dat te doen. Dus dit geval zou kunnen worden geschreven als Flatten[$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... *) }; 

treeform1

in deze:

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

treeform2

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

illus1

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:

  1. 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 …
  2. Dan gaan we verder met het bekijken van het tweede argument, doe hetzelfde …
  3. 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. {{1,3},{2,4}}
  2. {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? ?

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

illus2

Ik hoop dat dit kan helpen!

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *