Um zu zeigen, wie Sie itertoolsRezepte kombinieren können , erweitere ich das pairwiseRezept mit dem Rezept so direkt wie möglich wieder in windowdas consumeRezept:
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)
Das windowRezept ist das gleiche wie für pairwise, es ersetzt nur das einzelne Element "verbrauchen" auf dem zweiten teeIterator durch progressiv steigende Verzehr auf n - 1Iteratoren. Die Verwendung, consumeanstatt jeden Iterator einzuschließen, isliceist geringfügig schneller (für ausreichend große Iterables), da Sie den isliceUmbruchaufwand nur während der consumePhase zahlen , nicht während des Extrahierens jedes Fensters (also wird er durch ndie Anzahl der Elemente begrenzt) iterable).
In Bezug auf die Leistung ist dies im Vergleich zu einigen anderen Lösungen ziemlich gut (und besser als alle anderen Lösungen, die ich im Maßstab getestet habe). Getestet unter Python 3.5.0, Linux x86-64, mit ipython %timeitMagie.
Kindall ist die dequeLösung , gezwickt für die Leistung / Korrektheit durch die Verwendung isliceanstelle eines selbstgerollter Generator Expression und Testen der resultierenden Länge , so dass es keine Ergebnisse liefern , wenn die iterable kürzer als das Fenster ist, sowie die Weitergabe der maxlenvon der dequepositions anstelle von nach Schlüsselwort (macht einen überraschenden Unterschied bei kleineren Eingaben):
>>> %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
Wie die zuvor angepasste Kindall-Lösung, jedoch mit jeder yield winÄnderung, yield tuple(win)sodass das Speichern der Ergebnisse vom Generator funktioniert, ohne dass alle gespeicherten Ergebnisse wirklich eine Ansicht des neuesten Ergebnisses sind (alle anderen vernünftigen Lösungen sind in diesem Szenario sicher) und tuple=tuplezur Funktionsdefinition hinzugefügt werden um die Nutzung von tuplevon Bin LEGBnach zu verschieben 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-basierte Lösung wie oben gezeigt:
>>> %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
Gleich wie consume, aber inlining elseFall consume, um Funktionsaufruf und n is NoneTest zu vermeiden , um die Laufzeit zu reduzieren, insbesondere für kleine Eingaben, bei denen der Einrichtungsaufwand ein bedeutender Teil der Arbeit ist:
>>> %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
(Randnotiz: Eine Variante pairwise, die teemit dem Standardargument 2 wiederholt verschachtelte teeObjekte erstellt, sodass jeder Iterator nur einmal vorgerückt und nicht immer häufiger unabhängig verwendet wird, ähnlich wie die Antwort von MrDrFenner, ähnelt der von nicht inline consumeund langsamer als consumebei allen Tests angegeben, daher habe ich diese Ergebnisse der Kürze halber weggelassen.
Wie Sie sehen können, gewinnt meine optimierte Version der kindall-Lösung die meiste Zeit , wenn Sie sich nicht für die Möglichkeit interessieren, dass der Anrufer Ergebnisse speichern muss, außer im Fall "großer iterierbarer kleiner Fenstergröße" (wo Inline consumegewinnt ); es verschlechtert sich schnell, wenn die iterierbare Größe zunimmt, während es sich mit zunehmender Fenstergröße überhaupt nicht verschlechtert (jede andere Lösung verschlechtert sich langsamer, wenn die iterierbare Größe zunimmt, verschlechtert sich jedoch auch, wenn die Fenstergröße zunimmt). Es kann sogar durch Einwickeln an den Fall "Bedarfstupel" angepasst werden map(tuple, ...), der etwas langsamer läuft als das Einfügen des Tupels in die Funktion, aber es ist trivial (dauert 1-5% länger) und ermöglicht es Ihnen, die Flexibilität des schnelleren Laufens beizubehalten wenn Sie es tolerieren können, wiederholt denselben Wert zurückzugeben.
Wenn Sie Sicherheit gegen das Speichern von Retouren benötigen, consumegewinnt Inline bei allen bis auf die kleinsten Eingabegrößen (wobei Nicht-Inline consumeetwas langsamer ist, aber ähnlich skaliert). Die auf deque& tupling basierende Lösung gewinnt aufgrund geringerer Einrichtungskosten nur für die kleinsten Eingaben, und der Gewinn ist gering. es verschlechtert sich stark, wenn das iterable länger wird.
Für die Aufzeichnung der angepassten Version der Lösung des Kindall dass yields tuples ich verwendet wurde:
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)
Löschen Sie das Caching tuplein der Funktionsdefinitionszeile und die Verwendung von tuplein jedem yield, um die schnellere, aber weniger sichere Version zu erhalten.
sum()odermax()), sollten Sie berücksichtigen, dass es effiziente Algorithmen gibt, mit denen der neue Wert für jedes Fenster in konstanter Zeit (unabhängig von der Fenstergröße) berechnet werden kann . Ich habe einige dieser Algorithmen in einer Python-Bibliothek zusammengefasst: Rolling .