Gibt es eine SciPy-Funktion oder eine NumPy-Funktion oder ein Modul für Python, die den laufenden Mittelwert eines 1D-Arrays in einem bestimmten Fenster berechnet?
python
numpy
scipy
moving-average
Shejo284
quelle
quelle
UPD: Alleo und jasaarim haben effizientere Lösungen vorgeschlagen .
Sie können dafür verwenden
np.convolve
:Erläuterung
Der laufende Mittelwert ist ein Fall der mathematischen Operation der Faltung . Für den laufenden Mittelwert schieben Sie ein Fenster entlang der Eingabe und berechnen den Mittelwert des Fensterinhalts. Für diskrete 1D-Signale ist die Faltung dasselbe, außer dass Sie anstelle des Mittelwerts eine beliebige lineare Kombination berechnen, dh jedes Element mit einem entsprechenden Koeffizienten multiplizieren und die Ergebnisse addieren. Diese Koeffizienten, einer für jede Position in dem Fenster, sind manchmal die Faltung genannt Kernel . Das arithmetische Mittel der N-Werte ist
(x_1 + x_2 + ... + x_N) / N
also der entsprechende Kernel(1/N, 1/N, ..., 1/N)
, und genau das erhalten wir, wenn wir es verwendennp.ones((N,))/N
.Kanten
Das
mode
Argument vonnp.convolve
gibt an, wie mit den Kanten umgegangen werden soll. Ich habe denvalid
Modus hier gewählt, weil ich denke, dass die meisten Leute erwarten, dass das Laufen so funktioniert, aber Sie haben möglicherweise andere Prioritäten. Hier ist ein Diagramm, das den Unterschied zwischen den Modi veranschaulicht:quelle
numpy.cumsum
ist jedoch komplexer.Effiziente Lösung
Faltung ist viel besser als einfacher Ansatz, aber (ich denke) sie verwendet FFT und ist daher ziemlich langsam. Speziell für die Berechnung des laufenden Mittelwerts funktioniert der folgende Ansatz jedoch einwandfrei
Der zu überprüfende Code
Beachten Sie das
numpy.allclose(result1, result2)
istTrue
, sind zwei Verfahren äquivalent. Je größer N, desto größer der Zeitunterschied.Warnung: Obwohl Cumsum schneller ist, tritt ein erhöhter Gleitkommafehler auf, der dazu führen kann, dass Ihre Ergebnisse ungültig / falsch / inakzeptabel sind
Die Kommentare wiesen hier auf dieses Problem mit Gleitkommafehlern hin, aber ich mache es hier in der Antwort deutlicher. .
np.longdouble
aber Ihr Gleitkommafehler wird für eine relativ große Anzahl von Punkten immer noch signifikant (um> 1e5, hängt jedoch von Ihren Daten ab).quelle
numpy.convolve
O (mn); In den Dokumenten wird erwähnt, dassscipy.signal.fftconvolve
FFT verwendet wird.running_mean([1,2,3], 2)
gibtarray([1, 2])
. Ersetzenx
durch[float(value) for value in x]
macht den Trick.x
Schwimmer enthält. Beispiel:running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
kehrt zurück,0.003125
während man erwartet0.0
. Weitere Informationen: en.wikipedia.org/wiki/Loss_of_significanceUpdate: Das folgende Beispiel zeigt die alte
pandas.rolling_mean
Funktion, die in neueren Versionen von Pandas entfernt wurde. Ein modernes Äquivalent des folgenden Funktionsaufrufs wärepandas ist dafür besser geeignet als NumPy oder SciPy. Seine Funktion rolling_mean erledigt die Arbeit bequem. Es gibt auch ein NumPy-Array zurück, wenn die Eingabe ein Array ist.
Es ist schwer, die
rolling_mean
Leistung mit einer benutzerdefinierten reinen Python-Implementierung zu übertreffen . Hier ist ein Beispiel für eine Leistung gegenüber zwei der vorgeschlagenen Lösungen:Es gibt auch gute Möglichkeiten, mit den Kantenwerten umzugehen.
quelle
df.rolling(windowsize).mean()
funktioniert jetzt stattdessen (sehr schnell, könnte ich hinzufügen). für 6000 Zeilenreihe%timeit test1.rolling(20).mean()
zurück 1000 Schlaufen, am besten von 3: 1,16 ms pro Loopdf.rolling()
funktioniert gut genug, das Problem ist, dass selbst dieses Formular ndarrays in Zukunft nicht mehr unterstützt. Um es zu verwenden, müssen wir zuerst unsere Daten in einen Pandas-Datenrahmen laden. Ich würde gerne sehen, dass diese Funktion entwedernumpy
oder hinzugefügt wirdscipy.signal
.%timeit bottleneck.move_mean(x, N)
ist 3 bis 15 mal schneller als die Cumsum- und Pandas-Methoden auf meinem PC. Schauen Sie sich ihren Benchmark in der README des Repos an .Sie können einen laufenden Mittelwert berechnen mit:
Aber es ist langsam.
Glücklicherweise beinhaltet Numpy eine Faltung der wir die Dinge beschleunigen können. Der laufende Mittelwert entspricht der Faltung
x
mit einem Vektor, derN
lang ist und bei dem alle Mitglieder gleich sind1/N
. Die numpy-Implementierung von convolve enthält den Starttransienten, sodass Sie die ersten N-1-Punkte entfernen müssen:Auf meinem Computer ist die schnelle Version 20 bis 30 Mal schneller, abhängig von der Länge des Eingabevektors und der Größe des Mittelungsfensters.
Beachten Sie, dass Convolve einen
'same'
Modus enthält, der das vorübergehende Startproblem zu beheben scheint, ihn jedoch zwischen Anfang und Ende aufteilt.quelle
mode='valid'
beiconvolve
der keine Nachbearbeitung erforderlich ist.mode='valid'
entfernt den Übergang von beiden Enden, richtig? Wennlen(x)=10
undN=4
, für einen laufenden Mittelwert würde ich 10 Ergebnisse wollen, abervalid
7 zurückgeben.modes = ('full', 'same', 'valid'); [plot(convolve(ones((200,)), ones((50,))/50, mode=m)) for m in modes]; axis([-10, 251, -.1, 1.1]); legend(modes, loc='lower center')
anzuzeigen : (mit importiertem Pyplot und Numpy).runningMean
Habe ich Nebeneffekt der Mittelung mit Nullen, wenn Sie das Array mitx[ctr:(ctr+N)]
für die rechte Seite des Arrays verlassen.runningMeanFast
haben auch dieses Randeffektproblem.Bei meinen Tests bei Tradewave.net gewinnt TA-lib immer:
Ergebnisse:
quelle
NameError: name 'info' is not defined
. Ich erhalte diesen Fehler, Sir.Eine sofort einsatzbereite Lösung finden Sie unter https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Es liefert den laufenden Durchschnitt mit dem
flat
Fenstertyp. Beachten Sie, dass dies etwas ausgefeilter ist als die einfache Do-it-yourself-Convolve-Methode, da versucht wird, die Probleme am Anfang und am Ende der Daten durch Reflektion zu behandeln (was in Ihrem Fall möglicherweise funktioniert oder nicht). ..).Zunächst könnten Sie versuchen:
quelle
numpy.convolve
dem Unterschied nur in der Änderung der Reihenfolge.w
die Fenstergröße unds
die Daten?Sie können scipy.ndimage.filters.uniform_filter1d verwenden :
uniform_filter1d
::'reflect'
der Standard ist, aber in meinem Fall wollte ich lieber'nearest'
Es ist auch ziemlich schnell (fast 50-mal schneller als
np.convolve
und 2-5-mal schneller als der oben angegebene Cumsum-Ansatz ):Hier sind 3 Funktionen, mit denen Sie Fehler / Geschwindigkeit verschiedener Implementierungen vergleichen können:
quelle
uniform_filter1d
,np.convolve
mit einem Rechteck undnp.cumsum
anschließendnp.subtract
. Meine Ergebnisse: (1.) Faltung ist die langsamste. (2.) Cumsum / Subtrahieren ist ungefähr 20-30x schneller. (3.) uniform_filter1d ist ungefähr 2-3x schneller als cumsum / subtrahieren. Gewinner ist definitiv uniform_filter1d.uniform_filter1d
ist schneller als diecumsum
Lösung (um etwa 2-5x). unduniform_filter1d
erhält keinen massiven Gleitkommafehler wie diecumsum
Lösung.Ich weiß, dass dies eine alte Frage ist, aber hier ist eine Lösung, die keine zusätzlichen Datenstrukturen oder Bibliotheken verwendet. Die Anzahl der Elemente in der Eingabeliste ist linear, und ich kann mir keinen anderen Weg vorstellen, um sie effizienter zu gestalten (wenn jemand einen besseren Weg zur Zuordnung des Ergebnisses kennt, lassen Sie es mich bitte wissen).
HINWEIS: Dies wäre viel schneller, wenn ein Numpy-Array anstelle einer Liste verwendet würde, aber ich wollte alle Abhängigkeiten beseitigen. Es wäre auch möglich, die Leistung durch Multithread-Ausführung zu verbessern
Die Funktion setzt voraus, dass die Eingabeliste eindimensional ist. Seien Sie also vorsichtig.
Beispiel
Angenommen, wir haben eine Liste,
data = [ 1, 2, 3, 4, 5, 6 ]
für die wir einen gleitenden Mittelwert mit einer Periode von 3 berechnen möchten, und Sie möchten auch eine Ausgabeliste, die dieselbe Größe wie die Eingabeliste hat (dies ist meistens der Fall).Das erste Element hat den Index 0, daher sollte der rollierende Mittelwert für die Elemente Index -2, -1 und 0 berechnet werden. Offensichtlich haben wir keine Daten [-2] und Daten [-1] (es sei denn, Sie möchten spezielle verwenden Randbedingungen), daher nehmen wir an, dass diese Elemente 0 sind. Dies entspricht dem Auffüllen der Liste mit Null, außer wir füllen sie nicht auf, sondern verfolgen nur die Indizes, die aufgefüllt werden müssen (von 0 bis N-1).
Für die ersten N Elemente addieren wir also immer wieder die Elemente in einem Akkumulator.
Ab den Elementen N + 1 funktioniert eine einfache Akkumulation nicht. Wir erwarten,
result[3] = (2 + 3 + 4)/3 = 3
aber das ist anders als(sum + 4)/3 = 3.333
.Die Art und Weise des richtigen Wert zu berechnen ist zu subtrahieren
data[0] = 1
aussum+4
, so gebensum + 4 - 1 = 9
.Dies geschieht, weil derzeit
sum = data[0] + data[1] + data[2]
, aber es gilt auch für jeden,i >= N
weil vor der Subtraktionsum
istdata[i-N] + ... + data[i-2] + data[i-1]
.quelle
Ich bin der Meinung, dass dies durch Engpässe elegant gelöst werden kann
Siehe Basisbeispiel unten:
"mm" ist das gleitende Mittel für "a".
"Fenster" ist die maximale Anzahl von Einträgen, die für den gleitenden Mittelwert berücksichtigt werden müssen.
"min_count" ist die minimale Anzahl von Einträgen, die für den gleitenden Mittelwert berücksichtigt werden müssen (z. B. für die ersten paar Elemente oder wenn das Array Nanowerte hat).
Der gute Teil ist, dass Engpass beim Umgang mit Nanowerten hilft und auch sehr effizient ist.
quelle
Ich habe noch nicht überprüft, wie schnell dies ist, aber Sie könnten versuchen:
quelle
Diese Antwort enthält Lösungen, die die Python- Standardbibliothek für drei verschiedene Szenarien verwenden.
Laufender Durchschnitt mit
itertools.accumulate
Dies ist eine speichereffiziente Python 3.2+ -Lösung, die den laufenden Durchschnitt über eine iterierbare Anzahl von Werten durch Nutzung berechnet
itertools.accumulate
.Beachten Sie, dass
values
dies beliebig iterierbar sein kann, einschließlich Generatoren oder anderer Objekte, die im laufenden Betrieb Werte erzeugen.Konstruieren Sie zunächst träge die kumulative Summe der Werte.
Als nächstes
enumerate
die kumulative Summe (beginnend bei 1) und konstruieren Sie einen Generator, der den Bruchteil der akkumulierten Werte und den aktuellen Aufzählungsindex liefert.Sie können Probleme haben,
means = list(rolling_avg)
wenn Sie alle Werte gleichzeitig im Speicher benötigen odernext
inkrementell aufrufen .(Natürlich können Sie auch iterieren
rolling_avg
mit einerfor
Schleife , dienext
implizit aufgerufen wird .)Diese Lösung kann wie folgt als Funktion geschrieben werden.
Eine Coroutine, an die Sie jederzeit Werte senden können
Diese Coroutine verwendet die von Ihnen gesendeten Werte und führt einen laufenden Durchschnitt der bisher angezeigten Werte.
Dies ist nützlich, wenn Sie keine iterierbaren Werte haben, sondern die zu ermittelenden Werte zu unterschiedlichen Zeiten während des gesamten Programmlebens einzeln erfassen.
Die Coroutine funktioniert folgendermaßen:
Berechnung des Durchschnitts über ein Schiebefenster von Größe
N
Diese Generatorfunktion nimmt eine iterierbare und eine Fenstergröße an
N
und liefert den Durchschnitt über die aktuellen Werte innerhalb des Fensters. Esdeque
wird eine Datenstruktur verwendet , die einer Liste ähnelt, jedoch für schnelle Änderungen (pop
,append
) an beiden Endpunkten optimiert ist .Hier ist die Funktion in Aktion:
quelle
Ein bisschen spät zur Party, aber ich habe meine eigene kleine Funktion gemacht, die sich NICHT um die Enden oder Pads mit Nullen wickelt, die dann auch verwendet werden, um den Durchschnitt zu finden. Ein weiterer Vorteil ist, dass das Signal auch an linear beabstandeten Punkten erneut abgetastet wird. Passen Sie den Code nach Belieben an, um weitere Funktionen zu erhalten.
Die Methode ist eine einfache Matrixmultiplikation mit einem normalisierten Gaußschen Kernel.
Eine einfache Verwendung eines sinusförmigen Signals mit zusätzlichem normalverteilten Rauschen:
quelle
sum
. 2 Der Operator (keine Ahnung, was das ist) gibt einen Fehler aus. Ich kann es später untersuchen, aber mir fehlt gerade die Zeitnp.sum
@
@
ist die Matrix Multiplikationsoperator , der Arbeitsgeräte np.matmul . Überprüfen Sie, ob Ihry_in
Array ein Numpy-Array ist. Dies könnte das Problem sein.Anstelle von Numpy oder Scipy würde ich Pandas empfehlen, dies schneller zu tun:
Dies nimmt den gleitenden Durchschnitt (MA) von 3 Perioden der Spalte "Daten". Sie können auch die verschobenen Versionen berechnen. Beispielsweise kann die Version, die die aktuelle Zelle ausschließt (eine zurück verschoben), einfach wie folgt berechnet werden:
quelle
pandas.rolling_mean
während meine verwendetpandas.DataFrame.rolling
. Sie können auch die Bewegungmin(), max(), sum()
usw. sowiemean()
mit dieser Methode einfach berechnen.pandas.rolling_min, pandas.rolling_max
usw. verwenden. Sie sind ähnlich, aber unterschiedlich.In einer der obigen Antworten ist ein Kommentar von mab vergraben, der diese Methode enthält. hat was ist ein einfacher gleitender Durchschnitt:
bottleneck
move_mean
min_count
ist ein praktischer Parameter, mit dem der gleitende Durchschnitt bis zu diesem Punkt in Ihrem Array berechnet wird. Wenn Sie nicht setzenmin_count
, wird es gleich seinwindow
und alles bis zuwindow
Punkten wird seinnan
.quelle
Ein anderer Ansatz, um einen gleitenden Durchschnitt zu finden, ohne Numpy zu verwenden, Panda
druckt [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]
quelle
Diese Frage ist jetzt noch älter als als NeXuS letzten Monat darüber schrieb, ABER ich mag, wie sein Code mit Randfällen umgeht. Da es sich jedoch um einen "einfachen gleitenden Durchschnitt" handelt, bleiben seine Ergebnisse hinter den Daten zurück, für die sie gelten. Ich dachte , dass in einer befriedigenden Weise als NumPy der Modi mit Randfällen zu tun
valid
,same
undfull
konnte durch Anwendung einen ähnlichen Ansatzes zu einem erreicht werdenconvolution()
basierter Methode.In meinem Beitrag wird ein zentraler laufender Durchschnitt verwendet, um die Ergebnisse an den Daten auszurichten. Wenn zu wenige Punkte verfügbar sind, um das Fenster in voller Größe zu verwenden, werden laufende Durchschnittswerte aus sukzessive kleineren Fenstern an den Rändern des Arrays berechnet. [Eigentlich aus immer größeren Fenstern, aber das ist ein Implementierungsdetail.]
Es ist relativ langsam, weil es verwendet
convolve()
wird und wahrscheinlich von einem echten Pythonisten ziemlich aufgepeppt werden könnte, aber ich glaube, dass die Idee steht.quelle
Es gibt oben viele Antworten zur Berechnung eines laufenden Mittelwerts. Meine Antwort fügt zwei zusätzliche Funktionen hinzu:
Diese zweite Funktion ist besonders nützlich, um zu bestimmen, welche Werte um einen bestimmten Betrag vom allgemeinen Trend abweichen.
Ich benutze numpy.cumsum, da es die zeiteffizienteste Methode ist ( siehe Alleos Antwort oben ).
Dieser Code funktioniert nur für Ns. Sie kann für ungerade Zahlen angepasst werden, indem Sie den np.insert von padded_x und n_nan ändern.
Beispielausgabe (roh in schwarz, movavg in blau):
Dieser Code kann leicht angepasst werden, um alle gleitenden Durchschnittswerte zu entfernen, die aus weniger als Cutoff = 3 Nicht-Nan-Werten berechnet wurden.
quelle
Nur Python-Standardbibliothek verwenden (speichereffizient)
Geben Sie einfach eine andere Version der Verwendung nur der Standardbibliothek an
deque
. Es ist eine ziemliche Überraschung für mich, dass die meisten Antwortenpandas
oder verwendennumpy
.Eigentlich habe ich eine andere Implementierung in Python-Dokumenten gefunden
Die Implementierung scheint mir jedoch etwas komplexer zu sein, als es sein sollte. Aber es muss aus einem bestimmten Grund in den Standard-Python-Dokumenten enthalten sein. Könnte jemand die Implementierung von mir und dem Standard-Dokument kommentieren?
quelle
O(n*d)
Berechnungen durch (d
n
O(n)
Mit den Variablen von @ Aikude habe ich einen Einzeiler geschrieben.
quelle
Obwohl es hier Lösungen für diese Frage gibt, werfen Sie bitte einen Blick auf meine Lösung. Es ist sehr einfach und funktioniert gut.
quelle
Nach dem Lesen der anderen Antworten glaube ich nicht, dass dies die Frage ist, aber ich bin hierher gekommen, um einen laufenden Durchschnitt einer Liste von Werten zu führen, deren Größe zugenommen hat.
Wenn Sie also eine Liste der Werte, die Sie von einem Ort (einem Standort, einem Messgerät usw.) erhalten, und den Durchschnitt der zuletzt
n
aktualisierten Werte aufbewahren möchten, können Sie den folgenden Code verwenden, der den Aufwand für das Hinzufügen neuer Werte minimiert Elemente:Und Sie können es zum Beispiel testen mit:
Welches gibt:
quelle
Eine andere Lösung, die nur eine Standardbibliothek und eine Deque verwendet:
quelle
Lassen Sie mich zu Bildungszwecken zwei weitere Numpy-Lösungen hinzufügen (die langsamer als die Cumsum-Lösung sind):
Verwendete Funktionen: as_strided , add.reduceat
quelle
Alle oben genannten Lösungen sind schlecht, weil sie fehlen
numpy.cumsum
, oderO(len(x) * w)
Implementierungen als Windungen.Gegeben
Beachten Sie, dass
x_[:w].sum()
gleich istx[:w-1].sum()
. Für den ersten Durchschnittnumpy.cumsum(...)
addiertx[w] / w
(x_[w+1] / w
subtrahiert) und subtrahiert0
(vonx_[0] / w
). Das führt zux[0:w].mean()
Via cumsum, werden Sie die zweite durchschnittliche aktualisieren , indem Sie zusätzlich hinzufügen
x[w+1] / w
und subtrahierenx[0] / w
, was zux[1:w+1].mean()
.Dies geht so lange weiter, bis
x[-w:].mean()
es erreicht ist.Diese Lösung ist vektorisiert
O(m)
, lesbar und numerisch stabil.quelle
Wie wäre es mit einem gleitenden Durchschnittsfilter ? Es ist auch ein Einzeiler und hat den Vorteil, dass Sie den Fenstertyp leicht manipulieren können, wenn Sie etwas anderes als das Rechteck benötigen, dh. ein N-langer einfacher gleitender Durchschnitt eines Arrays a:
Und mit dem angewendeten dreieckigen Fenster:
Hinweis: Normalerweise verwerfe ich die ersten N Proben als Fälschung, daher
[N:]
am Ende, aber es ist nicht notwendig und es handelt sich nur um eine persönliche Entscheidung.quelle
Wenn Sie sich dafür entscheiden, Ihre eigene zu rollen, anstatt eine vorhandene Bibliothek zu verwenden, sollten Sie sich des Gleitkommafehlers bewusst sein und versuchen, dessen Auswirkungen zu minimieren:
Wenn alle Ihre Werte ungefähr die gleiche Größenordnung haben, hilft dies, die Genauigkeit zu erhalten, indem immer Werte mit ungefähr ähnlichen Größen hinzugefügt werden.
quelle