Eu tenho uma lista de tamanho arbitrário e preciso dividi-la em partes do mesmo tamanho e operar com ela. Existem algumas maneiras óbvias de fazer isso, como manter um contador e duas listas e, quando a segunda lista for preenchida, adicione-a à primeira lista e esvazie a segunda lista para a próxima rodada de dados, mas isso é potencialmente extremamente caro.
Fiquei me perguntando se alguém tinha uma boa solução para isso para listas de qualquer tamanho, por exemplo, usando geradores.
Eu estava procurando por algo útil, itertools
mas não consegui encontrar nada obviamente útil. Pode ter perdido, no entanto.
Pergunta relacionada: Qual é a maneira mais “pitônica” de iterar sobre uma lista em pedaços?
Respostas:
Aqui está um gerador que produz os pedaços que você deseja:
Se você estiver usando o Python 2, use em
xrange()
vez derange()
:Além disso, você pode simplesmente usar a compreensão da lista em vez de escrever uma função, embora seja uma boa idéia encapsular operações como esta em funções nomeadas para que seu código seja mais fácil de entender. Python 3:
Versão do Python 2:
fonte
Se você quer algo super simples:
Use em
xrange()
vez derange()
no caso do Python 2.xfonte
max()
.Diretamente da documentação do Python (antiga) (receitas para itertools):
A versão atual, conforme sugerido por JFSebastian:
Acho que a máquina do tempo de Guido funciona - funcionou - funcionará - terá funcionado - estava funcionando novamente.
Essas soluções funcionam porque
[iter(iterable)]*n
(ou o equivalente na versão anterior) cria um iterador, repetidasn
vezes na lista.izip_longest
efetivamente executa um round-robin de "cada" iterador; como esse é o mesmo iterador, ele é avançado em cada chamada, resultando em cada zip-roundrobin gerando uma tupla den
itens.fonte
list(grouper(3, range(10)))
retorna[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
, e todas as tuplas são de comprimento 3. Por favor, elabore seu comentário porque eu não consigo entender; como você chama uma coisa e como você define que ela é um múltiplo de 3 em "esperar que sua coisa seja um múltiplo de 3"? Agradeço antecipadamente.itertools
abordagem funcional transformar alguns lama ilegível, quando comparado com uma implementação python pura ingenuidade simples el==[1, 2, 3]
, em seguida,f(*l)
é equivalente af(1, 2, 3)
. Veja essa pergunta e a documentação oficial .Eu sei que isso é meio velho, mas ninguém ainda mencionou
numpy.array_split
:fonte
Estou surpreso que ninguém tenha pensado em usar
iter
a forma de dois argumentos :Demo:
Isso funciona com qualquer iterável e produz resultados preguiçosamente. Ele retorna tuplas em vez de iteradores, mas acho que, apesar disso, tem uma certa elegância. Também não acolchoa; se você quiser preenchimento, basta uma simples variação no item acima:
Demo:
Como as
izip_longest
soluções baseadas, o acima sempre se aplica. Até onde eu sei, não há receita de ferramentas de uma ou duas linhas para uma função que é opcional . Combinando as duas abordagens acima, essa chega bem perto:Demo:
Acredito que este seja o menor chunker proposto que oferece preenchimento opcional.
Como Tomasz Gandor observou , os dois chunkers de preenchimento irão parar inesperadamente se encontrarem uma longa sequência de valores de preenchimento. Aqui está uma variação final que soluciona esse problema de maneira razoável:
Demo:
fonte
islice(it, size)
expressão básica e a incorporaram (como eu havia feito) em uma construção de loop. Somente você pensou na versão de dois argumentositer()
(eu desconhecia completamente), o que a torna super elegante (e provavelmente mais eficaz em termos de desempenho). Eu não tinha ideia de que o primeiro argumentoiter
muda para uma função de 0 quando recebe o sentinela. Você retorna um iterador (pote. Infinito) de blocos, pode usar um iterador (pote. Infinito) como entrada,len()
sem fatias de matriz e sem. Impressionante!it
iterador segundo lugar, e mais importanlty -. Você vai acabar prematuramente se um pedaço depadval
realmente existe em seu iterable, e deve ser processado.izip_longest
abordagem, por exemplo - suspeito que possa ser uma troca complexa. Mas ... opadval
problema não é compartilhado por todas as respostas aqui que oferecem umpadval
parâmetro?()
como a sentinela, faz trabalho corretamente Isto porque.tuple(islice(it, size))
Rendimentos()
quandoit
está vazio.)Aqui está um gerador que trabalha em iterables arbitrários:
Exemplo:
fonte
fonte
map(None, iter)
é igualizip_longest(iter)
.*
tupla do iterador à sua frente? Possivelmente no seu texto de resposta, mas notei que isso foi*
usado dessa maneira em Python antes. Obrigado!Simples, mas elegante
ou se você preferir:
fonte
1
el
são indistinguíveis. Como são0
eO
. E às vezes atéI
e1
.print [l[x:x+10] for x in xrange(1, len(l), 10)]
range
.Crítica de outras respostas aqui:
Nenhuma dessas respostas é de tamanho uniforme; todas deixam um pedaço de acréscimo no final, para que não fiquem completamente equilibradas. Se você estava usando essas funções para distribuir trabalho, incorporou a perspectiva de uma provável conclusão bem antes das outras, para que ela permanecesse sem fazer nada enquanto as outras continuassem trabalhando duro.
Por exemplo, a resposta principal atual termina com:
Eu apenas odeio esse idiota no final!
Outros, como
list(grouper(3, xrange(7)))
, echunk(xrange(7), 3)
tanto retorno:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. ElesNone
são apenas estofados e bastante deselegantes na minha opinião. Eles NÃO estão uniformemente dividindo os iteráveis.Por que não podemos dividir isso melhor?
Minha (s) solução (s)
Aqui está uma solução equilibrada, adaptada de uma função que eu usei na produção (Nota em Python 3 para substituir
xrange
comrange
):E eu criei um gerador que faz o mesmo se você o colocar em uma lista:
E, finalmente, como vejo que todas as funções acima retornam elementos em uma ordem contígua (como foram dadas):
Resultado
Para testá-los:
Que imprime:
Observe que o gerador contíguo fornece blocos nos mesmos padrões de comprimento que os outros dois, mas os itens estão todos em ordem e são tão uniformemente divididos quanto se pode dividir uma lista de elementos discretos.
fonte
list(grouper(3, xrange(7)))
e o segundo,chunk(xrange(7), 3)
ambos retorno:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. ElesNone
são apenas estofados e bastante deselegantes na minha opinião. Eles NÃO estão uniformemente dividindo os iteráveis. Obrigado pelo seu voto!import pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
Eu vi a resposta mais impressionante do Python-ish em uma duplicata desta pergunta:
Você pode criar n-tupla para qualquer n. Se
a = range(1, 15)
, então o resultado será:Se a lista estiver dividida igualmente, você poderá substituí-lo
zip_longest
porzip
, caso contrário, o trigêmeo(13, 14, None)
seria perdido. Python 3 é usado acima. Para Python 2, useizip_longest
.fonte
zip(i, i, i, ... i)
com argumentos "chunk_size", zip () pode ser escrito comozip(*[i]*chunk_size)
se é uma boa ideia ou não, é discutível, é claro.zip_longest
deve ser usado, como feito em: stackoverflow.com/a/434411/1959808range(1, 15)
já está faltando elementos, porque existem 14 elementos emrange(1, 15)
, não 15.Se você souber o tamanho da lista:
Caso contrário (um iterador):
No último caso, ele pode ser reformulado de uma maneira mais bonita, se você tiver certeza de que a sequência sempre contém um número inteiro de pedaços de determinado tamanho (ou seja, não há um último pedaço incompleto).
fonte
A biblioteca toolz tem a
partition
função para isso:fonte
Se você tivesse um tamanho de 3, por exemplo, você poderia:
fonte: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/
Eu usaria isso quando meu tamanho de bloco for um número fixo que eu possa digitar, por exemplo, '3', e nunca mudaria.
fonte
Eu gosto muito da versão do doc do Python proposta pelo tzot e pelo JFSebastian, mas tem duas deficiências:
Estou usando muito este no meu código:
ATUALIZAÇÃO: Uma versão preguiçosa dos pedaços:
fonte
while True
loop?StopIteration
gerado quando otuple
está vazio eiterable.next()
é executado. Porém, não funciona corretamente no Python moderno, onde a saída de um gerador deve ser feitareturn
, e não a elevaçãoStopIteration
. Umatry/except StopIteration: return
volta ao redor de todo o loop (e a alteraçãoiterable.next()
paranext(iterable)
compatibilidade entre versões) corrige isso com uma sobrecarga mínima, pelo menos.Onde AA é matriz, SS é tamanho de bloco. Por exemplo:
fonte
Fiquei curioso sobre o desempenho de diferentes abordagens e aqui está:
Testado em Python 3.5.1
Resultados:
fonte
time
biblioteca não é uma ótima idéia quando temostimeit
módulocódigo:
resultado:
fonte
Você também pode usar a
get_chunks
função dautilspie
biblioteca como:Você pode instalar
utilspie
via pip:Disclaimer: Eu sou o criador da biblioteca utilspie .
fonte
Neste ponto, acho que precisamos de um gerador recursivo , apenas no caso de ...
No python 2:
No python 3:
Além disso, em caso de invasão massiva de alienígenas, um gerador recursivo decorado pode se tornar útil:
fonte
Com as Expressões de atribuição no Python 3.8, torna-se bastante agradável:
Isso funciona em um iterável arbitrário, não apenas em uma lista.
fonte
heh, versão de uma linha
fonte
def chunk
vez dechunk=lambda
possui o atributo .__ name__ 'chunk' em vez de '<lambda>'. O nome específico é mais útil no rastreamento.<lamba>
ou não é, pelo menos, uma diferença notável.uso:
fonte
Outra versão mais explícita.
fonte
Sem chamar len (), o que é bom para grandes listas:
E isso é para iterables:
O sabor funcional do acima exposto:
OU:
OU:
fonte
len()
em grandes listas; é uma operação de tempo constante.Aqui está uma lista de abordagens adicionais:
Dado
Código
A Biblioteca Padrão
more_itertools
+Referências
zip_longest
( postagem relacionada , postagem relacionada )setdefault
(os resultados ordenados requerem Python 3.6+)collections.defaultdict
(os resultados ordenados requerem Python 3.6+)more_itertools.chunked
( publicado relacionado )more_itertools.sliced
more_itertools.grouper
( publicação relacionada )more_itertools.windowed
(ver tambémstagger
,zip_offset
)+ Uma biblioteca de terceiros que implementa receitas de ferramentas e muito mais.
> pip install more_itertools
fonte
Veja esta referência
Python3
fonte
zip(*[iter(range(7))]*3)
apenas retorna[(0, 1, 2), (3, 4, 5)]
e esquece o6
da entrada.Já que todo mundo aqui está falando sobre iteradores.
boltons
tem um método perfeito para isso, chamadoiterutils.chunked_iter
.Resultado:
Mas se você não quer ter piedade da memória, pode usar o modo antigo e armazenar o máximo
list
em primeiro lugariterutils.chunked
.fonte
Mais uma solução
fonte
fonte
Considere usar partes do matplotlib.cbook
por exemplo:
fonte