Uma coisa que eu nunca consegui entender é como Flatten
funciona quando fornecido com uma matriz como o segundo argumento, e a ajuda do Mathematica não é particularmente boa neste.
Retirado do Flatten
Mathematica documentação:
Flatten[list, {{s11, s12, ...}, {s21, s22, ...}, ...}]
Nivela
list
combinando todos os níveis $ s_ {ij} $ para tornar cada nível $ i $ no resultado.
Alguém poderia explicar o que isso realmente significa / significa?
Resposta
Um uma maneira conveniente de pensar em Flatten
com o segundo argumento é que ele executa algo como Transpose
para listas desiguais (irregulares). Aqui está uma exemplo:
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}}
O que acontece são os elementos que constituem nível uted 1
na lista original agora são constituintes no nível 2
no resultado e vice-versa. Isso é exatamente o que Transpose
faz, mas feito para listas irregulares. Observe, entretanto, que algumas informações sobre as posições são perdidas aqui, então não podemos inverter a operação diretamente:
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}}
Para inverter corretamente, teríamos para fazer algo assim:
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}}
Um exemplo mais interessante é quando temos um aninhamento mais profundo:
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}}}
Aqui, novamente, podemos ver que Flatten
funcionou efetivamente como (generalizado) Transpose
, trocando peças nos 2 primeiros níveis . O seguinte será mais difícil de entender:
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}}}
A imagem a seguir ilustra essa transposição generalizada:
Podemos fazer isso em duas etapas consecutivas:
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}}}
Desde a permutação {3,1,2}
pode ser obtido como {1,3,2}
seguido por {2,1,3}
. Outra maneira de ver como funciona é usar números que que indicam a posição na estrutura da lista:
Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {221, 222, 223}}}, {{3}, {1}, {2}}] (* ==> {{{111, 121}, {211, 221}}, {{112, 122}, {212, 222}}, {{113}, {223}}} *)
A partir disso, pode-se ver que na lista mais externa (primeiro nível), o terceiro índice (correspondendo a terceiro nível da lista original) cresce, em cada lista de membros (segundo nível) o primeiro elemento cresce por elemento (correspondendo ao primeiro nível da lista original) e, finalmente, nas listas mais internas (terceiro nível), o segundo índice cresce , correspondendo ao segundo nível na lista original. Geralmente, se o k-ésimo elemento da lista passado como segundo elemento for {n}
, aumentar o k-ésimo índice na estrutura de lista resultante corresponde a aumentar o n-ésimo índice no estrutura original.
Finalmente, é possível combinar vários níveis para nivelar efetivamente os subníveis, da seguinte forma:
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}}
Comentários
Resposta
Um segundo argumento de lista para Flatten
serve dois finalidades. Primeiro, ele especifica a ordem em que os índices serão iterados ao reunir elementos. Em segundo lugar, descreve o achatamento da lista no resultado final. Vejamos cada um desses recursos separadamente.
Ordem de iteração
Considere a seguinte matriz:
$m = Array[Subscript[m, Row[{##}]]&, {4, 3, 2}]; $m // MatrixForm
Podemos usar uma expressão Table
para criar uma cópia da matriz iterando todos os seus elementos:
$m === Table[$m[[i, j, k]], {i, 1, 4}, {j, 1, 3}, {k, 1, 2}] (* True *)
Esta identidade operação não é interessante, mas podemos transformar a matriz trocando a ordem das variáveis de iteração. Por exemplo, podemos trocar i
e j
iteradores. Isso equivale a trocar os índices de nível 1 e nível 2 e seus elementos correspondentes:
$r = Table[$m[[i, j, k]], {j, 1, 3}, {i, 1, 4}, {k, 1, 2}]; $r // MatrixForm
Se olharmos com atenção, podemos ver que cada elemento original $m[[i, j, k]]
corresponderá ao elemento resultante $r[[j, i, k]]
– os primeiros dois índices foram n “swapped”.
Flatten
nos permite expressar uma operação equivalente a esta Table
expressão de forma mais sucinta:
$r === Flatten[$m, {{2}, {1}, {3}}] (* True *)
O segundo argumento da expressão Flatten
especifica explicitamente a ordem do índice desejada: os índices 1, 2, 3 são alterado para se tornar os índices 2, 1, 3. Observe como não precisamos especificar um intervalo para cada dimensão da matriz – uma notação de conveniência significativa.
O seguinte Flatten
é uma operação de identidade, uma vez que não especifica nenhuma mudança na ordem do índice:
$m === Flatten[$m, {{1}, {2}, {3}}] (* True *)
Considerando que a seguinte expressão reorganiza todos os três índices: 1, 2 , 3 -> 3, 2, 1
Flatten[$m, {{3}, {2}, {1}}] // MatrixForm
Novamente , podemos verificar que um elemento original encontrado no índice [[i, j, k]]
agora será encontrado em [[k, j, i]]
no resultado.
Se algum índice for omitido de um Flatten
expressão, eles são tratados como se tivessem sido especificados por último e em sua ordem natural:
Flatten[$m, {{3}}] === Flatten[$m, {{3}, {1}, {2}}] (* True *)
Este último exemplo pode ser abreviado ainda mais:
Flatten[$m, {3}] === Flatten[$m, {{3}}] (* True *)
Uma lista de índice vazia resulta na operação de identidade:
$m === Flatten[$m, {}] === Flatten[$m, {1}] === Flatten[$m, {{1}, {2}, {3}}] (* True *)
Isso cuida da ordem de iteração e troca de índice. Agora, vejamos …
Achatamento da lista
Alguém pode se perguntar por que tivemos que especificar cada índice em uma sublista nos exemplos anteriores. O motivo é que cada sublista na especificação do índice especifica quais índices devem ser nivelados no resultado. Considere novamente a seguinte operação de identidade:
Flatten[$m, {{1}, {2}, {3}}] // MatrixForm
O que acontece se combinarmos os dois primeiros índices na mesma sublista ?
Flatten[$m, {{1, 2}, {3}}] // MatrixForm
Podemos ver que o resultado original foi um Grade de pares 4 x 3, mas o segundo resultado é uma lista simples de pares. A estrutura mais profunda, os pares, não foi alterada. Os dois primeiros níveis foram nivelados em um único nível. Os pares no terceiro nível da fonte a matriz permaneceu inalterada.
Poderíamos combinar os segundos dois índices em vez disso:
Flatten[$m, {{1}, {2, 3}}] // MatrixForm
Este resultado tem o mesmo número de linhas da matriz original, significando que o primeiro nível não foi alterado. Mas cada linha de resultado tem uma lista plana de seis elementos retirados da linha original correspondente de três pares. Assim, os dois níveis inferiores foram nivelados.
Também podemos combinar os três índices para obter um resultado completamente nivelado:
Flatten[$m, {{1, 2, 3}}]
Isso pode ser abreviado:
Flatten[$m, {{1, 2, 3}}] === Flatten[$m, {1, 2, 3}] === Flatten[$m] (* True *)
Flatten
também oferece uma notação abreviada quando nenhuma troca de índice deve ocorrer:
$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 *)
Arrays “Ragged”
Todos os exemplos até agora usaram matrizes de várias dimensões. Flatten
oferece um recurso muito poderoso que o torna mais do que apenas uma abreviatura para uma expressão Table
. Flatten
tratará normalmente o caso em que as sublistas em qualquer nível têm comprimentos diferentes. Elementos ausentes serão silenciosamente ignorados. Por exemplo, uma matriz triangular pode ser invertida:
$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 *)
…ou invertido e achatado:
Flatten[$t, {{2, 1}}] (* {1,2,3,4,5,4,6,8,10,9,12,15,16,20,25} *)
Comentários
- Esta é uma explicação fantástica e completa!
- @ rm-rf Obrigado. Eu acho que se
Flatten
fosse generalizado para aceitar uma função a ser aplicada ao nivelar (contrair) índices, isso seria um excelente começo para ” álgebra tensorial em uma lata “. - Às vezes precisamos fazer contrações internas. Agora eu sei que posso fazer isso usando
Flatten[$m, {{1}, {2, 3}}]
em vez de Map Flatten em algum nível. Seria bom seFlatten
aceitasse argumentos negativos para fazer isso. Portanto, este caso poderia ser escrito comoFlatten[$m, -2]
. - Por que essa excelente resposta teve menos votos do que Leonid ‘ s: (.
- @Tangshutao Veja a segunda FAQ em meu perfil .
Resposta
Aprendi muito com as respostas de WReach “se Leonid” e gostaria de dar uma pequena contribuição:
Parece valer a pena enfatizando que a intenção primária do segundo argumento com valor de lista de Flatten
é meramente nivelar certos níveis de listas (como WReach menciona em seu Nivelamento de lista seção). Usar Flatten
como um Transpose
irregular parece um lado -efeito deste design primário, na minha opinião.
Por exemplo, ontem eu precisei transformar esta lista
lists = { {{{1, 0}, {1, 1}}, {{2, 0}, {2, 4}}, {{3, 0}}}, {{{1, 2}, {1, 3}}, {{2, Sqrt[2]}}, {{3, 4}}} (*, more lists... *) };
neste:
list2 = { {{1, 0}, {1, 1}, {2, 0}, {2, 4}, {3, 0}}, {{1, 2}, {1, 3}, {2, Sqrt[2]}, {3, 4}} (*, more lists... *) }
Ou seja, eu precisava compactar o segundo e o terceiro níveis da lista.
Fiz isso com
list2 = Flatten[lists, {{1}, {2, 3}}];
Resposta
Esta é uma pergunta antiga, mas frequentemente feita por um lote de pessoas. Hoje, quando estava tentando explicar como isso funciona, me deparei com uma explicação bastante clara, então acho que compartilhá-la aqui seria útil para um público maior.
O que significa índice?
Primeiro vamos deixar claro o que index é: No Mathematica cada expressão é uma árvore, por exemplo, vamos olhar em uma lista:
TreeForm@{{1,2},{3,4}}
Como você navega em uma árvore?
Simples! Você começa da raiz e em cada cruzamento escolhe o caminho a seguir, por exemplo, aqui se você deseja alcançar 2
, você começa escolhendo primeiro caminho e, em seguida, escolha o segundo caminho. Vamos escrever como {1,2}
que é apenas o índice do elemento 2
nesta expressão.
Como entender Flatten
?
Considere aqui uma pergunta simples, se eu não fornecer uma expressão completa, mas, em vez disso, fornecer todos os elementos e seus índices, como você constrói a expressão original? Por exemplo, aqui eu dou a você:
{<|"index" -> {1, 1}, "value" -> 1|>, <|"index" -> {1, 2}, "value" -> 2|>, <|"index" -> {2, 1}, "value" -> 3|>, <|"index" -> {2, 2}, "value" -> 4|>}
e digo a você que todas as caras são List
, e daí “s a expressão original?
Bem, certamente você pode reconstruir a expressão original como {{1,2},{3,4}}
, mas como? Você provavelmente pode listar as seguintes etapas:
- Primeiro, olhamos o primeiro elemento do índice, classificamos e reunimos por ele. Então sabemos que o primeiro elemento de toda a expressão deve conter os primeiros dois elementos da lista original …
- Em seguida, continuamos a olhar para o segundo argumento, faça o mesmo …
- Finalmente, obtemos a lista original como
{{1,2},{3,4}}
.
Bem, isso é razoável! E se eu disser que não, você deve primeiro classificar e reunir pelo segundo elemento do índice e, em seguida, reunir pelo primeiro elemento do índice? Ou eu digo que não os reunimos duas vezes, apenas classificamos por ambos os elementos, mas atribuímos ao primeiro argumento uma prioridade mais alta?
Bem, você provavelmente obteria as duas listas a seguir, respectivamente, certo?
-
{{1,3},{2,4}}
-
{1,2,3,4}
Bem, verifique você mesmo, Flatten[{{1,2},{3,4}},{{2},{1}}]
e Flatten[{{1,2},{3,4}},{{1,2}}]
faça o mesmo!
Então, como você entende o segundo argumento de Flatten ?
- Cada elemento da lista dentro da lista principal, por exemplo,
{1,2}
, significa que você deve REÚNA todas as listas de acordo com esses elementos no índice, em outras palavras, estes níveis . - A ordem dentro de um elemento de lista representa como você CLASSIFICAR os elementos reunidos em uma lista na etapa anterior . por exemplo,
{2,1}
significa que a posição no segundo nível tem maior prioridade do que a posição no primeiro nível.
Exemplos
Agora vamos ter alguma prática para se familiarizar com as regras anteriores.
1. Transpose
O objetivo de Transpose
em uma matriz m * n simples é fazer $ A_ {i, j} \ rightarrow A ^ T_ {j, i} $. Mas podemos considerar isso de outra forma, originalmente classificamos o elemento por seu i
índice primeiro, depois classifique-os por seu j
índice, agora tudo o que precisamos fazer é alterar para classificá-los por j
indexe primeiro e depois i
em seguida! Portanto, o código se torna:
Flatten[mat,{{2},{1}}]
Simples, certo?
2. Tradicional Flatten
O objetivo do achatamento tradicional em uma matriz m * n simples é crie uma matriz 1D em vez de uma matriz 2D, por exemplo: Flatten[{{1,2},{3,4}}]
retorna {1,2,3,4}
. Isso significa que não “t reunimos elementos desta vez, apenas classifique -los, primeiro pelo primeiro índice e depois pelo segundo:
Flatten[mat,{{1,2}}]
3. ArrayFlatten
Vamos discutir um caso mais simples de ArrayFlatten, aqui temos uma lista 4D:
{{{{1,2},{5,6}},{{3,4},{7,8}}},{{{9,10},{13,14}},{{11,12},{15,16}}}}
então, como podemos fazer essa conversão para torná-la uma lista 2D?
$ \ left (\ begin {array} {cc} \ left (\ begin {array} {cc} 1 & 2 \\ 5 & 6 \\ \ end {array} \ right) & \ esquerda (\ 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) $
Bem, isso também é simples, precisamos do grupo pelo índice original de primeiro e terceiro nível primeiro, e devemos dar ao primeiro índice maior prioridade em Ordenação. O mesmo vale para o segundo e o quarto nível:
Flatten[mat,{{1,3},{2,4}}]
4. “Redimensionar” uma imagem
Agora temos uma imagem, por exemplo:
img=Image@RandomReal[1,{10,10}]
Mas é definitivamente muito pequeno para nós , portanto, queremos torná-lo maior estendendo cada pixel para um pixel enorme de tamanho 10 * 10.
Primeiro, tentaremos:
ConstantArray[ImageData@img,{10,10}]
Mas ele retorna uma matriz 4D com dimensões {10,10,10,10}. Portanto, devemos Flatten
. Desta vez, queremos que o terceiro argumento tenha maior prioridade do primeiro, então um pequeno ajuste funcionaria:
Image@Flatten[ConstantArray[ImageData@img,{10,10}],{{3,1},{4,2}}]
Uma comparação:
Espero que isso possa ajudar!
Flatten[{{{111, 112, 113}, {121, 122}}, {{211, 212}, {{221,222,223}}}, {{3},{1},{2}}}
e o resultado seria{{{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}}
, você diz: O que acontece é que os elementos que constituíam o nível 1 na lista original agora são constituintes em nível 2 no resultado. Não ‘ não entendo muito bem, a entrada e a saída têm a mesma estrutura de nível, os elementos ainda estão no mesmo nível. Você poderia explicar isso em geral?