вычисление экспоненциальной скользящей средней в python

StackOverflow https://stackoverflow.com/questions/488670

Вопрос

У меня есть диапазон дат и измерения для каждой из этих дат.Я бы хотел вычислить экспоненциальную скользящую среднюю для каждой из дат.Кто-нибудь знает, как это сделать?

Я новичок в python.Не похоже, что средние значения встроены в стандартную библиотеку python, что кажется мне немного странным.Может быть, я ищу не в том месте.

Итак, учитывая следующий код, как я мог бы рассчитать скользящее средневзвешенное значение баллов IQ для календарных дат?

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

(вероятно, есть лучший способ структурировать данные, мы будем признательны за любые советы)

Это было полезно?

Решение

Редактировать:Кажется , что mov_average_expw() функция из scikits.временные ряды.библиотека.moving_funcs подмодуль из Шикиты (дополнительные наборы инструментов, которые дополняют СциПи) лучше соответствует формулировке вашего вопроса.


Чтобы вычислить экспоненциальное сглаживание ваших данных с коэффициентом сглаживания alpha (это (1 - alpha) с точки зрения Википедии):

>>> 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

Вышесказанное некрасиво, так что давайте немного реорганизуем его:

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))

Пример:

$ 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

Другие советы

Я немного погуглил и нашел следующий пример кода (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

Я всегда рассчитываю EMA с помощью Pandas:

Вот пример того, как это сделать:

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)

Дополнительная информация о Pandas EWMA:

http://pandas.pydata.org/pandas-docs/stable/generated/pandas.ewma.html

Мой python немного подзабылся (любой может смело редактировать этот код, чтобы внести исправления, если я как-то перепутал синтаксис), но вот что получается....

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

Эта функция перемещается назад, от конца списка к началу, вычисляя экспоненциальное скользящее среднее для каждого значения, работая в обратном направлении до тех пор, пока весовой коэффициент для элемента не станет меньше заданного эпсилона.

В конце функции она переворачивает значения, прежде чем возвращать список (чтобы они были в правильном порядке для вызывающего объекта).

(ПРИМЕЧАНИЕ СБОКУ:если бы я использовал язык, отличный от python, я бы сначала создал полноразмерный пустой массив, а затем заполнил его в обратном порядке, чтобы мне не пришлось переворачивать его в конце.Но я не думаю, что вы можете объявить большой пустой массив в python.А в списках python добавление обходится намного дешевле, чем предпослание, вот почему я построил список в обратном порядке.Пожалуйста, поправьте меня, если я ошибаюсь.)

Аргумент 'alpha' - это коэффициент затухания на каждой итерации.Например, если вы использовали альфа-значение 0,5, то сегодняшнее значение скользящей средней будет состоять из следующих взвешенных значений:

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

Конечно, если у вас есть огромный массив значений, значения десяти- или пятнадцатидневной давности не внесут большого вклада в сегодняшнее средневзвешенное значение.Аргумент 'epsilon' позволяет вам установить предельную величину, ниже которой вы перестанете заботиться о старых значениях (поскольку их вклад в сегодняшнее значение будет незначительным).

Вы бы вызвали функцию примерно так:

result = movingAverageExponential(values, 0.75, 0.0001)

В разделе matplotlib.org примеры (http://matplotlib.org/examples/pylab_examples/finance_work2.html) приведен один хороший пример функции Экспоненциальной скользящей средней (EMA) с использованием 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

Я не знаю Python, но что касается части усреднения, вы имеете в виду экспоненциально затухающий фильтр нижних частот вида

y_new = y_old + (input - y_old)*alpha

где alpha = dt / tau, dt = временной интервал фильтра, tau = постоянная времени фильтра?(форма этого параметра с переменным временным шагом выглядит следующим образом, просто обрезайте dt / tau, чтобы оно было не больше 1.0)

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

Если вы хотите отфильтровать что-то вроде даты, убедитесь, что вы преобразовали в количество с плавающей запятой, например, количество секунд с 1 января 1970 года.

Вы также можете использовать метод SciPy filter, поскольку EMA является IIR-фильтром.Преимущество этого будет заключаться в том, что оно будет примерно в 64 раза быстрее, чем измерено в моей системе с использованием время пришло на больших наборах данных по сравнению с перечислять() подходите.

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)

Я нашел приведенный выше фрагмент кода от @earino довольно полезным, но мне нужно было что- то, что могло бы непрерывно сглаживать поток значений, поэтому я переработал его следующим образом:

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

и я использую его следующим образом:

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()))

(где pin.read() выдает следующее значение, которое я хотел бы использовать).

Вот простой пример, который я разработал на основе http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages

Обратите внимание, что в отличие от их электронной таблицы, я не вычисляю SMA и не жду, чтобы сгенерировать EMA после 10 выборок.Это означает, что мои значения немного отличаются, но если вы нанесете их на график, они будут точно соответствовать 10 образцам.В течение первых 10 выборок EMA, которое я рассчитываю, соответствующим образом сглаживается.

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

Быстрый способ (скопирован с здесь) заключается в следующем:

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

Я использую список и скорость затухания в качестве входных данных.Я надеюсь, что эта небольшая функция всего из двух строк может помочь вам здесь, учитывая, что глубокая рекурсия нестабильна в python.

def expma(aseries, ratio):
    return sum([ratio*aseries[-x-1]*((1-ratio)**x) for x in range(len(aseries))])
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top