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:

Ilustração da transposta generalizada cíclica

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

  • Eu acho ‘ mais fácil de entender se os números indicam a posição original na lista aninhada. Por exemplo, no exemplo de permutação de três números, seria 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}}}.
  • @celtschk Obrigado, mas não estou convencido. Para mim, pessoalmente, é mais fácil rastrear números visualmente distintos e ver onde eles terminam na nova estrutura. Mas sinta-se à vontade para acrescentar isso à minha resposta, está perfeitamente bem para mim.
  • Acho que nossas formas de compreensão são diferentes nesse aspecto. Na verdade, só depois de reescrever meu caminho, eu entendi completamente a permutação cíclica (mas diretamente, sem a decomposição em duas etapas que você fez depois). A questão é que, dessa forma, você pode ver imediatamente quais índices mudam se você mover ao longo de cada lista, e você pode determinar de onde o número se originou na estrutura da lista, mesmo sem olhar para a lista original. Em outras palavras, revela mais diretamente (pelo menos para mim) a estrutura da transformação.
  • @celtschk Sim, entendi seu raciocínio. Mas, para mim, foi mais fácil entender quais elementos saltaram quais listas, em vez de olhar para os índices dos elementos ‘. Pode ser que ‘ seja só eu, sempre gostei da geometria em vez da álgebra. Acho que as duas maneiras têm seus méritos.
  • @LeonidShifrin Para 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?

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 

resultado da matriz

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 

resultado da matriz

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 

resultado da matriz

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 

resultado da matriz

O que acontece se combinarmos os dois primeiros índices na mesma sublista ?

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

resultado da matriz

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 

resultado da matriz

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

resultado da matriz

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 se Flatten aceitasse argumentos negativos para fazer isso. Portanto, este caso poderia ser escrito como Flatten[$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... *) }; 

treeform1

neste:

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

treeform2

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

illus1

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:

  1. 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 …
  2. Em seguida, continuamos a olhar para o segundo argumento, faça o mesmo …
  3. 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. {{1,3},{2,4}}
  2. {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 ?

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

illus2

Espero que isso possa ajudar!

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *