حساب المتوسط المتحرك الأسي في بايثون
-
20-08-2019 - |
سؤال
لدي مجموعة من التواريخ و القياس على كل من تلك التواريخ.أود لحساب المتوسط المتحرك الأسي لكل من التواريخ.لا أحد يعرف كيفية القيام بذلك ؟
أنا جديدة على الثعبان.لا يبدو أن المتوسطات في مستوى بيثون المكتبة ، الذي يبدو لي غريبا.ربما أنا لا أبحث في المكان المناسب.
وذلك نظرا البرمجية التالية كيف يمكن لي حساب نقل متوسط من الذكاء نقاط تقويم التواريخ ؟
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 submodule من SciKits (إضافة على الأدوات التي تكمل SciPy) الدعاوى أفضل صيغة سؤالك.
لحساب أسي تجانس البيانات الخاصة بك مع عامل تخفيف 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
أنا دائما حساب ايماس مع الباندا:
هنا هو مثال على كيفية القيام بذلك:
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-docs/stable/generated/pandas.ewma.html
بلدي الثعبان قليلا صدئ (أي شخص يمكن أن لا تتردد في تعديل هذه التعليمة البرمجية إلى إجراء التصحيحات إذا لقد افسدت الجملة بطريقة أو بأخرى) ، ولكن هنا يذهب....
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
هذه الوظيفة يتحرك إلى الخلف من نهاية القائمة إلى بداية حساب المتوسط المتحرك الأسي لكل قيمة من خلال العمل الخلف حتى وزن معامل عنصر أقل من معين ابسيلون.
في نهاية الدالة ، عكس القيم قبل أن يعود قائمة (بحيث انهم في الترتيب الصحيح للحصول على المتصل).
(ملاحظة جانبية:إذا كنت تستخدم لغة أخرى غير بيثون ، أود إنشاء الحجم الكامل مجموعة فارغة أولا ثم ملء الوراء النظام, حتى لا تضطر إلى عكس ذلك في نهاية المطاف.ولكن أنا لا أعتقد أنك يمكن أن تعلن مجموعة كبيرة فارغة في بيثون.و في بيثون قوائم إلحاق هو أقل تكلفة بكثير من أن يتبع ، الذي هو السبب في أنني بنيت القائمة في ترتيب عكسي.يرجى تصحيح لي إذا كنت مخطئا.)
إن 'ألفا' الحجة هو تسوس عامل على كل التكرار.على سبيل المثال, إذا كنت تستخدم ألفا من 0.5, اليوم هو المتوسط المتحرك قيمة يتكون من التالية مرجح القيم:
today: 1.0
yesterday: 0.5
2 days ago: 0.25
3 days ago: 0.125
...etc...
بالطبع, إذا كنت قد حصلت على مجموعة ضخمة من القيم ، قيم من عشرة أو خمسة عشر يوما لن تساهم كثيرا إلى اليوم المتوسط المرجح.إن 'ابسيلون حجة يتيح لك وضع قطع النقطة أدناه والتي سوف تتوقف عن الرعاية عن القيم القديمة (منذ مساهمتها في قيمة اليوم سوف تكون ضئيلة).
كنت استدعاء الدالة شيئا من هذا القبيل:
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
أنا لا أعرف بيثون ، ولكن بمعدل جزء تعني بشكل كبير المتحللة مرشح تمرير منخفض من شكل
y_new = y_old + (input - y_old)*alpha
حيث ألفا = dt/تاو ، dt = على timestep من مرشح ، tau = وقت ثابت من مرشح ؟ (المتغير-timestep شكل هذا هو كما يلي, فقط احجز dt/تاو أن لا يكون أكثر من 1.0)
y_new = y_old + (input - y_old)*dt/tau
إذا كنت ترغب في تصفية ما يشبه التاريخ ، تأكد من تحويل إلى نقطة عائمة الكمية مثل # ثوان منذ 1 يناير 1970.
يمكنك أيضا استخدام SciPy تصفية الأسلوب لأن 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()))
(حيث دبوس.قراءة() تنتج القيمة التالية أود أن تستهلك).
هنا عينة بسيطة لقد عملت على أساس 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
أنا باستخدام قائمة معدل تسوس كمدخلات.آمل أن يكون هذا القليل وظيفة مع سطرين فقط قد تساعدك هنا بالنظر العميق العودية ليست مستقرة في بيثون.
def expma(aseries, ratio):
return sum([ratio*aseries[-x-1]*((1-ratio)**x) for x in range(len(aseries))])