Ich möchte einen effizienten zirkulären Puffer in Python erstellen (mit dem Ziel, Durchschnittswerte der ganzzahligen Werte im Puffer zu ermitteln).
Ist dies eine effiziente Möglichkeit, eine Liste zum Sammeln von Werten zu verwenden?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
Was wäre effizienter (und warum)?
python
circular-buffer
jedierikb
quelle
quelle
Antworten:
Ich würde
collections.deque
mit einemmaxlen
arg verwendenIn den Dokumenten gibt es ein Rezept
deque
, das dem entspricht, was Sie möchten. Meine Behauptung, dass es das effizienteste ist, beruht ausschließlich auf der Tatsache, dass es in C von einer unglaublich erfahrenen Crew implementiert wird, die es sich zur Gewohnheit macht, erstklassigen Code herauszubringen.quelle
maxlen
definiert sind. O (n) ist verständlich, wenn diedeque
Dose bis ins Unendliche wachsen kann, aber wennmaxlen
gegeben, sollte die Indizierung eines Elements eine konstante Zeit sein.Wenn Sie vom Kopf einer Liste abspringen, wird die gesamte Liste kopiert und ist daher ineffizient
Sie sollten stattdessen eine Liste / ein Array mit fester Größe und einen Index verwenden, der sich beim Hinzufügen / Entfernen von Elementen durch den Puffer bewegt
quelle
Basierend auf der Antwort von MoonCactus ist hier eine
circularlist
Klasse. Der Unterschied zu seiner Version besteht darin, dass hierc[0]
immer das älteste angehängte Element angegeben wird.c[-1]
das zuletzt angehängte Element,c[-2]
das vorletzte ... angegeben wird. Dies ist für Anwendungen natürlicher.Klasse:
[Bearbeitet]: Optionaler
data
Parameter hinzugefügt , um die Initialisierung aus vorhandenen Listen zu ermöglichen, z.quelle
c[-1]
ist immer das richtige Element zu sehen.__getitem__
macht es richtig.Pythons Deque ist langsam. Sie können stattdessen auch numpy.roll verwenden. Wie drehen Sie die Zahlen in einem numpy-Array der Form (n,) oder (n, 1)?
In diesem Benchmark beträgt die Deque 448 ms. Numpy.roll ist 29ms http://scimusing.wordpress.com/2013/10/25/ring-buffers-in-pythonnumpy/
quelle
numpy.roll
gibt eine Kopie des Arrays zurück, oder?ok mit der Verwendung der Deque-Klasse, aber für die Anforderungen der Frage (Durchschnitt) ist dies meine Lösung:
quelle
TypeError: 'numpy.float64' object is not callable
beim Versuch,average
Methodecollections
ist Teil der Standardbibliothek,numpy
nicht. Abhängigkeiten von Bibliotheken von Drittanbietern würden zu einer schrecklichen Standardbibliothek führen.Obwohl es hier bereits viele gute Antworten gibt, konnte ich keinen direkten Vergleich der Timings für die genannten Optionen finden. Deshalb finden Sie unten meinen bescheidenen Versuch eines Vergleichs.
Nur zu Testzwecken kann die Klasse zwischen einem
list
Puffer auf Basis, einemcollections.deque
Puffer auf Basis und einemNumpy.roll
Puffer auf Basis wechseln .Beachten Sie, dass die
update
Methode jeweils nur einen Wert hinzufügt, um dies einfach zu halten.Auf meinem System ergibt dies:
quelle
Wie wäre es mit der Lösung aus dem Python-Kochbuch , einschließlich einer Neuklassifizierung der Ringpufferinstanz, wenn sie voll ist?
Bildnachweis: Sébastien Keim
quelle
Sie können auch dieses ziemlich alte Python-Rezept sehen .
Hier ist meine eigene Version mit NumPy-Array:
quelle
O(n)
Zeit. Um einen richtigen Umlaufpuffer zu implementieren , sollten Sie sowohl einen Index als auch eine Größenvariable haben und den Fall korrekt behandeln, wenn die Daten das Ende des Puffers "umschließen". Beim Abrufen von Daten müssen Sie möglicherweise zwei Abschnitte am Anfang und Ende des Puffers verketten.Dieser benötigt keine Bibliothek. Es wird eine Liste erstellt und anschließend ein Index erstellt.
Der Footprint ist sehr klein (keine Bibliothek) und läuft mindestens doppelt so schnell wie die Warteschlange. Dies ist in der Tat gut, um gleitende Durchschnitte zu berechnen. Beachten Sie jedoch, dass die Elemente nicht wie oben nach Alter sortiert sind.
Um den Durchschnittswert zu erhalten, z.
Ergebnisse in:
Dies ist ungefähr 1/3 der Zeit des Äquivalents mit Warteschlange.
quelle
__getitem__
nicht ein bisschen mächtiger sein :self._data[(key + self._index + 1) % self._size]
?Ich hatte dieses Problem vor der seriellen Programmierung. Zu der Zeit vor etwas mehr als einem Jahr konnte ich auch keine effizienten Implementierungen finden, so dass ich eine als C-Erweiterung schrieb und sie auch auf pypi unter einer MIT-Lizenz verfügbar ist . Es ist super einfach, verarbeitet nur Puffer mit 8-Bit-Zeichen mit Vorzeichen, hat jedoch eine flexible Länge, sodass Sie Struct oder etwas darüber verwenden können, wenn Sie etwas anderes als Zeichen benötigen. Ich sehe jetzt mit einer Google-Suche, dass es heutzutage jedoch mehrere Optionen gibt, also sollten Sie sich diese auch ansehen.
quelle
Ihre Antwort ist nicht richtig. Circular Buffer Main haben zwei Prinzipien ( https://en.wikipedia.org/wiki/Circular_buffer )
Ihr Code unten:
Betrachten wir eine Situation, in der die Liste voll ist, indem Sie Ihren Code verwenden:
Jetzt fügen wir 6 hinzu, die Liste wird in geändert
Die Elemente erwarten, dass 1 in der Liste ihre Position geändert hat
Ihr Code ist eine Warteschlange, kein Kreispuffer.
Die Antwort von Basj ist meiner Meinung nach die effizienteste.
Übrigens kann ein Kreispuffer die Leistung der Operation zum Hinzufügen eines Elements verbessern.
quelle
Von Github:
https://github.com/heineman/python-data-structures/blob/master/2.%20Ubiquitous%20Lists/circBuffer.py
quelle
Die ursprüngliche Frage war: " effizienter " Ringpuffer. Entsprechend dieser geforderten Effizienz scheint die Antwort von aaronasterling definitiv richtig zu sein. Die Verwendung einer in Python programmierten dedizierten Klasse und der Vergleich der Zeitverarbeitung mit collection.deque zeigt eine x5,2-fache Beschleunigung mit deque! Hier ist ein sehr einfacher Code, um dies zu testen:
Um eine Deque in eine Liste umzuwandeln, verwenden Sie einfach:
Sie erhalten dann O (1) zufälligen Zugriff auf die Deque-Elemente. Dies ist natürlich nur dann von Nutzen, wenn Sie nach einmaligem Einstellen viele zufällige Zugriffe auf die Deque vornehmen müssen.
quelle
Dies wendet dasselbe Prinzip auf einige Puffer an, die die neuesten Textnachrichten enthalten sollen.
quelle
Sie können diesen zirkulären Puffer basierend auf einem vordefinierten numpy-Array mit Größe auschecken. Die Idee ist, dass Sie einen Puffer erstellen (Speicher für das numpy-Array zuweisen) und ihn später anhängen. Das Einfügen und Abrufen von Daten erfolgt sehr schnell. Ich habe dieses Modul für einen ähnlichen Zweck erstellt, wie Sie es benötigen. In meinem Fall habe ich ein Gerät, das ganzzahlige Daten generiert. Ich lese die Daten und lege sie für zukünftige Analysen und Verarbeitungen in den Umlaufpuffer.
quelle