it-swarm.com.de

Gleitender Durchschnitt oder laufender Mittelwert

Gibt es eine scipy-Funktion oder eine numpy-Funktion oder ein Modul für Python, die den laufenden Mittelwert eines 1D-Arrays bei einem bestimmten Fenster berechnet?

132
Shejo284

Für eine kurze, schnelle Lösung, die das Ganze in einer Schleife ohne Abhängigkeiten ausführt, funktioniert der folgende Code hervorragend.

mylist = [1, 2, 3, 4, 5, 6, 7]
N = 3
cumsum, moving_aves = [0], []

for i, x in enumerate(mylist, 1):
    cumsum.append(cumsum[i-1] + x)
    if i>=N:
        moving_ave = (cumsum[i] - cumsum[i-N])/N
        #can do stuff with moving_ave here
        moving_aves.append(moving_ave)
16
Aikude

UPD: effizientere Lösungen wurden von Alleo und jasaarim vorgeschlagen.


Sie können np.convolve dafür verwenden: 

np.convolve(x, np.ones((N,))/N, mode='valid')

Erläuterung

Der laufende Mittelwert ist ein Fall der mathematischen Operation von 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, d. H. Jedes Element mit einem entsprechenden Koeffizienten multiplizieren und die Ergebnisse addieren. Diese Koeffizienten, einer für jede Position im Fenster, werden manchmal als Faltung Kernel bezeichnet. Das arithmetische Mittel von N-Werten ist (x_1 + x_2 + ... + x_N) / N, der entsprechende Kernel ist (1/N, 1/N, ..., 1/N). Genau das bekommen wir mit np.ones((N,))/N.

Kanten

Das mode-Argument von np.convolve gibt an, wie die Kanten behandelt werden. Ich habe hier den valid-Modus gewählt, weil ich glaube, dass die meisten Leute davon ausgehen, dass das laufende Mittel funktioniert, aber Sie haben vielleicht andere Prioritäten. Hier ist ein Diagramm, das den Unterschied zwischen den Modi veranschaulicht:

import numpy as np
import matplotlib.pyplot as plt
modes = ['full', 'same', 'valid']
for m in modes:
    plt.plot(np.convolve(np.ones((200,)), np.ones((50,))/50, mode=m));
plt.axis([-10, 251, -.1, 1.1]);
plt.legend(modes, loc='lower center');
plt.show()

Running mean convolve modes

172
lapis

Effiziente Lösung

Die Faltung ist viel besser als die einfache Herangehensweise, aber (denke ich) verwendet sie FFT und ist daher ziemlich langsam. Speziell für die Berechnung des laufenden Mittelwerts funktioniert der folgende Ansatz jedoch gut

def running_mean(x, N):
    cumsum = numpy.cumsum(numpy.insert(x, 0, 0)) 
    return (cumsum[N:] - cumsum[:-N]) / float(N)

Der zu überprüfende Code

In[3]: x = numpy.random.random(100000)
In[4]: N = 1000
In[5]: %timeit result1 = numpy.convolve(x, numpy.ones((N,))/N, mode='valid')
10 loops, best of 3: 41.4 ms per loop
In[6]: %timeit result2 = running_mean(x, N)
1000 loops, best of 3: 1.04 ms per loop

Beachten Sie, dass numpy.allclose(result1, result2)True ist, zwei Methoden sind gleichwertig . Je größer N, desto größer ist der Zeitunterschied.

110
Alleo

Update: Das folgende Beispiel zeigt die alte pandas.rolling_mean-Funktion, die in den letzten Versionen von Pandas entfernt wurde. Ein modernes Äquivalent des Funktionsaufrufs wäre

In [8]: pd.Series(x).rolling(window=N).mean().iloc[N-1:].values
Out[8]: 
array([ 0.49815397,  0.49844183,  0.49840518, ...,  0.49488191,
        0.49456679,  0.49427121])

pandas ist dafür besser geeignet als NumPy oder SciPy. Seine Funktion rolling_mean erledigt die Arbeit bequem. Es wird auch ein NumPy-Array zurückgegeben, wenn die Eingabe ein Array ist.

Es ist schwierig, rolling_mean mit jeder benutzerdefinierten Python-Implementierung zu übertreffen. Hier ist ein Beispiel für zwei der vorgeschlagenen Lösungen: 

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: def running_mean(x, N):
   ...:     cumsum = np.cumsum(np.insert(x, 0, 0)) 
   ...:     return (cumsum[N:] - cumsum[:-N]) / N
   ...:

In [4]: x = np.random.random(100000)

In [5]: N = 1000

In [6]: %timeit np.convolve(x, np.ones((N,))/N, mode='valid')
10 loops, best of 3: 172 ms per loop

In [7]: %timeit running_mean(x, N)
100 loops, best of 3: 6.72 ms per loop

In [8]: %timeit pd.rolling_mean(x, N)[N-1:]
100 loops, best of 3: 4.74 ms per loop

In [9]: np.allclose(pd.rolling_mean(x, N)[N-1:], running_mean(x, N))
Out[9]: True

Es gibt auch nette Optionen für den Umgang mit den Edge-Werten.

62
jasaarim

Sie können einen laufenden Mittelwert berechnen mit:

import numpy as np

def runningMean(x, N):
    y = np.zeros((len(x),))
    for ctr in range(len(x)):
         y[ctr] = np.sum(x[ctr:(ctr+N)])
    return y/N

Aber es ist langsam.

Zum Glück enthält numpy eine Convolve - Funktion, mit der wir die Dinge beschleunigen können. Der laufende Mittelwert entspricht der Faltung von x mit einem Vektor, der N lang ist, wobei alle Mitglieder 1/N entsprechen. Die numpy-Implementierung von Convolve enthält den Startübergang, sodass Sie die ersten N-1-Punkte entfernen müssen:

def runningMeanFast(x, N):
    return np.convolve(x, np.ones((N,))/N)[(N-1):]

Auf meinem Rechner ist die schnelle Version je nach Länge des Eingabevektors und der Größe des Mittelungsfensters 20 bis 30-mal schneller.

Beachten Sie, dass Convolve einen 'same'-Modus enthält, der anscheinend das anfänglich auftretende vorübergehende Problem angehen sollte, es aber zwischen Anfang und Ende aufteilt.

48
mtrw

oder ein Modul für Python, das berechnet

bei meinen Tests bei Tradewave.net gewinnt TA-lib immer:

import talib as ta
import numpy as np
import pandas as pd
import scipy
from scipy import signal
import time as t

PAIR = info.primary_pair
PERIOD = 30

def initialize():
    storage.reset()
    storage.elapsed = storage.get('elapsed', [0,0,0,0,0,0])

def cumsum_sma(array, period):
    ret = np.cumsum(array, dtype=float)
    ret[period:] = ret[period:] - ret[:-period]
    return ret[period - 1:] / period

def pandas_sma(array, period):
    return pd.rolling_mean(array, period)

def api_sma(array, period):
    # this method is native to Tradewave and does NOT return an array
    return (data[PAIR].ma(PERIOD))

def talib_sma(array, period):
    return ta.MA(array, period)

def convolve_sma(array, period):
    return np.convolve(array, np.ones((period,))/period, mode='valid')

def fftconvolve_sma(array, period):    
    return scipy.signal.fftconvolve(
        array, np.ones((period,))/period, mode='valid')    

def tick():

    close = data[PAIR].warmup_period('close')

    t1 = t.time()
    sma_api = api_sma(close, PERIOD)
    t2 = t.time()
    sma_cumsum = cumsum_sma(close, PERIOD)
    t3 = t.time()
    sma_pandas = pandas_sma(close, PERIOD)
    t4 = t.time()
    sma_talib = talib_sma(close, PERIOD)
    t5 = t.time()
    sma_convolve = convolve_sma(close, PERIOD)
    t6 = t.time()
    sma_fftconvolve = fftconvolve_sma(close, PERIOD)
    t7 = t.time()

    storage.elapsed[-1] = storage.elapsed[-1] + t2-t1
    storage.elapsed[-2] = storage.elapsed[-2] + t3-t2
    storage.elapsed[-3] = storage.elapsed[-3] + t4-t3
    storage.elapsed[-4] = storage.elapsed[-4] + t5-t4
    storage.elapsed[-5] = storage.elapsed[-5] + t6-t5    
    storage.elapsed[-6] = storage.elapsed[-6] + t7-t6        

    plot('sma_api', sma_api)  
    plot('sma_cumsum', sma_cumsum[-5])
    plot('sma_pandas', sma_pandas[-10])
    plot('sma_talib', sma_talib[-15])
    plot('sma_convolve', sma_convolve[-20])    
    plot('sma_fftconvolve', sma_fftconvolve[-25])

def stop():

    log('ticks....: %s' % info.max_ticks)

    log('api......: %.5f' % storage.elapsed[-1])
    log('cumsum...: %.5f' % storage.elapsed[-2])
    log('pandas...: %.5f' % storage.elapsed[-3])
    log('talib....: %.5f' % storage.elapsed[-4])
    log('convolve.: %.5f' % storage.elapsed[-5])    
    log('fft......: %.5f' % storage.elapsed[-6])

ergebnisse:

[2015-01-31 23:00:00] ticks....: 744
[2015-01-31 23:00:00] api......: 0.16445
[2015-01-31 23:00:00] cumsum...: 0.03189
[2015-01-31 23:00:00] pandas...: 0.03677
[2015-01-31 23:00:00] talib....: 0.00700  # <<< Winner!
[2015-01-31 23:00:00] convolve.: 0.04871
[2015-01-31 23:00:00] fft......: 0.22306

 enter image description here

19
litepresence

Eine einsatzbereite Lösung finden Sie unter https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Sie liefert einen laufenden Durchschnitt mit dem Fenstertyp flat. Beachten Sie, dass dies etwas komplizierter ist als die einfache Convolve-Methode, da es versucht, die Probleme am Anfang und am Ende der Daten durch Reflexion zu behandeln (was in Ihrem Fall möglicherweise funktioniert). ..).

Zu Beginn können Sie Folgendes versuchen:

a = np.random.random(100)
plt.plot(a)
b = smooth(a, window='flat')
plt.plot(b)
16
Hansemann

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 der Eingabeliste ist linear, und ich kann mir keine andere Möglichkeit vorstellen, um sie effizienter zu gestalten (wenn jemand eine bessere Methode zur Zuordnung des Ergebnisses kennt, lass es mich wissen).

HINWEIS: Dies wäre bei Verwendung eines Numpy-Arrays anstelle einer Liste viel schneller, aber ich wollte alle Abhängigkeiten beseitigen. Es ist auch möglich, die Leistung durch Multithread-Ausführung zu verbessern

Die Funktion geht davon aus, dass die Eingabeliste eindimensional ist. Seien Sie also vorsichtig.

### Running mean/Moving average
def running_mean(l, N):
    sum = 0
    result = list( 0 for x in l)

    for i in range( 0, N ):
        sum = sum + l[i]
        result[i] = sum / (i+1)

    for i in range( N, len(l) ):
        sum = sum - l[i-N] + l[i]
        result[i] = sum / N

    return result
12
NeXuS

Wenn es wichtig ist, die Dimensionen der Eingabe beizubehalten (anstatt die Ausgabe auf den Bereich 'valid' einer Faltung zu beschränken), können Sie scipy.ndimage.filters.uniform_filter1d verwenden:

import numpy as np
from scipy.ndimage.filters import uniform_filter1d
N = 1000
x = np.random.random(100000)
y = uniform_filter1d(x, size=N)

y.shape == x.shape
>>> True

uniform_filter1d bietet mehrere Möglichkeiten, um den Rand zu behandeln, wobei 'reflect' die Standardeinstellung ist. In meinem Fall wollte ich jedoch 'nearest'.

Es ist auch ziemlich schnell (fast 50-mal schneller als np.convolve):

%timeit y1 = np.convolve(x, np.ones((N,))/N, mode='same')
100 loops, best of 3: 9.28 ms per loop

%timeit y2 = uniform_filter1d(x, size=N)
10000 loops, best of 3: 191 µs per loop
10
moi

Ich habe noch nicht überprüft, wie schnell das ist, aber Sie könnten es versuchen:

from collections import deque

cache = deque() # keep track of seen values
n = 10          # window size
A = xrange(100) # some dummy iterable
cum_sum = 0     # initialize cumulative sum

for t, val in enumerate(A, 1):
    cache.append(val)
    cum_sum += val
    if t < n:
        avg = cum_sum / float(t)
    else:                           # if window is saturated,
        cum_sum -= cache.popleft()  # subtract oldest value
        avg = cum_sum / float(n)
5
Kris

Ein bisschen zu spät zur Party, aber ich habe meine eigene kleine Funktion erstellt, die sich NICHT um die Enden oder Pads mit Nullen windet, mit denen dann auch der Durchschnitt ermittelt wird. Als weiterer Leckerbissen kann das Signal auch an linear beabstandeten Punkten neu abgetastet werden. Passen Sie den Code nach Belieben an, um weitere Funktionen zu erhalten.

Die Methode ist eine einfache Matrixmultiplikation mit einem normalisierten Gaußschen Kernel.

def running_mean(y_in, x_in, N_out=101, sigma=1):
    '''
    Returns running mean as a Bell-curve weighted average at evenly spaced
    points. Does NOT wrap signal around, or pad with zeros.

    Arguments:
    y_in -- y values, the values to be smoothed and re-sampled
    x_in -- x values for array

    Keyword arguments:
    N_out -- NoOf elements in resampled array.
    sigma -- 'Width' of Bell-curve in units of param x .
    '''
    N_in = size(y_in)

    # Gaussian kernel
    x_out = np.linspace(np.min(x_in), np.max(x_in), N_out)
    x_in_mesh, x_out_mesh = np.meshgrid(x_in, x_out)
    gauss_kernel = np.exp(-np.square(x_in_mesh - x_out_mesh) / (2 * sigma**2))
    # Normalize kernel, such that the sum is one along axis 1
    normalization = np.tile(np.reshape(sum(gauss_kernel, axis=1), (N_out, 1)), (1, N_in))
    gauss_kernel_normalized = gauss_kernel / normalization
    # Perform running average as a linear operation
    y_out = gauss_kernel_normalized @ y_in

    return y_out, x_out

Eine einfache Verwendung für ein Sinussignal mit zusätzlichem normalem verteiltem Rauschen:  enter image description here

4
Clausen

Ein anderer Ansatz, um den gleitenden Durchschnitt ohne mithilfe von numpy, panda zu finden

import itertools
sample = [2, 6, 10, 8, 11, 10]
list(itertools.starmap(lambda a,b: b/a, 
               enumerate(itertools.accumulate(sample), 1)))

druckt [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]

3
DmitrySemenov

Anstelle von Numpy oder Scipy würde ich Pandas empfehlen, dies schneller zu machen:

df['data'].rolling(3).mean()

Dies erfordert den gleitenden Durchschnitt (MA) von 3 Perioden der Spalte "Daten". Sie können auch die verschobenen Versionen berechnen. Beispielsweise kann diejenige, die die aktuelle Zelle (zurückgeschoben) ausschließt, einfach berechnet werden:

df['data'].shift(periods=1).rolling(3).mean()
3
Gursel Karacor

Diese Frage ist jetzt noch älter als NeXuS letzten Monat darüber geschrieben hat, ABER ich mag, wie sein Code mit Edge-Fällen umgeht. Da es sich jedoch um einen "einfachen gleitenden Durchschnitt" handelt, bleiben die Ergebnisse hinter den Daten zurück, für die sie gelten. Ich dachte, dass der Umgang mit Edge-Fällen auf befriedigendere Weise als mit den Modi valid, same und full von NumPy erreicht werden könnte, wenn ein ähnlicher Ansatz auf eine convolution()-basierte Methode angewendet wird.

In meinem Beitrag wird ein zentraler laufender Durchschnitt verwendet, um die Ergebnisse an ihre Daten anzupassen. Wenn zu wenige Punkte für die Verwendung des Fensters voller Größe zur Verfügung stehen, werden laufende Durchschnittswerte aus aufeinanderfolgend kleineren Fenstern an den Rändern des Arrays berechnet. [Eigentlich aus immer größeren Fenstern, aber das ist ein Implementierungsdetail.]

import numpy as np

def running_mean(l, N):
    # Also works for the(strictly invalid) cases when N is even.
    if (N//2)*2 == N:
        N = N - 1
    front = np.zeros(N//2)
    back = np.zeros(N//2)

    for i in range(1, (N//2)*2, 2):
        front[i//2] = np.convolve(l[:i], np.ones((i,))/i, mode = 'valid')
    for i in range(1, (N//2)*2, 2):
        back[i//2] = np.convolve(l[-i:], np.ones((i,))/i, mode = 'valid')
    return np.concatenate([front, np.convolve(l, np.ones((N,))/N, mode = 'valid'), back[::-1]])

Es ist relativ langsam, weil es convolve() verwendet, und von einem echten Pythonista wahrscheinlich ziemlich viel aufgewertet werden könnte. Ich glaube jedoch, dass die Idee Bestand hat.

3
marisano

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 Mittelwert über eine iterierbare Anzahl von Werten berechnet, indem itertools.accumulate verwendet wird.

>>> from itertools import accumulate
>>> values = range(100)

Beachten Sie, dass values eine beliebige Iteration sein kann, einschließlich Generatoren oder anderen Objekten, die spontan Werte erzeugen.

Konstruieren Sie zunächst träge die kumulierte Summe der Werte.

>>> cumu_sum = accumulate(value_stream)

Als Nächstes enumerate die kumulative Summe (beginnend bei 1) und konstruieren Sie einen Generator, der den Bruchteil der angesammelten Werte und den aktuellen Aufzählungsindex ergibt.

>>> rolling_avg = (accu/i for i, accu in enumerate(cumu_sum, 1))

Sie können means = list(rolling_avg) ausgeben, wenn Sie alle Werte gleichzeitig im Speicher benötigen oder next inkrementell aufrufen.
(Natürlich können Sie rolling_avg auch mit einer for-Schleife durchlaufen, die implizit next aufruft.)

>>> next(rolling_avg) # 0/1
>>> 0.0
>>> next(rolling_avg) # (0 + 1)/2
>>> 0.5
>>> next(rolling_avg) # (0 + 1 + 2)/3
>>> 1.0

Diese Lösung kann als Funktion wie folgt geschrieben werden.

from itertools import accumulate

def rolling_avg(iterable):
    cumu_sum = accumulate(iterable)
    yield from (accu/i for i, accu in enumerate(cumu_sum, 1))

Eine Coroutine an die Sie jederzeit Werte senden können

Diese Coroutine verbraucht die von Ihnen gesendeten Werte und behält einen laufenden Durchschnitt der bisher angezeigten Werte bei.

Dies ist nützlich, wenn Sie keine Werte iterierbar haben, sondern die Werte so ermitteln, dass sie zu verschiedenen Zeitpunkten im Verlauf Ihres Programms gemittelt werden.

def rolling_avg_coro():
    i = 0
    total = 0.0
    avg = None

    while True:
        next_value = yield avg
        i += 1
        total += next_value
        avg = total/i

Die Coroutine funktioniert so:

>>> averager = rolling_avg_coro() # instantiate coroutine
>>> next(averager) # get coroutine going (this is called priming)
>>>
>>> averager.send(5) # 5/1
>>> 5.0
>>> averager.send(3) # (5 + 3)/2
>>> 4.0
>>> print('doing something else...')
doing something else...
>>> averager.send(13) # (5 + 3 + 13)/3
>>> 7.0

Berechnung des Durchschnitts über ein Schiebefenster der Größe N

Diese Generatorfunktion nimmt eine iterierbare und eine Fenstergröße N und liefert den Durchschnitt der aktuellen Werte im Fenster. Es verwendet eine deque , eine Datenstruktur, die einer Liste ähnelt, jedoch für schnelle Änderungen optimiert ist (pop, append) an beiden Endpunkten.

from collections import deque
from itertools import islice

def sliding_avg(iterable, N):        
    it = iter(iterable)
    window = deque(islice(it, N))        
    num_vals = len(window)

    if num_vals < N:
        msg = 'window size {} exceeds total number of values {}'
        raise ValueError(msg.format(N, num_vals))

    N = float(N) # force floating point division if using Python 2
    s = sum(window)

    while True:
        yield s/N
        try:
            nxt = next(it)
        except StopIteration:
            break
        s = s - window.popleft() + nxt
        window.append(nxt)

Hier ist die Funktion in Aktion:

>>> values = range(100)
>>> N = 5
>>> window_avg = sliding_avg(values, N)
>>> 
>>> next(window_avg) # (0 + 1 + 2 + 3 + 4)/5
>>> 2.0
>>> next(window_avg) # (1 + 2 + 3 + 4 + 5)/5
>>> 3.0
>>> next(window_avg) # (2 + 3 + 4 + 5 + 6)/5
>>> 4.0
2
timgeb

Es gibt viele Antworten über die Berechnung eines laufenden Mittelwerts. Meine Antwort fügt zwei zusätzliche Funktionen hinzu:

  1. ignoriert nan-Werte 
  2. berechnet den Mittelwert für die N Nachbarwerte, die NICHT den Wert von Interesse selbst enthalten

Dieses zweite Merkmal ist besonders nützlich, um zu bestimmen, welche Werte sich um einen bestimmten Betrag vom allgemeinen Trend unterscheiden.

Ich verwende numpy.cumsum, da dies die zeiteffizienteste Methode ist ( siehe Alleos Antwort oben ). 

N=10 # number of points to test on each side of point of interest, best if even
padded_x = np.insert(np.insert( np.insert(x, len(x), np.empty(int(N/2))*np.nan), 0, np.empty(int(N/2))*np.nan ),0,0)
n_nan = np.cumsum(np.isnan(padded_x))
cumsum = np.nancumsum(padded_x) 
window_sum = cumsum[N+1:] - cumsum[:-(N+1)] - x # subtract value of interest from sum of all values within window
window_n_nan = n_nan[N+1:] - n_nan[:-(N+1)] - np.isnan(x)
window_n_values = (N - window_n_nan)
movavg = (window_sum) / (window_n_values)

Dieser Code funktioniert nur für gerade Ns. Sie kann für ungerade Zahlen eingestellt werden, indem Sie np.insert von padded_x und n_nan ändern.

Beispielausgabe (roh in schwarz, movavg in blau):  raw data (black) and moving average (blue) of 10 points around each value, not including that value. nan values are ignored.

Dieser Code kann leicht angepasst werden, um alle gleitenden Durchschnittswerte zu entfernen, die aus weniger als Cutoff = 3 Nicht-Nanowerten berechnet werden.

window_n_values = (N - window_n_nan).astype(float) # dtype must be float to set some values to nan
cutoff = 3
window_n_values[window_n_values<cutoff] = np.nan
movavg = (window_sum) / (window_n_values)

 raw data (black) and moving average (blue) while ignoring any window with fewer than 3 non-nan values

2
gtcoder

Nur Python Stadnard Library verwenden (speichereffizient)

Geben Sie einfach eine andere Version der Verwendung der Standardbibliothek deque an. Ich bin überrascht, dass die meisten Antworten pandas oder numpy verwenden.

def moving_average(iterable, n=3):
    d = deque(maxlen=n)
    for i in iterable:
        d.append(i)
        if len(d) == n:
            yield sum(d)/n

r = moving_average([40, 30, 50, 46, 39, 44])
assert list(r) == [40.0, 42.0, 45.0, 43.0]

Actaully Ich habe eine andere Implementierung in Python-Dokumenten gefunden

def moving_average(iterable, n=3):
    # moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
    # http://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable)
    d = deque(itertools.islice(it, n-1))
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n

Die Implementierung scheint mir jedoch etwas komplexer als sie sein sollte. Aber es muss aus einem Grund in den Standard-Python-Dokumenten enthalten sein. Kann sich jemand zur Implementierung von mir und dem Standard-Dokument äußern?

1
MaThMaX

Ich denke, das lässt sich mit Engpass elegant lösen.

Siehe grundlegendes Beispiel unten:

import numpy as np
import bottleneck as bn

a = np.random.randint(4, 1000, size=100)
mm = bn.move_mean(a, window=5, min_count=1)
  • "mm" ist der gleitende Mittelwert 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 zu berücksichtigen sind (z. B. für die ersten Elemente oder wenn das Array nan-Werte hat).

Das Gute daran ist, dass Bottleneck hilft, mit Nan-Werten umzugehen, und es ist auch sehr effizient.

1
Anthony Anyanwu

Es gibt einen Kommentar von mab in einer der Antworten , die über diese Methode verfügen. bottleneck hat move_mean, was ein einfacher gleitender Durchschnitt ist:

import numpy as np
import bottleneck as bn

a = np.arange(10) + np.random.random(10)

mva = bn.move_mean(a, window=2, min_count=1)

min_count ist ein praktischer Parameter, der den gleitenden Durchschnitt bis zu diesem Punkt in Ihrem Array übernimmt. Wenn Sie nicht min_count setzen, entspricht dies window, und alles bis zu window-Punkten ist nan.

1
wordsforthewise

Obwohl es hier Lösungen für diese Frage gibt, werfen Sie doch einen Blick auf meine Lösung. Es ist sehr einfach und funktioniert gut. 

import numpy as np
dataset = np.asarray([1, 2, 3, 4, 5, 6, 7])
ma = list()
window = 3
for t in range(0, len(dataset)):
    if t+window <= len(dataset):
        indices = range(t, t+window)
        ma.append(np.average(np.take(dataset, indices)))
else:
    ma = np.asarray(ma)
1
Ayberk Yavuz

Durch das Lesen der anderen Antworten glaube ich nicht, dass dies die Frage ist, aber ich kam hierher mit der Notwendigkeit, einen laufenden Durchschnitt einer Liste von Werten zu halten, deren Größe zunahm.

Wenn Sie also eine Liste mit Werten speichern möchten, die Sie von irgendwo abrufen (Standort, Messgerät usw.) und den Durchschnitt der letzten n-Werte aktualisiert haben, können Sie den untenstehenden Code verwenden, der das Hinzufügen von Daten minimiert neue elemente:

class Running_Average(object):
    def __init__(self, buffer_size=10):
        """
        Create a new Running_Average object.

        This object allows the efficient calculation of the average of the last
        `buffer_size` numbers added to it.

        Examples
        --------
        >>> a = Running_Average(2)
        >>> a.add(1)
        >>> a.get()
        1.0
        >>> a.add(1)  # there are two 1 in buffer
        >>> a.get()
        1.0
        >>> a.add(2)  # there's a 1 and a 2 in the buffer
        >>> a.get()
        1.5
        >>> a.add(2)
        >>> a.get()  # now there's only two 2 in the buffer
        2.0
        """
        self._buffer_size = int(buffer_size)  # make sure it's an int
        self.reset()

    def add(self, new):
        """
        Add a new number to the buffer, or replaces the oldest one there.
        """
        new = float(new)  # make sure it's a float
        n = len(self._buffer)
        if n < self.buffer_size:  # still have to had numbers to the buffer.
            self._buffer.append(new)
            if self._average != self._average:  # ~ if isNaN().
                self._average = new  # no previous numbers, so it's new.
            else:
                self._average *= n  # so it's only the sum of numbers.
                self._average += new  # add new number.
                self._average /= (n+1)  # divide by new number of numbers.
        else:  # buffer full, replace oldest value.
            old = self._buffer[self._index]  # the previous oldest number.
            self._buffer[self._index] = new  # replace with new one.
            self._index += 1  # update the index and make sure it's...
            self._index %= self.buffer_size  # ... smaller than buffer_size.
            self._average -= old/self.buffer_size  # remove old one...
            self._average += new/self.buffer_size  # ...and add new one...
            # ... weighted by the number of elements.

    def __call__(self):
        """
        Return the moving average value, for the lazy ones who don't want
        to write .get .
        """
        return self._average

    def get(self):
        """
        Return the moving average value.
        """
        return self()

    def reset(self):
        """
        Reset the moving average.

        If for some reason you don't want to just create a new one.
        """
        self._buffer = []  # could use np.empty(self.buffer_size)...
        self._index = 0  # and use this to keep track of how many numbers.
        self._average = float('nan')  # could use np.NaN .

    def get_buffer_size(self):
        """
        Return current buffer_size.
        """
        return self._buffer_size

    def set_buffer_size(self, buffer_size):
        """
        >>> a = Running_Average(10)
        >>> for i in range(15):
        ...     a.add(i)
        ...
        >>> a()
        9.5
        >>> a._buffer  # should not access this!!
        [10.0, 11.0, 12.0, 13.0, 14.0, 5.0, 6.0, 7.0, 8.0, 9.0]

        Decreasing buffer size:
        >>> a.buffer_size = 6
        >>> a._buffer  # should not access this!!
        [9.0, 10.0, 11.0, 12.0, 13.0, 14.0]
        >>> a.buffer_size = 2
        >>> a._buffer
        [13.0, 14.0]

        Increasing buffer size:
        >>> a.buffer_size = 5
        Warning: no older data available!
        >>> a._buffer
        [13.0, 14.0]

        Keeping buffer size:
        >>> a = Running_Average(10)
        >>> for i in range(15):
        ...     a.add(i)
        ...
        >>> a()
        9.5
        >>> a._buffer  # should not access this!!
        [10.0, 11.0, 12.0, 13.0, 14.0, 5.0, 6.0, 7.0, 8.0, 9.0]
        >>> a.buffer_size = 10  # reorders buffer!
        >>> a._buffer
        [5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0]
        """
        buffer_size = int(buffer_size)
        # order the buffer so index is zero again:
        new_buffer = self._buffer[self._index:]
        new_buffer.extend(self._buffer[:self._index])
        self._index = 0
        if self._buffer_size < buffer_size:
            print('Warning: no older data available!')  # should use Warnings!
        else:
            diff = self._buffer_size - buffer_size
            print(diff)
            new_buffer = new_buffer[diff:]
        self._buffer_size = buffer_size
        self._buffer = new_buffer

    buffer_size = property(get_buffer_size, set_buffer_size)

Und Sie können es zB mit testen:

def graph_test(N=200):
    import matplotlib.pyplot as plt
    values = list(range(N))
    values_average_calculator = Running_Average(N/2)
    values_averages = []
    for value in values:
        values_average_calculator.add(value)
        values_averages.append(values_average_calculator())
    fig, ax = plt.subplots(1, 1)
    ax.plot(values, label='values')
    ax.plot(values_averages, label='averages')
    ax.grid()
    ax.set_xlim(0, N)
    ax.set_ylim(0, N)
    fig.show()

Was gibt:

 Values and their average as a function of values #

1
berna1111

Lassen Sie mich zu Bildungszwecken zwei weitere Numpy-Lösungen hinzufügen (die langsamer sind als die Cumsum-Lösung):

import numpy as np
from numpy.lib.stride_tricks import as_strided

def ra_strides(arr, window):
    ''' Running average using as_strided'''
    n = arr.shape[0] - window + 1
    arr_strided = as_strided(arr, shape=[n, window], strides=2*arr.strides)
    return arr_strided.mean(axis=1)

def ra_add(arr, window):
    ''' Running average using add.reduceat'''
    n = arr.shape[0] - window + 1
    indices = np.array([0, window]*n) + np.repeat(np.arange(n), 2)
    arr = np.append(arr, 0)
    return np.add.reduceat(arr, indices )[::2]/window

Verwendete Funktionen: as_strided , add.reduceat

0
AndyK

Wie wäre es mit einem gleitenden Durchschnittsfilter ? Es ist auch ein Einzeiler und hat den Vorteil, dass Sie den Fenstertyp leicht bearbeiten können, wenn Sie etwas anderes als das Rechteck benötigen, dh ein N-langer einfacher gleitender Durchschnitt eines Arrays a:

lfilter(np.ones(N)/N, [1], a)[N:]

Und mit dem dreieckigen Fenster:

lfilter(np.ones(N)*scipy.signal.triang(N)/N, [1], a)[N:]
0
mac13k

Eine andere Lösung, die nur eine Standardbibliothek und ein Deque verwendet:

from collections import deque
import itertools

def moving_average(iterable, n=3):
    # http://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable) 
    # create an iterable object from input argument
    d = deque(itertools.islice(it, n-1))  
    # create deque object by slicing iterable
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n

# example on how to use it
for i in  moving_average([40, 30, 50, 46, 39, 44]):
    print(i)

# 40.0
# 42.0
# 45.0
# 43.0
0
Vlad Bezden