Pythonで指数移動平均を計算する
-
20-08-2019 - |
質問
日付の範囲と、それぞれの日付の測定値があります。各日付の指数移動平均を計算したいと思います。誰かこれを行う方法を知っていますか?
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.timeseries.lib.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を計算しています:
ここでそれを行う方法の例です。
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)
パンダEWMAの詳細に関する情報:
http://pandas.pydata.org/pandas-ドキュメント/安定/生成/ 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
この関数は、要素の重み係数が与えられ、εより小さくなるまで後方に作業することによって、各値の指数移動平均を計算する、リストの末尾から先頭に、後方に移動する。
(彼らは、発信者のために正しい順序にいるように)関数の最後で、それはリストを返す前の値を反転します。
(SIDE注:私は、Python以外の言語を使用していた場合、私は最後にそれを逆にする必要がないように、私は、最初のフルサイズの空の配列を作成し、下位の順序それを埋めるだろう。しかし、私はあなたがPythonで大きな空の配列を宣言することができないと思います。そして、Pythonのリストで、添付ははるかに安価で、私は逆の順序でリストを建て理由である、前置するよりもです。私が間違っているなら、私を修正してください。)
「アルファ」の引数は、各反復での減衰係数です。あなたは0.5のアルファを使用した場合、その後、今日の移動平均値は、次の加重値で構成されます。
today: 1.0
yesterday: 0.5
2 days ago: 0.25
3 days ago: 0.125
...etc...
あなたは値の巨大な配列を持っている場合は、もちろん、10または15日前からの値は、今日の加重平均にあまり寄与しません。 「イプシロン」引数は、あなたが(今日の値への寄与は無視できるほど小さくなりますので、)古い値を気にしなくなり、その下のカットオフポイントを設定できます。
あなたはこのような関数の何かを呼び出すと思います:
result = movingAverageExponential(values, 0.75, 0.0001)
matplotlib.org例において( http://matplotlib.org/examples/pylab_examples/finance_work2。 HTMLする)numpyのを使用して平均(EMA)関数を移動指数の良い例が提供される:
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
ここで、α= DT / dtのタウ、=フィルタ、タウのタイムステップ=フィルタの時定数?
(次のように本の可変タイムステップの形態である、ちょうど1.0以上ではないとDT /タウクリップ)y_new = y_old + (input - y_old)*dt/tau
あなたは、日付のようなものをフィルタリングする場合は、、あなたは1970年1月1日以降の秒#のような浮動小数点数に変換することを確認します。
また、scipyのダウンロードフィルタ方式を使用することができます。これは、の列挙()のアプローチと比較すると、大規模なデータセットにのは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)
私は@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()は私が消費したい次の値を生成します)。
自分のスプレッドシートとは異なり、私はSMAを計算していない、と私は10個のサンプルの後にEMAを生成するために待っていないことに注意してください。これは私の値は若干異なるが、あなたはそれをグラフ化すれば、それは正確に10個のサンプルの後に、次のことを意味します。最初の10個のサンプル中、Iは計算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
私は、入力として、リストや減衰率を使用しています。私はちょうど2つのラインを持つこの小さな機能は、深い再帰がpythonで安定していない考慮すると、ここであなたを助けることを願っています。
def expma(aseries, ratio):
return sum([ratio*aseries[-x-1]*((1-ratio)**x) for x in range(len(aseries))])