Solo per mostrare come combinare le itertoolsricette , sto estendendo la pairwisericetta il più direttamente possibile nella windowricetta usando la consumericetta:
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)
La windowricetta è la stessa di pairwise, sostituisce semplicemente il singolo elemento "consuma" sul secondo teeiteratore con consumi progressivamente crescenti sugli n - 1iteratori. L'utilizzo consumeanziché il wrapping di ogni iteratore isliceè leggermente più veloce (per iterable sufficientemente grandi) poiché si paga solo il islicesovraccarico di wrapping durante la consumefase, non durante il processo di estrazione di ogni valore con finestra (quindi è limitato n, non dal numero di elementi in iterable).
Per quanto riguarda le prestazioni, rispetto ad alcune altre soluzioni, questo è abbastanza buono (e migliore di qualsiasi altra soluzione che ho testato in scala). Testato su Python 3.5.0, Linux x86-64, usando la ipython %timeitmagia.
kindall è la dequesoluzione , ottimizzata per le prestazioni / correttezza utilizzando isliceinvece di un'espressione del generatore home-roll e testando la lunghezza risultante in modo che non produca risultati quando l'iterabile è più corto della finestra, oltre a passare il maxlendel dequeposizionalmente anziché per parola chiave (fa una differenza sorprendente per input più piccoli):
>>> %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
Come la precedente soluzione kindall adattata, ma con ciascuna yield winmodifica in yield tuple(win)modo da memorizzare i risultati dal generatore funziona senza che tutti i risultati memorizzati siano realmente una vista del risultato più recente (tutte le altre soluzioni ragionevoli sono sicure in questo scenario) e si aggiungono tuple=tuplealla definizione della funzione per spostare l'uso di tupleda Bin LEGBa 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
consumesoluzione di base mostrata sopra:
>>> %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
Uguale consume, ma inlining elsecaso consumedi chiamata di funzione evita e n is Noneprova per ridurre runtime, particolarmente per piccoli ingressi dove l'overhead configurazione è una parte significativa del lavoro:
>>> %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
(Nota a margine: una variante pairwiseche utilizza teeripetutamente l'argomento 2 per creare teeoggetti nidificati , quindi ogni dato iteratore viene avanzato solo una volta, non consumato in modo indipendente un numero crescente di volte, simile alla risposta di MrDrFenner è simile a non in linea consumee più lento di quello integrato consumein tutti i test, quindi ho omesso quei risultati per brevità).
Come puoi vedere, se non ti interessa la possibilità che il chiamante abbia bisogno di memorizzare i risultati, la mia versione ottimizzata della soluzione di kindall vince la maggior parte del tempo, tranne nel "caso iterabile grande, di dimensioni ridotte" (dove consumevince la linea ); si degrada rapidamente all'aumentare della dimensione iterabile, mentre non diminuisce affatto all'aumentare della dimensione della finestra (ogni altra soluzione si degrada più lentamente all'aumentare della dimensione iterabile, ma diminuisce anche all'aumentare della dimensione della finestra). Può anche essere adattato al caso "bisogno di tuple" avvolgendolo map(tuple, ...), che funziona in modo leggermente più lento rispetto al mettere la tupling nella funzione, ma è banale (richiede 1-5% in più) e ti consente di mantenere la flessibilità di correre più veloce quando puoi tollerare ripetutamente la restituzione dello stesso valore.
Se hai bisogno di sicurezza contro la memorizzazione dei ritorni, inline consumevince su tutti tranne le dimensioni di input più piccole (con non inline consumeleggermente più lento ma ridimensionamento in modo simile). La dequesoluzione basata su & tupling vince solo per gli input più piccoli, a causa di minori costi di installazione e il guadagno è ridotto; si degrada male quando l'iterabile si allunga.
Per la cronaca, la versione adattata della soluzione di Kindall che yields tuples ho usato è stato:
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)
Eliminare la memorizzazione nella cache tuplenella riga di definizione della funzione e l'uso di tuplein ciascuna yieldper ottenere la versione più veloce ma meno sicura.
sum()Omax()), vale la pena ricordare che esistono algoritmi efficienti per calcolare il nuovo valore per ogni finestra in tempo costante (indipendentemente dalle dimensioni della finestra). Ho raccolto alcuni di questi algoritmi insieme in una libreria Python: rolling .