Question

J'ai une plage de dates et une mesure à chacune de ces dates. J'aimerais calculer une moyenne mobile exponentielle pour chacune des dates. Est-ce que quelqu'un sait comment faire cela?

Je suis nouveau sur Python. Il ne semble pas que les moyennes soient intégrées à la bibliothèque standard Python, ce qui me semble un peu étrange. Peut-être que je ne cherche pas au bon endroit.

Alors, étant donné le code suivant, comment puis-je calculer la moyenne mobile pondérée des points de QI pour les dates du calendrier?

from datetime import date
days = [date(2008,1,1), date(2008,1,2), date(2008,1,7)]
IQ = [110, 105, 90]

(il existe probablement un meilleur moyen de structurer les données; tout conseil serait apprécié)

Était-ce utile?

La solution

EDIT: Il semble que mov_average_expw() fonction du scikits.timeseries.lib.moving_funcs du sous-module de SciKits (kits d'outils complémentaires complémentaires à SciPy ) convient mieux au libellé de votre question.

Pour calculer un lissage exponentiel de vos données avec un facteur de lissage alpha (c'est (1 - alpha) en termes de Wikipedia):

>>> alpha = 0.5
>>> assert 0 < alpha <= 1.0
>>> av = sum(alpha**n.days * iq 
...     for n, iq in map(lambda (day, iq), today=max(days): (today-day, iq), 
...         sorted(zip(days, IQ), key=lambda p: p[0], reverse=True)))
95.0

Ce qui précède n’est pas joli, alors modifions-le un peu:

from collections import namedtuple
from operator    import itemgetter

def smooth(iq_data, alpha=1, today=None):
    """Perform exponential smoothing with factor `alpha`.

    Time period is a day.
    Each time period the value of `iq` drops `alpha` times.
    The most recent data is the most valuable one.
    """
    assert 0 < alpha <= 1

    if alpha == 1: # no smoothing
        return sum(map(itemgetter(1), iq_data))

    if today is None:
        today = max(map(itemgetter(0), iq_data))

    return sum(alpha**((today - date).days) * iq for date, iq in iq_data)

IQData = namedtuple("IQData", "date iq")

if __name__ == "__main__":
    from datetime import date

    days = [date(2008,1,1), date(2008,1,2), date(2008,1,7)]
    IQ = [110, 105, 90]
    iqdata = list(map(IQData, days, IQ))
    print("\n".join(map(str, iqdata)))

    print(smooth(iqdata, alpha=0.5))

Exemple:

$ python26 smooth.py
IQData(date=datetime.date(2008, 1, 1), iq=110)
IQData(date=datetime.date(2008, 1, 2), iq=105)
IQData(date=datetime.date(2008, 1, 7), iq=90)
95.0

Autres conseils

J'ai fait un peu de googler et j'ai trouvé l'exemple de code suivant ( http://osdir.com/ml/python.matplotlib.general/2005-04/msg00044.html ):

def ema(s, n):
    """
    returns an n period exponential moving average for
    the time series s

    s is a list ordered from oldest (index 0) to most
    recent (index -1)
    n is an integer

    returns a numeric array of the exponential
    moving average
    """
    s = array(s)
    ema = []
    j = 1

    #get n sma first and calculate the next n period ema
    sma = sum(s[:n]) / n
    multiplier = 2 / float(1 + n)
    ema.append(sma)

    #EMA(current) = ( (Price(current) - EMA(prev) ) x Multiplier) + EMA(prev)
    ema.append(( (s[n] - sma) * multiplier) + sma)

    #now calculate the rest of the values
    for i in s[n+1:]:
        tmp = ( (i - ema[j]) * multiplier) + ema[j]
        j = j + 1
        ema.append(tmp)

    return ema

Je calcule toujours les EMA avec les pandas:

Voici un exemple comment procéder:

import pandas as pd
import numpy as np

def ema(values, period):
    values = np.array(values)
    return pd.ewma(values, span=period)[-1]

values = [9, 5, 10, 16, 5]
period = 5

print ema(values, period)

Plus d'infos sur les Pandas EWMA:

http://pandas.pydata.org/pandas- docs / stable / généré / pandas.ewma.html

Mon python est un peu rouillé (n'importe qui peut se sentir libre d'éditer ce code pour y apporter des corrections, si j'ai quelque peu falsifié la syntaxe), mais voilà ...

def movingAverageExponential(values, alpha, epsilon = 0):

   if not 0 < alpha < 1:
      raise ValueError("out of range, alpha='%s'" % alpha)

   if not 0 <= epsilon < alpha:
      raise ValueError("out of range, epsilon='%s'" % epsilon)

   result = [None] * len(values)

   for i in range(len(result)):
       currentWeight = 1.0

       numerator     = 0
       denominator   = 0
       for value in values[i::-1]:
           numerator     += value * currentWeight
           denominator   += currentWeight

           currentWeight *= alpha
           if currentWeight < epsilon: 
              break

       result[i] = numerator / denominator

   return result

Cette fonction revient en arrière, de la fin de la liste au début, en calculant la moyenne mobile exponentielle pour chaque valeur en travaillant en arrière jusqu'à ce que le coefficient de pondération d'un élément soit inférieur à l'epsilon donné.

À la fin de la fonction, il inverse les valeurs avant de renvoyer la liste (afin qu'elles soient dans le bon ordre pour l'appelant).

(NOTE LATÉRALE: si j'utilisais un langage autre que python, je créerais d'abord un tableau vide de taille normale, puis je le remplirais dans l'ordre inverse, de sorte que je n'aurais pas à l'inverser à la fin. Mais je ne pense pas que vous puissiez déclarer un grand tableau vide en python. Et dans les listes python, l’ajout coûte beaucoup moins cher que l’ajout en amont, c’est pourquoi j’ai construit la liste dans l’ordre inverse. Corrigez-moi si je me trompe.)

L'argument 'alpha' est le facteur de décroissance à chaque itération. Par exemple, si vous utilisiez un alpha de 0,5, la moyenne mobile actuelle serait composée des valeurs pondérées suivantes:

today:        1.0
yesterday:    0.5
2 days ago:   0.25
3 days ago:   0.125
...etc...

Bien sûr, si vous avez un grand nombre de valeurs, les valeurs d'il y a dix ou quinze jours ne contribueront pas beaucoup à la moyenne pondérée d'aujourd'hui. L'argument 'epsilon' vous permet de définir un seuil en dessous duquel vous cesserez de vous soucier des anciennes valeurs (car leur contribution à la valeur d'aujourd'hui sera insignifiante).

Vous appelez la fonction à peu près comme ceci:

result = movingAverageExponential(values, 0.75, 0.0001)

Dans matplotlib.org, exemples ( http://matplotlib.org/examples/pylab_examples/finance_work2. html ) fournit un bon exemple de la fonction de moyenne mobile exponentielle (EMA) utilisant numpy:

def moving_average(x, n, type):
    x = np.asarray(x)
    if type=='simple':
        weights = np.ones(n)
    else:
        weights = np.exp(np.linspace(-1., 0., n))

    weights /= weights.sum()

    a =  np.convolve(x, weights, mode='full')[:len(x)]
    a[:n] = a[n]
    return a

Je ne connais pas Python, mais pour la partie moyenne, voulez-vous dire un filtre passe-bas à décroissance exponentielle du formulaire

y_new = y_old + (input - y_old)*alpha

où alpha = dt / tau, dt = le pas du filtre, tau = la constante de temps du filtre? (la forme variable de timestep est la suivante, il suffit de découper dt / tau pour qu’il ne dépasse pas 1.0)

y_new = y_old + (input - y_old)*dt/tau

Si vous souhaitez filtrer quelque chose comme une date, assurez-vous de convertir cette valeur en virgule flottante telle que le nombre de secondes depuis le 1er janvier 1970.

Vous pouvez également utiliser la méthode du filtre SciPy car l’EMA est un filtre IIR. Cela aura l'avantage d'être environ 64 fois plus rapide que celle mesurée sur mon système en utilisant timeit sur des ensembles de données volumineux par rapport à enumerate () .

import numpy as np
from scipy.signal import lfilter

x = np.random.normal(size=1234)
alpha = .1 # smoothing coefficient
zi = [x[0]] # seed the filter state with first value
# filter can process blocks of continuous data if <zi> is maintained
y, zi = lfilter([1.-alpha], [1., -alpha], x, zi=zi)

J'ai trouvé l'extrait de code ci-dessus de @earino très utile - mais j'avais besoin de quelque chose qui puisse lisser en permanence un flux de valeurs - je l'ai donc refactoré:

def exponential_moving_average(period=1000):
    """ Exponential moving average. Smooths the values in v over ther period. Send in values - at first it'll return a simple average, but as soon as it's gahtered 'period' values, it'll start to use the Exponential Moving Averge to smooth the values.
    period: int - how many values to smooth over (default=100). """
    multiplier = 2 / float(1 + period)
    cum_temp = yield None  # We are being primed

    # Start by just returning the simple average until we have enough data.
    for i in xrange(1, period + 1):
        cum_temp += yield cum_temp / float(i)

    # Grab the timple avergae
    ema = cum_temp / period

    # and start calculating the exponentially smoothed average
    while True:
        ema = (((yield ema) - ema) * multiplier) + ema

et je l'utilise comme ceci:

def temp_monitor(pin):
    """ Read from the temperature monitor - and smooth the value out. The sensor is noisy, so we use exponential smoothing. """
    ema = exponential_moving_average()
    next(ema)  # Prime the generator

    while True:
        yield ema.send(val_to_temp(pin.read()))

(où pin.read () produit la valeur suivante que je voudrais consommer).

Voici un exemple simple que j'ai élaboré à partir de http: // stockcharts.com/school/doku.php?id=chart_school:indicators_technical:maver_averages

Notez que contrairement à leur feuille de calcul, je ne calcule pas le SMA et je n’attends pas de générer l’EMA après 10 échantillons. Cela signifie que mes valeurs diffèrent légèrement, mais si vous le tracez, cela suit exactement après 10 échantillons. Au cours des 10 premiers échantillons, l'EMA calculé est lissé de manière appropriée.

def emaWeight(numSamples):
    return 2 / float(numSamples + 1)

def ema(close, prevEma, numSamples):
    return ((close-prevEma) * emaWeight(numSamples) ) + prevEma

samples = [
22.27, 22.19, 22.08, 22.17, 22.18, 22.13, 22.23, 22.43, 22.24, 22.29,
22.15, 22.39, 22.38, 22.61, 23.36, 24.05, 23.75, 23.83, 23.95, 23.63,
23.82, 23.87, 23.65, 23.19, 23.10, 23.33, 22.68, 23.10, 22.40, 22.17,
]
emaCap = 10
e=samples[0]
for s in range(len(samples)):
    numSamples = emaCap if s > emaCap else s
    e =  ema(samples[s], e, numSamples)
    print e

Voici un moyen rapide (copier-coller depuis ici ):

def ExpMovingAverage(values, window):
    """ Numpy implementation of EMA
    """
    weights = np.exp(np.linspace(-1., 0., window))
    weights /= weights.sum()
    a =  np.convolve(values, weights, mode='full')[:len(values)]
    a[:window] = a[window]
    return a

J'utilise une liste et un taux de décroissance en tant qu'entrées. J'espère que cette petite fonction composée de deux lignes seulement peut vous aider, sachant que la récursion profonde n'est pas stable en python.

def expma(aseries, ratio):
    return sum([ratio*aseries[-x-1]*((1-ratio)**x) for x in range(len(aseries))])
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top