Apenas para mostrar como você pode combinar itertoolsreceitas , estendo a pairwisereceita o mais diretamente possível de volta à windowreceita usando a consumereceita:
def consume(iterator, n):
"Advance the iterator n-steps ahead. If n is none, consume entirely."
# Use functions that consume iterators at C speed.
if n is None:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n), None)
def window(iterable, n=2):
"s -> (s0, ...,s(n-1)), (s1, ...,sn), (s2, ..., s(n+1)), ..."
iters = tee(iterable, n)
# Could use enumerate(islice(iters, 1, None), 1) to avoid consume(it, 0), but that's
# slower for larger window sizes, while saving only small fixed "noop" cost
for i, it in enumerate(iters):
consume(it, i)
return zip(*iters)
A windowreceita é a mesma que para pairwise, apenas substitui o elemento único "consumir" no teeiterador secundário por um aumento progressivo do consumo nos n - 1iteradores. Usar em consumevez de agrupar cada iterador isliceé marginalmente mais rápido (para iteráveis suficientemente grandes), pois você paga apenas a islicesobrecarga do agrupamento durante a consumefase, não durante o processo de extração de cada valor da janela (portanto, é limitado por n, não pelo número de itens em iterable)
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 %timeitmagia.
kindall é a dequesolução , aprimorada em termos de desempenho / correção usando em islicevez 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 o maxlenda dequeposição em vez de por palavra-chave (faz uma diferença surpreendente para entradas menores):
>>> %timeit -r5 deque(windowkindall(range(10), 3), 0)
100000 loops, best of 5: 1.87 μs per loop
>>> %timeit -r5 deque(windowkindall(range(1000), 3), 0)
10000 loops, best of 5: 72.6 μs per loop
>>> %timeit -r5 deque(windowkindall(range(1000), 30), 0)
1000 loops, best of 5: 71.6 μs per loop
O mesmo que a solução padrão adaptada anterior, mas com cada yield winalteração alterada para yield 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 adicionando tuple=tupleà definição da função para mover uso de tupledo Bem LEGBao L:
>>> %timeit -r5 deque(windowkindalltupled(range(10), 3), 0)
100000 loops, best of 5: 3.05 μs per loop
>>> %timeit -r5 deque(windowkindalltupled(range(1000), 3), 0)
10000 loops, best of 5: 207 μs per loop
>>> %timeit -r5 deque(windowkindalltupled(range(1000), 30), 0)
1000 loops, best of 5: 348 μs per loop
consumeà base de água mostrada acima:
>>> %timeit -r5 deque(windowconsume(range(10), 3), 0)
100000 loops, best of 5: 3.92 μs per loop
>>> %timeit -r5 deque(windowconsume(range(1000), 3), 0)
10000 loops, best of 5: 42.8 μs per loop
>>> %timeit -r5 deque(windowconsume(range(1000), 30), 0)
1000 loops, best of 5: 232 μs per loop
Mesmo que consume, mas inlining elsecaso consumeda chamada de função evitar e n is Noneteste para reduzir tempo de execução, especialmente para as pequenas entradas onde a sobrecarga de configuração é uma parte significativa do trabalho:
>>> %timeit -r5 deque(windowinlineconsume(range(10), 3), 0)
100000 loops, best of 5: 3.57 μs per loop
>>> %timeit -r5 deque(windowinlineconsume(range(1000), 3), 0)
10000 loops, best of 5: 40.9 μs per loop
>>> %timeit -r5 deque(windowinlineconsume(range(1000), 30), 0)
1000 loops, best of 5: 211 μs per loop
(Observação: uma variante pairwiseusada teecom o argumento padrão 2 repetidamente para criar teeobjetos 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 alinhado consumee mais lento que o descrito consumeem 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 consumevence ); é 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", envolvendo map(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 consumevence em todos os tamanhos de entrada, exceto os menores (com o não inline consumesendo um pouco mais lento, mas com escala semelhante). A dequesoluçã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 yields tuples que usei foi:
def windowkindalltupled(iterable, n=2, tuple=tuple):
it = iter(iterable)
win = deque(islice(it, n), n)
if len(win) < n:
return
append = win.append
yield tuple(win)
for e in it:
append(e)
yield tuple(win)
Abandone o armazenamento tupleem cache na linha de definição de função e o uso de tupleem cada um yieldpara obter a versão mais rápida, mas menos segura.
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 .