web-dev-qa-db-ja.com

pythonで指数移動平均を計算します

日付の範囲と、各日付の測定値があります。各日付の指数移動平均を計算したいと思います。誰かがこれを行う方法を知っていますか?

私はPythonが初めてです。平均が標準のpythonライブラリに組み込まれているようには見えません。これは少し奇妙なことに私を驚かせます。おそらく私は正しい場所を探していません。

それで、次のコードが与えられた場合、カレンダー日付のIQポイントの移動加重平均をどのように計算できますか?

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

(おそらくデータを構造化するためのより良い方法があるでしょう、どんなアドバイスもいただければ幸いです)

24
Jim

編集: mov_average_expw() from scikits.timeseries.lib.movi​​ng_funcs submodule from SciKitsSciPy を補完するアドオンツールキット)は、質問の表現に適しています。


平滑化係数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
20
jfs

私は少しグーグルしましたが、次のサンプルコードが見つかりました( 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
11
earino

私は常にパンダでEMAを計算しています:

これを行う方法の例を次に示します。

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

9
Into Numbers

私の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...

もちろん、大量の値の配列がある場合、10日または15日前の値は、今日の加重平均にはあまり貢献しません。 'epsilon'引数を使用すると、カットオフポイントを設定できます。これを下回ると、古い値を気にする必要がなくなります(今日の値への寄与は重要ではなくなるため)。

次のような関数を呼び出します。

result = movingAverageExponential(values, 0.75, 0.0001)
5
benjismith

Matplotlib.orgの例( http://matplotlib.org/examples/pylab_examples/finance_work2.html )には、numpyを使用した指数移動平均(EMA)関数の良い例が1つあります。

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

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

日付などをフィルタリングする場合は、1970年1月1日からの秒数などの浮動小数点数に変換してください。

5
Jason S

EMAはIIRフィルターなので、SciPyフィルターメソッドを使用することもできます。これは、enumerate()アプローチと比較した場合、大規模なデータセットでtimeitを使用した私のシステムで測定した場合、約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)
4
papahabla

@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()は、消費したい次の値を生成します)。

2
Rikard Anglerud

高速な方法( here からコピーして貼り付け)は次のとおりです。

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

これは私が http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages に基づいて作成した簡単なサンプルです。

スプレッドシートとは異なり、私はSMAを計算せず、10サンプル後にEMAを生成するのを待ちません。これは、私の値がわずかに異なることを意味しますが、グラフ化すると、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
1
user9170

入力として、リストと減衰率を使用しています。 pythonで深い再帰が安定していないことを考慮して、2行だけのこの小さな関数がここで役立つことを願っています。

def expma(aseries, ratio):
    return sum([ratio*aseries[-x-1]*((1-ratio)**x) for x in range(len(aseries))])
0
Xiaotian

コードを使用しようとすると、次のエラーが発生します。

https://codereview.stackexchange.com/questions/70510/calculating-exponential-moving-average-in-python/166640#16664

あなたの助けは大歓迎です

a[:n] = a[n]

IndexError:インデックス26はサイズ1の軸0の範囲外です

0
Robo

最短かもしれません:

#Specify decay in terms of span
#data_series should be a DataFrame

ema=data_series.ewm(span=5, adjust=False).mean()

0
Yusufmet

より簡単に、パンダを使用して

def EMA(tw):
    for x in tw:
        data["EMA{}".format(x)] = data['close'].ewm(span=x, adjust=False).mean()
        EMA([10,50,100])
0
Guillaume