Ich brauche ein rollendes Fenster (auch bekannt als Schiebefenster), das über eine Sequenz / Iterator / Generator iteriert werden kann. Die Standard-Python-Iteration kann als Sonderfall betrachtet werden, bei dem die Fensterlänge 1 beträgt. Ich verwende derzeit den folgenden Code. Hat jemand eine pythonischere, weniger ausführliche oder effizientere Methode, um dies zu tun?
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()
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 .Antworten:
Es gibt eine in einer alten Version der Python-Dokumente mit
itertools
Beispielen :Der aus den Dokumenten ist etwas prägnanter und verwendet,
itertools
wie ich mir vorstellen kann, eine größere Wirkung.quelle
for elem in it
Schleife eintreten ?Dies scheint maßgeschneidert für a zu sein,
collections.deque
da Sie im Wesentlichen ein FIFO haben (an einem Ende hinzufügen, vom anderen entfernen). Selbst wenn Sie a verwendenlist
, sollten Sie nicht zweimal in Scheiben schneiden. Stattdessen sollten Sie wahrscheinlich nurpop(0)
aus der Liste undappend()
dem neuen Element.Hier ist eine optimierte Deque-basierte Implementierung, die Ihrem Original nachempfunden ist:
In meinen Tests schlägt es die meiste Zeit handlich alles andere, was hier gepostet wurde, obwohl die Pillmuncher-
tee
Version es für große Iterables und kleine Fenster schlägt. Bei größeren Fensterndeque
fährt der in rauer Geschwindigkeit wieder vorwärts.Der Zugriff auf einzelne Elemente in
deque
kann schneller oder langsamer sein als bei Listen oder Tupeln. (Elemente am Anfang sind schneller oder Elemente am Ende, wenn Sie einen negativen Index verwenden.) Ich habe ein Elementsum(w)
in den Körper meiner Schleife eingefügt. Dies spielt mit der Stärke der Deque (die Iteration von einem Gegenstand zum nächsten ist schnell, so dass diese Schleife 20% schneller lief als die nächstschnellste Methode, die Pillmuncher). Als ich es änderte, um Elemente in einem Zehnerfenster einzeln nachzuschlagen und hinzuzufügen, drehte sich der Spieß um und dietee
Methode war 20% schneller. Ich konnte etwas Geschwindigkeit wiederherstellen, indem ich negative Indizes für die letzten fünf Begriffe in der Addition verwendete, war abertee
immer noch etwas schneller. Insgesamt würde ich schätzen, dass beide für die meisten Anwendungen schnell genug sind. Wenn Sie etwas mehr Leistung benötigen, profilieren Sie und wählen Sie diejenige aus, die am besten funktioniert.quelle
yield win
sollte seinyield tuple(win)
oderyield list(win)
verhindern, dass ein Iterator von Verweisen auf dasselbedeque
Objekt zurückgegeben wird.pip install sliding_window
und ausführen mitfrom sliding_window import window
.list(window(range(10)))
dass Sie etwas wie [[0,1], [1,2], [2,3], ...] produzieren solltenlist(list(x) for x in window(range(10)))
oder das dem Iterator hinzufügen. Für einige Anwendungen wird dies von Bedeutung sein, für andere nicht, und da ich mich für Geschwindigkeit entschieden habe, habe ich mich nicht dafür entschieden und den Anrufer verpflichtet, das Fenster bei Bedarf zu kopieren.tuple()
Vorertrag zurückaddieren, hat diese Methode keinen Vorteil gegenüber den anderen.Ich mag
tee()
:gibt:
quelle
timeit
ist dies viel langsamer als das von Daniel DePaolo (etwa im Verhältnis 2: 1) und fühlt sich nicht viel "schöner" an.size
. Wenn Sie es erhöhen (z. B. wenn die Iterable 100000 Elemente lang ist, stellen Sie die Fenstergröße 1000 ein), wird möglicherweise eine Erhöhung angezeigt.iters
O (Größe!), Und ein mehrmaliges Aufrufennext()
(inizip()
) ist wahrscheinlich viel zeitaufwändiger als das zweimalige Kopieren eines Tupels. Ich habe übrigens Python 2.6.5 verwendet.iters
ist O (Größe ^ 2), oder?Hier ist eine Verallgemeinerung , die Unterstützung für fügt hinzu
step
,fillvalue
Parameter:Es liefert in Blöcken
size
Elemente zu einem Zeitpunkt, in dem diestep
Positionen pro Iteration rollt, wobeifillvalue
bei Bedarf jeder Block aufgefüllt wird. Beispiel fürsize=4, step=3, fillvalue='*'
:Ein Beispiel für einen Anwendungsfall für den
step
Parameter finden Sie unter Effizientes Verarbeiten einer großen TXT-Datei in Python .quelle
Es gibt eine Bibliothek, die genau das tut, was Sie brauchen:
quelle
step=3
sollte tatsächlich entfernt werden, um der Anforderung des OP zu entsprechen:list(more_itertools.windowed(range(6), 3))
Nur ein kurzer Beitrag.
Da die aktuellen Python-Dokumente in den itertool-Beispielen (dh am Ende von http://docs.python.org/library/itertools.html ) kein "Fenster" haben , ist hier ein Ausschnitt, der auf dem Code für grouper basiert ist eines der angegebenen Beispiele:
Grundsätzlich erstellen wir eine Reihe von in Scheiben geschnittenen Iteratoren, von denen jeder einen Startpunkt einen Punkt weiter vorne hat. Dann reißen wir diese zusammen. Beachten Sie, dass diese Funktion einen Generator zurückgibt (es ist nicht direkt ein Generator selbst).
Ähnlich wie bei den obigen Versionen für Anhängeelemente und fortschreitende Iteratoren variiert die Leistung (dh die beste) je nach Listengröße und Fenstergröße. Ich mag dieses, weil es ein Zweiliner ist (es könnte ein Einzeiler sein, aber ich bevorzuge Namenskonzepte).
Es stellt sich heraus, dass der obige Code falsch ist . Es funktioniert, wenn der an iterable übergebene Parameter eine Sequenz ist, nicht jedoch, wenn es sich um einen Iterator handelt. Wenn es sich um einen Iterator handelt, wird derselbe Iterator von den islice-Aufrufen gemeinsam genutzt (aber nicht abgeschlagen), und dies führt zu einer schlechten Funktionsweise.
Hier ist ein fester Code:
Auch noch eine Version für die Bücher. Anstatt einen Iterator zu kopieren und dann viele Male vorzurücken, erstellt diese Version paarweise Kopien jedes Iterators, während wir die Startposition vorwärts bewegen. Somit liefert der Iterator t sowohl den "vollständigen" Iterator mit dem Startpunkt bei t als auch die Basis zum Erstellen des Iterators t + 1:
quelle
Um zu zeigen, wie Sie
itertools
Rezepte kombinieren können , erweitere ich daspairwise
Rezept mit dem Rezept so direkt wie möglich wieder inwindow
dasconsume
Rezept:Das
window
Rezept ist das gleiche wie fürpairwise
, es ersetzt nur das einzelne Element "verbrauchen" auf dem zweitentee
Iterator durch progressiv steigende Verzehr aufn - 1
Iteratoren. Die Verwendung,consume
anstatt jeden Iterator einzuschließen,islice
ist geringfügig schneller (für ausreichend große Iterables), da Sie denislice
Umbruchaufwand nur während derconsume
Phase zahlen , nicht während des Extrahierens jedes Fensters (also wird er durchn
die 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
%timeit
Magie.Kindall ist die
deque
Lösung , gezwickt für die Leistung / Korrektheit durch die Verwendungislice
anstelle 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 dermaxlen
von derdeque
positions anstelle von nach Schlüsselwort (macht einen überraschenden Unterschied bei kleineren Eingaben):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) undtuple=tuple
zur Funktionsdefinition hinzugefügt werden um die Nutzung vontuple
vonB
inLEGB
nach zu verschiebenL
:consume
-basierte Lösung wie oben gezeigt:Gleich wie
consume
, aber inliningelse
Fallconsume
, um Funktionsaufruf undn is None
Test zu vermeiden , um die Laufzeit zu reduzieren, insbesondere für kleine Eingaben, bei denen der Einrichtungsaufwand ein bedeutender Teil der Arbeit ist:(Randnotiz: Eine Variante
pairwise
, dietee
mit dem Standardargument 2 wiederholt verschachteltetee
Objekte 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 inlineconsume
und langsamer alsconsume
bei 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
consume
gewinnt ); 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 werdenmap(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,
consume
gewinnt Inline bei allen bis auf die kleinsten Eingabegrößen (wobei Nicht-Inlineconsume
etwas langsamer ist, aber ähnlich skaliert). Die aufdeque
& 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
yield
stuple
s ich verwendet wurde:Löschen Sie das Caching
tuple
in der Funktionsdefinitionszeile und die Verwendung vontuple
in jedemyield
, um die schnellere, aber weniger sichere Version zu erhalten.quelle
consume
ist ein allgemeiner Zweck (einschließlich der Fähigkeit, einen vollständigenconsume
Vorgang durchzuführen) und erfordert daher einen zusätzlichen Import und einen Test pro Verwendung fürn is None
. Wenn ich im realen Code nur dann festgestellt hätte, dass die Leistung ein Problem darstellt, oder wenn ich wirklich präziseren Code benötige, würde ich in Betracht ziehen, denelse
Fallconsume
in zu integrierenwindow
, vorausgesetzt , ich würdeconsume
nichts anderes verwenden. Wenn sich jedoch nicht herausstellt, dass die Leistung ein Problem darstellt, würde ich die separaten Definitionen beibehalten. Die genannteconsume
Funktion macht den Vorgang weniger magisch / selbstdokumentierend.Ich verwende den folgenden Code als einfaches Schiebefenster, das Generatoren verwendet, um die Lesbarkeit drastisch zu verbessern. Nach meiner Erfahrung war seine Geschwindigkeit bisher ausreichend für die Verwendung in der Bioinformatik-Sequenzanalyse.
Ich füge es hier ein, weil ich diese Methode noch nicht gesehen habe. Auch hier mache ich keinen Anspruch auf die verglichene Leistung.
quelle
len(sequence)
Anruf. Dies funktioniert nicht, wennsequence
es sich um einen Iterator oder Generator handelt. Wenn die Eingabe in den Speicher passt, bietet dies eine besser lesbare Lösung als bei Iteratoren.quelle
range(len
Python ein schlechtes Muster ist?Eine leicht modifizierte Version des Deque-Fensters, um es zu einem echten Rolling Window zu machen. Damit es mit nur einem Element gefüllt wird, dann auf die maximale Fenstergröße anwächst und dann schrumpft, wenn der linke Rand sich dem Ende nähert:
das gibt
quelle
Gemacht für eine gleitende Durchschnittsfunktion
quelle
warum nicht
Es ist in Python doc dokumentiert . Sie können es leicht auf ein breiteres Fenster erweitern.
quelle
Mehrere Iteratoren!
next(it)
Raises ,StopIteration
wenn die Sequenz beendet, und aus irgendeinem kühlen Grunde , dass das über mich, die Ausbeute Aussage hier excepts es und die Funktion zurück, die übrig gebliebenen Werte ignorieren , die nicht mit einem vollen Fenster bilden.Wie auch immer, dies ist die bisher kleinste Lösung, deren einzige Anforderung darin besteht,
seq
entweder die Lösung von @ dansalmo zu implementieren__iter__
oder__getitem__
nichtitertools
oder nichtcollections
:)quelle
Lass es uns faul machen!
quelle
"" "
quelle
Ich habe ein paar Lösungen getestet und eine, die ich mir ausgedacht habe, und festgestellt, dass die, die ich mir ausgedacht habe, die schnellste ist, also dachte ich, ich würde sie teilen.
quelle
quelle
Wie wäre es mit folgendem:
Ausgabe:
quelle
Dies ist eine alte Frage, aber für diejenigen, die noch interessiert sind, gibt es auf dieser Seite eine großartige Implementierung eines Fensterschiebers mit Generatoren (von Adrian Rosebrock).
Es ist eine Implementierung für OpenCV, Sie können es jedoch problemlos für jeden anderen Zweck verwenden. Für die Eifrigen werde ich den Code hier einfügen, aber um ihn besser zu verstehen, empfehle ich, die Originalseite zu besuchen.
Tipp: Sie können
.shape
das Fenster beim Iterieren des Generators überprüfen , um diejenigen zu verwerfen, die Ihren Anforderungen nicht entsprechenProst
quelle
Die Antwort von DiPaolo wurde geändert , um eine beliebige Füllung und eine variable Schrittgröße zu ermöglichen
quelle
Hier ist ein Einzeiler. Ich habe es zeitlich festgelegt und es ist kompatibel mit der Leistung der Top-Antwort und wird mit größerer Sequenz von 20% langsamer mit len (seq) = 20 und 7% langsamer mit len (seq) = 10000 zunehmend besser
quelle
Ich versuche meinen Teil, einfach, einzeilig, pythonisch mit islice. Ist aber möglicherweise nicht optimal effizient.
Erläuterung: Erstellen Sie ein Fenster mit islice von window_size und wiederholen Sie diese Operation mithilfe von map over all array.
quelle
Optimierte Funktion zum Schieben von Fensterdaten in Deep Learning
quelle