Gostaria de saber se existe um atalho para fazer uma lista simples fora da lista de listas em Python.
Eu posso fazer isso em um for
loop, mas talvez haja algum "one-liner" legal? Eu tentei reduce()
, mas recebo um erro.
Código
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)
Mensagem de erro
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Respostas:
Dada uma lista de listas
l
,flat_list = [item for sublist in l for item in sublist]
que significa:
é mais rápido que os atalhos publicados até o momento. (
l
é a lista para achatar.)Aqui está a função correspondente:
Como evidência, você pode usar o
timeit
módulo na biblioteca padrão:Explicação: os atalhos baseados em
+
(incluindo o uso implícito emsum
) são, necessariamente,O(L**2)
quando existem L sublistas - como a lista de resultados intermediários fica cada vez mais longa, a cada etapa um novo objeto da lista de resultados intermediários é alocado e todos os itens no resultado intermediário anterior deve ser copiado (assim como alguns novos adicionados no final). Portanto, por simplicidade e sem perda real de generalidade, digamos que você tenha L sublistas de itens I cada: os primeiros itens I são copiados para frente e para trás L-1 vezes, o segundo I itens L-2 vezes e assim por diante; o número total de cópias é I vezes a soma de x para x de 1 a L excluída, ou sejaI * (L**2)/2
,.A compreensão da lista gera apenas uma lista, uma vez, e copia cada item (do local de residência original para a lista de resultados) também exatamente uma vez.
fonte
itertools.chain.from_iterable
:$ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'
. Ele roda um pouco mais que o dobro da velocidade da compreensão da lista aninhada, que é a mais rápida das alternativas mostradas aqui.list()
para realizar o iterador em uma lista.list(itertools.chain.from_iterable(l))
é melhor - como observado em outros comentários e na resposta de Shawn.Você pode usar
itertools.chain()
:Ou você pode usar o
itertools.chain.from_iterable()
que não requer descompactar a lista com o*
operador :fonte
*
é o truque que tornachain
menos direto do que a compreensão da lista. Você precisa saber que a cadeia une apenas os iterables passados como parâmetros, e o * faz com que a lista de nível superior seja expandida em parâmetros, portanto,chain
une todos esses iterables, mas não desce mais. Eu acho que isso torna a compreensão mais legível do que o uso da cadeia neste caso.for
loop repetidamenteappend
mais óbvio.list = [["abc","bcd"],["cde","def"],"efg"]
resultará em uma saída de["abc", "bcd", "cde", "def", "e", "f", "g"].
*
operador não pode ser usado em python2Nota do autor : Isso é ineficiente. Mas divertido, porque os monoides são incríveis. Não é apropriado para o código Python de produção.
Isso apenas soma os elementos de iterável passados no primeiro argumento, tratando o segundo argumento como o valor inicial da soma (se não for fornecido,
0
é usado em seu lugar e, neste caso, ocorrerá um erro).Como você está somando listas aninhadas, na verdade você obtém o
[1,3]+[2,4]
resultado desum([[1,3],[2,4]],[])
, que é igual a[1,3,2,4]
.Observe que funciona apenas em listas de listas. Para listas de listas de listas, você precisará de outra solução.
fonte
Monoid
, que é uma das abstrações mais convenientes para pensar em uma+
operação em um sentido geral (não limitado apenas a números). Portanto, esta resposta merece um +1 de mim pelo tratamento (correto) de listas como um monóide. O desempenho é preocupante ...Testei as soluções mais sugeridas com perfplot (um projeto para animais de estimação, essencialmente um invólucro
timeit
) e descobripara ser a solução mais rápida, quando muitas listas pequenas e poucas listas longas são concatenadas. (
operator.iadd
é igualmente rápido.)Código para reproduzir o gráfico:
fonte
O
extend()
método no seu exemplo modifica, emx
vez de retornar um valor útil (quereduce()
espera).Uma maneira mais rápida de fazer a
reduce
versão seriafonte
reduce(operator.add, l)
seria a maneira correta de fazer areduce
versão. Os embutidos são mais rápidos que os lambdas.timeit.timeit('reduce(operator.add, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000)
,017956018447875977 *timeit.timeit('reduce(lambda x, y: x+y, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000)
,025218963623046875integers
. Mas e se a lista contiverstring
?operator.add
função funciona igualmente bem para listas de números inteiros e listas de strings.Não reinvente a roda se você usar o Django :
... Pandas :
... Ferramentas :
... Matplotlib
... Unipath :
... Setuptools :
fonte
flatten = itertools.chain.from_iterable
deve ser a resposta certalist_of_menuitems = [1, 2, [3, [4, 5, [6]]]]
, que irá resultar em:[1, 2, 3, 4, 5, 6]
. O que sinto falta é o nível achatado.Aqui está uma abordagem geral que se aplica a números , seqüências de caracteres , listas aninhadas e contêineres mistos .
Código
Notas :
yield from flatten(x)
pode substituirfor sub_x in flatten(x): yield sub_x
collection.abc
para otyping
módulo.Demo
Referência
fonte
more_itertools
entre outros discutidos neste post. Felicidades.traverse
também possa ser um bom nome para esse tipo de árvore, enquanto eu o manteria menos universal para esta resposta, aderindo a listas aninhadas.if hasattr(x, '__iter__')
vez de importar / verificarIterable
e isso excluirá também as strings.Se você deseja achatar uma estrutura de dados em que não sabe o quão profundo está aninhado, você pode usar 1
iteration_utilities.deepflatten
É um gerador, então você precisa converter o resultado em
list
ou iterar explicitamente sobre ele.Para nivelar apenas um nível e se cada um dos itens for iterável, você também poderá usar o
iteration_utilities.flatten
que é apenas um invólucro finoitertools.chain.from_iterable
:Apenas para adicionar alguns horários (com base na resposta de Nico Schlömer que não incluiu a função apresentada nesta resposta):
É um gráfico de log-log para acomodar a enorme variedade de valores estendidos. Para raciocínio qualitativo: quanto mais baixo, melhor.
Os resultados mostram que se o iterable contém apenas alguns iterables internas, em seguida,
sum
será mais rápido, no entanto por longos iterables única asitertools.chain.from_iterable
,iteration_utilities.deepflatten
ou a compreensão aninhada ter um desempenho razoável comitertools.chain.from_iterable
sendo o mais rápido (como já foi observado por Nico Schlömer).1 Isenção de responsabilidade: eu sou o autor dessa biblioteca
fonte
sum
não funciona mais em seqüências arbitrárias como ele inicia0
, fazendofunctools.reduce(operator.add, sequences)
a substituição (não estamos felizes por elas terem sido removidasreduce
dos componentes internos?). Quando os tipos são conhecidos, pode ser mais rápido usartype.__add__
.sum
. Você sabe em quais versões do Python ele parou de funcionar?0
é apenas o valor inicial padrão, portanto, funciona se alguém usar o argumento start para começar com uma lista vazia ... mas ainda assim casos especiais seqüências de caracteres e me dizem para usar join. Está implementando emfoldl
vez defoldl1
. O mesmo problema aparece no 2.7.Eu retiro minha declaração. soma não é o vencedor. Embora seja mais rápido quando a lista é pequena. Mas o desempenho diminui significativamente com listas maiores.
A versão sum ainda está em execução por mais de um minuto e ainda não foi processada!
Para listas médias:
Usando listas pequenas e timeit: number = 1000000
fonte
Parece haver uma confusão com
operator.add
! Quando você adiciona duas listas, o termo correto para isso éconcat
não adicionar.operator.concat
é o que você precisa usar.Se você está pensando funcional, é tão fácil quanto isso ::
Você vê reduzir os aspectos do tipo de sequência; portanto, quando você fornece uma tupla, recebe uma tupla. Vamos tentar com uma lista:
Aha, você volta a lista.
Como sobre o desempenho ::
from_iterable
é bem rápido! Mas não há comparação com a qual reduzirconcat
.fonte
list(chain.from_iterable(...))
e 2,5 segundos parareduce(concat, ...)
. O problema é quereduce(concat, ...)
possui tempo de execução quadrático, enquantochain
é linear.Por que você usa estender?
Isso deve funcionar bem.
fonte
from functools import reduce
Considere instalar o
more_itertools
pacote.É fornecido com uma implementação para
flatten
( fonte , das receitas do itertools ):A partir da versão 2.4, você pode nivelar iteráveis aninhados mais complicados com
more_itertools.collapse
( fonte , contribuído por abarnet).fonte
A razão pela qual sua função não funcionou é porque a extensão estende uma matriz no local e não a retorna. Você ainda pode retornar x do lambda, usando algo como isto:
Nota: estender é mais eficiente que + nas listas.
fonte
extend
é melhor usado comonewlist = []
,extend = newlist.extend
,for sublist in l: extend(l)
uma vez que evita o (bastante grande) sobrecarga dolambda
, a procura do atributo nox
, e aor
.from functools import reduce
fonte
def flatten(l, a=None): if a is None: a = []
[...]Versão recursiva
fonte
matplotlib.cbook.flatten()
funcionará para listas aninhadas, mesmo que elas se aninhem mais profundamente que o exemplo.Resultado:
Isso é 18x mais rápido que o sublinhado ._. Flatten:
fonte
A resposta aceita não funcionou para mim ao lidar com listas de tamanho variável de textos. Aqui está uma abordagem alternativa que funcionou para mim.
Resposta aceita que não funcionou:
Nova solução que propôs fez trabalho para mim:
fonte
Uma característica ruim da função do Anil acima é que ele exige que o usuário sempre especifique manualmente o segundo argumento como uma lista vazia
[]
. Em vez disso, isso deve ser um padrão. Devido à maneira como os objetos Python funcionam, eles devem ser definidos dentro da função, não nos argumentos.Aqui está uma função de trabalho:
Teste:
fonte
A seguir, parece-me mais simples:
fonte
Pode-se também usar o apartamento do NumPy :
Editar 11/02/2016: Funciona apenas quando sublistas têm dimensões idênticas.
fonte
Você pode usar numpy:
flat_list = list(np.concatenate(list_of_list))
fonte
[1, 2, [3], [[4]], [5, [6]]]
Se você estiver disposto a abrir uma pequena quantidade de velocidade para obter uma aparência mais limpa, poderá usar
numpy.concatenate().tolist()
ounumpy.concatenate().ravel().tolist()
:Você pode descobrir mais aqui nos documentos numpy.concatenate e numpy.ravel
fonte
[1, 2, [3], [[4]], [5, [6]]]
Solução mais rápida que encontrei (para lista grande de qualquer maneira):
Feito! Obviamente, você pode transformá-lo novamente em uma lista executando a lista (l)
fonte
Código simples para o
underscore.py
ventilador do pacoteResolve todos os problemas de nivelamento (nenhum item da lista ou aninhamento complexo)
Você pode instalar
underscore.py
com pipfonte
fonte
[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Nota : Abaixo se aplica ao Python 3.3+ porque ele usa
yield_from
.six
também é um pacote de terceiros, embora seja estável. Como alternativa, você pode usarsys.version
.No caso de
obj = [[1, 2,], [3, 4], [5, 6]]
, todas as soluções aqui são boas, incluindo compreensão de lista eitertools.chain.from_iterable
.No entanto, considere este caso um pouco mais complexo:
Existem vários problemas aqui:
6
,, é apenas um escalar; não é iterável; portanto, as rotas acima falharão aqui.'abc'
, é tecnicamente iterable (todosstr
s são). No entanto, lendo um pouco as entrelinhas, você não deseja tratá-lo como tal - você deseja tratá-lo como um único elemento.[8, [9, 10]]
é ele próprio um iterável aninhado. Compreensão básica da lista echain.from_iterable
extraia apenas "1 nível abaixo".Você pode corrigir isso da seguinte maneira:
Aqui, você verifica se o subelemento (1) é iterável com
Iterable
um ABC deitertools
, mas também deseja garantir que (2) o elemento não seja "semelhante a uma string".fonte
yield from
para umfor
loop, por exemplofor x in flatten(i): yield x
Este código também funciona bem, pois apenas estende a lista até o fim. Embora seja muito parecido, mas só tem um para loop. Portanto, ele tem menos complexidade do que adicionar 2 para loops.
fonte
A vantagem desta solução sobre a maioria das outras aqui é que, se você tiver uma lista como:
enquanto a maioria das outras soluções gera um erro, esta solução lida com elas.
fonte
Esta pode não ser a maneira mais eficiente, mas pensei em colocar uma linha (na verdade, uma linha). Ambas as versões funcionarão em listas aninhadas na hierarquia arbitrária e explorarão os recursos da linguagem (Python3.5) e a recursão.
A saída é
Isso funciona de maneira profunda e profunda. A recursão diminui até encontrar um elemento que não seja da lista, estende a variável local
flist
e a reverte para o pai. Sempre queflist
é retornado, ele é estendido aosflist
na compreensão da lista. Portanto, na raiz, uma lista simples é retornada.A lista acima cria várias listas locais e as retorna, que são usadas para estender a lista dos pais. Eu acho que o caminho para isso pode estar criando um gloabl
flist
, como abaixo.A saída é novamente
Embora não tenha certeza no momento sobre a eficiência.
fonte
Outra abordagem incomum que funciona para listas hetero e homogêneas de números inteiros:
fonte
wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10]
>>nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0]
flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
[int(e.strip('[ ]')) for e in str(deep_list).split(',')]
. Mas eu sugiro manter a proposta de Deleet para casos de uso reais. Ele não contém transformações de tipo hacky, é mais rápido e versátil, porque naturalmente também lida com listas com tipos mistos.