Preciso de uma janela rotativa (também conhecida como janela deslizante) iterável em uma sequência / iterador / gerador. A iteração padrão do Python pode ser considerada um caso especial, onde o comprimento da janela é 1. Atualmente, estou usando o código a seguir. Alguém tem um método mais pitonico, menos detalhado ou mais eficiente para fazer isso?
def rolling_window(seq, window_size):
it = iter(seq)
win = [it.next() for cnt in xrange(window_size)] # First window
yield win
for e in it: # Subsequent windows
win[:-1] = win[1:]
win[-1] = e
yield win
if __name__=="__main__":
for w in rolling_window(xrange(6), 3):
print w
"""Example output:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
"""
sum()
oumax()
), vale lembrar que existem algoritmos eficientes para calcular o novo valor para cada janela em tempo constante (independentemente do tamanho da janela). Eu coletei alguns desses algoritmos juntos em uma biblioteca Python: rolling .Respostas:
Há um em uma versão antiga dos documentos Python com
itertools
exemplos :O dos documentos é um pouco mais sucinto e usa
itertools
com maior efeito, imagino.fonte
for elem in it
loop?Isso parece feito sob medida para uma
collections.deque
vez que você basicamente possui um FIFO (adicione a uma extremidade, remova da outra). No entanto, mesmo se você usar umlist
, não deve fatiar duas vezes; em vez disso, você deve provavelmente apenas apop(0)
partir da lista eappend()
do novo item.Aqui está uma implementação otimizada baseada em deque padronizada após o seu original:
Nos meus testes, ele supera facilmente tudo o que foi postado aqui a maior parte do tempo, embora a
tee
versão do pillmuncher o supere para iterables grandes e janelas pequenas. Em janelas maiores, adeque
força avança novamente em velocidade bruta.O acesso a itens individuais no
deque
pode ser mais rápido ou mais lento do que com listas ou tuplas. (Os itens próximos ao início são mais rápidos ou os itens próximos ao final, se você usar um índice negativo.) Coloquei umsum(w)
no corpo do meu loop; isso é compatível com a força do deque (a iteração de um item para o próximo é rápida, portanto, esse loop é executado 20% mais rápido que o método mais rápido, o pillmuncher). Quando mudei para procurar individualmente e adicionar itens em uma janela de dez, as tabelas giraram e otee
método foi 20% mais rápido. Consegui recuperar alguma velocidade usando índices negativos nos últimos cinco termos da adição, mastee
ainda era um pouco mais rápido. No geral, eu estimaria que qualquer um deles seja bastante rápido para a maioria dos usos e, se você precisar de um pouco mais de desempenho, crie um perfil e escolha o que funciona melhor.fonte
yield win
deve seryield tuple(win)
ouyield list(win)
impedir o retorno de um iterador de referências para o mesmodeque
objeto.pip install sliding_window
e execute comfrom sliding_window import window
.list(window(range(10)))
deve produzir algo como [[0,1], [1,2], [2,3], ...] #list(list(x) for x in window(range(10)))
ou adicioná-lo ao iterador. Para alguns aplicativos, isso é importante, para outros, e, como eu estava acelerando, eu não o escolhi e coloquei o ônus no chamador para copiar a janela, se necessário.tuple()
antes do rendimento, esse método não terá nenhuma vantagem sobre os outros.Eu gosto
tee()
:dá:
fonte
timeit
testes rápidos , isso é muito mais lento que o de Daniel DePaolo (na proporção de 2: 1) e não parece muito "mais agradável".size
. Se você aumentar (por exemplo, se o iterável tiver 100000 elementos, faça o tamanho da janela 1000), você poderá ver um aumento.iters
é O (tamanho!), E chamarnext()
muitas vezes (inizip()
) provavelmente consome muito mais tempo do que copiar uma tupla duas vezes. Eu estava usando Python 2.6.5, BTW.iters
é O (tamanho ^ 2), certo?Aqui está uma generalização que adiciona suporte para
step
,fillvalue
parâmetros:Ele gera
size
itens em pedaços de cada vez, rolandostep
posições por iteração preenchendo cada pedaço,fillvalue
se necessário. Exemplo parasize=4, step=3, fillvalue='*'
:Para um exemplo de caso de uso para o
step
parâmetro, consulte Processando um arquivo .txt grande em python com eficiência .fonte
Existe uma biblioteca que faz exatamente o que você precisa:
fonte
step=3
deve realmente ser removido para coincidir com o do OP pedido:list(more_itertools.windowed(range(6), 3))
Apenas uma contribuição rápida.
Como os documentos python atuais não têm "janela" nos exemplos de itertool (por exemplo, na parte inferior de http://docs.python.org/library/itertools.html ), aqui está um trecho baseado no código do agrupador que é um dos exemplos dados:
Basicamente, criamos uma série de iteradores fatiados, cada um com um ponto de partida um ponto à frente. Depois, fechamos juntos. Observe que essa função retorna um gerador (não é diretamente um gerador em si).
Assim como as versões de elemento anexador e iterador avançado acima, o desempenho (ou seja, o melhor) varia com o tamanho da lista e o tamanho da janela. Gosto desta porque é uma linha dupla (pode ser uma linha única, mas prefiro nomear conceitos).
Acontece que o código acima está errado . Funciona se o parâmetro passado para iterável for uma sequência, mas não se for um iterador. Se for um iterador, o mesmo iterador é compartilhado (mas não é tee'd) entre as chamadas islice e isso interrompe muito as coisas.
Aqui está um código fixo:
Além disso, mais uma versão para os livros. Em vez de copiar um iterador e avançar cópias várias vezes, esta versão faz cópias em pares de cada iterador à medida que avançamos a posição inicial. Portanto, o iterador t fornece ao iterador "completo" o ponto inicial em te também a base para a criação do iterador t + 1:
fonte
Apenas para mostrar como você pode combinar
itertools
receitas , estendo apairwise
receita o mais diretamente possível de volta àwindow
receita usando aconsume
receita:A
window
receita é a mesma que parapairwise
, apenas substitui o elemento único "consumir" notee
iterador secundário por um aumento progressivo do consumo nosn - 1
iteradores. Usar emconsume
vez de agrupar cada iteradorislice
é marginalmente mais rápido (para iteráveis suficientemente grandes), pois você paga apenas aislice
sobrecarga do agrupamento durante aconsume
fase, não durante o processo de extração de cada valor da janela (portanto, é limitado porn
, não pelo número de itens emiterable
)Em termos de desempenho, comparado a outras soluções, isso é muito bom (e melhor do que qualquer outra solução que eu testei à medida). Testado em Python 3.5.0, Linux x86-64, usando
ipython
%timeit
magia.kindall é a
deque
solução , aprimorada em termos de desempenho / correção usando emislice
vez de uma expressão de gerador rolada em casa e testando o comprimento resultante para que não produza resultados quando o iterável for mais curto que a janela, além de passar omaxlen
dadeque
posição em vez de por palavra-chave (faz uma diferença surpreendente para entradas menores):O mesmo que a solução padrão adaptada anterior, mas com cada
yield win
alteração alterada parayield tuple(win)
que o armazenamento de resultados do gerador funcione sem que todos os resultados armazenados sejam realmente uma visão do resultado mais recente (todas as outras soluções razoáveis são seguras nesse cenário) e adicionandotuple=tuple
à definição da função para mover uso detuple
doB
emLEGB
aoL
:consume
à base de água mostrada acima:Mesmo que
consume
, mas inliningelse
casoconsume
da chamada de função evitar en is None
teste para reduzir tempo de execução, especialmente para as pequenas entradas onde a sobrecarga de configuração é uma parte significativa do trabalho:(Observação: uma variante
pairwise
usadatee
com o argumento padrão 2 repetidamente para criartee
objetos aninhados , portanto, qualquer iterador específico é avançado apenas uma vez, não consumido independentemente um número crescente de vezes, semelhante à resposta de MrDrFenner é semelhante a não alinhadoconsume
e mais lento que o descritoconsume
em todos os testes, por isso omiti esses resultados por questões de brevidade).Como você pode ver, se você não se importa com a possibilidade de o chamador precisar armazenar resultados, minha versão otimizada da solução da kindall ganha na maioria das vezes, exceto no "caso grande iterável e pequeno do tamanho da janela" (onde o inline
consume
vence ); é degradado rapidamente à medida que o tamanho iterável aumenta, sem degradar à medida que o tamanho da janela aumenta (todas as outras soluções degradam mais lentamente para o aumento do tamanho iterável, mas também degradam para o aumento do tamanho da janela). Pode até ser adaptado para o caso "precisar de tuplas", envolvendomap(tuple, ...)
, que é um pouco mais lento do que colocar a tupla na função, mas é trivial (leva 1-5% a mais) e permite manter a flexibilidade de executar mais rapidamente quando você pode tolerar retornar repetidamente o mesmo valor.Se você precisar de segurança contra o armazenamento de retornos, o inline
consume
vence em todos os tamanhos de entrada, exceto os menores (com o não inlineconsume
sendo um pouco mais lento, mas com escala semelhante). Adeque
solução baseada em & tupling ganha apenas nos menores insumos, devido aos menores custos de instalação, e o ganho é pequeno; ele se degrada muito à medida que o iterável fica mais longo.Para o registro, a versão adaptada da solução da Kindall que
yield
stuple
s que usei foi:Abandone o armazenamento
tuple
em cache na linha de definição de função e o uso detuple
em cada umyield
para obter a versão mais rápida, mas menos segura.fonte
consume
é de uso geral (incluindo a capacidade de executar uma execução completaconsume
) e, portanto, precisa de uma importação extra e de um teste por uson is None
. No código real, se e somente se eu tivesse determinado que o desempenho era um problema ou se eu realmente precisasse de um código mais conciso, consideraria incluir oelse
caso doconsume
intowindow
, assumindo que não estava usandoconsume
para mais nada. Mas se o desempenho não for um problema, eu manteria as definições separadas; aconsume
função nomeada torna a operação menos mágica / auto-documentada.Eu uso o código a seguir como uma simples janela deslizante que usa geradores para aumentar drasticamente a legibilidade. Até agora, sua velocidade tem sido suficiente para uso em análises de sequências de bioinformática.
Eu o incluo aqui porque ainda não vi esse método. Novamente, não faço reivindicações sobre seu desempenho comparado.
fonte
len(sequence)
chamada. Isso não funcionará sesequence
for um iterador ou gerador. Quando a entrada cabe na memória, isso oferece uma solução mais legível do que com os iteradores.fonte
range(len
é um padrão ruim em python?uma versão ligeiramente modificada da janela deque, para torná-la uma verdadeira janela rolante. Para que ele comece a ser preenchido com apenas um elemento, aumente para o tamanho máximo da janela e diminua conforme a borda esquerda se aproxima do fim:
isto dá
fonte
Feito isso para uma função de média móvel
fonte
Por que não
Ele é documentado em Python doc . Você pode facilmente estendê-lo para uma janela maior.
fonte
Vários iteradores!
next(it)
aumentaStopIteration
quando a sequência é concluída e, por algum motivo interessante que está além de mim, a instrução yield aqui a excede e a função retorna, ignorando os valores restantes que não formam uma janela completa.Enfim, esta é a solução de menor número de linhas, mas cujo único requisito é que
seq
implemente um__iter__
ou outro__getitem__
e não dependa da solução da @ dansalmoitertools
oucollections
além dela :)fonte
Vamos torná-lo preguiçoso!
fonte
"" "
fonte
Testei algumas soluções e uma que eu achei e achei a que eu achei mais rápida, então pensei em compartilhar.
fonte
fonte
Que tal usar o seguinte:
Resultado:
fonte
Esta é uma pergunta antiga, mas para aqueles que ainda estão interessados, há uma ótima implementação de um controle deslizante de janela usando geradores nesta página (por Adrian Rosebrock).
É uma implementação para o OpenCV, mas você pode usá-lo facilmente para qualquer outro propósito. Para os mais ansiosos, colarei o código aqui, mas para entendê-lo melhor, recomendo visitar a página original.
Dica: Você pode verificar a
.shape
janela ao iterar o gerador para descartar aqueles que não atendem aos seus requisitosFelicidades
fonte
Resposta do DiPaolo modificada para permitir preenchimento arbitrário e tamanho de etapa variável
fonte
aqui está um forro. Eu cronometrei e é compatível com o desempenho da resposta principal e melhora progressivamente com seq maior de 20% mais lento com len (seq) = 20 e 7% mais lento com len (seq) = 10000
fonte
Tentando a minha parte, de maneira simples, simples e python usando o islice. Mas, pode não ser idealmente eficiente.
Explicação: Crie uma janela usando islice of window_size e itere esta operação usando o mapa em toda a matriz.
fonte
Função otimizada para deslizar dados da janela no Deep Learning
fonte