پیاده سازی میانگین متحرک نمایی در پایتون — راهنمای گام به گام

۵۸۲ بازدید
آخرین به‌روزرسانی: ۰۶ شهریور ۱۴۰۲
زمان مطالعه: ۴ دقیقه
پیاده سازی میانگین متحرک نمایی در پایتون — راهنمای گام به گام

در آموزش‌های قبلی مجله فرادرس، با پیاده‌سازی میانگین متحرک ساده در پایتون آشنا شدیم و به برخی از نقاط ضعف آن اشاره کردیم. در این آموزش، به میانگین متحرک نمایی (Exponential Moving Average) یا EMA خواهیم پرداخت. در این روشِ میانگین‌گیری، برخلاف روش ساده، با تمامی داده‌ها به یک شکل برخورد نمی‌کنیم و به داده‌های جدیدتر، وزن زیادی قائل می‌شویم. این ویژگی باعث می‌شود تا تأخیر کمتری در رفتار آن وجود داشته باشد. در ادامه، به پیاده سازی میانگین متحرک نمایی در پایتون می‌پردازیم.

میانگین متحرک نمایی چیست؟

اگر یک سری زمانی به شکل زیر داشته باشیم:

$$ \large X =\left\{x_{1}, x_{2} \ldots x_{n}\right\} $$

یک میانگین متحرک نمایی با طول $$L$$ به شکل زیر محاسبه می‌شود:

$$ \large \begin{aligned}
&M_{L}=\frac{1}{L} \sum_{i=1}^{L} x_{i}\\
&M_{t}=\alpha \cdot x_{t}+(1-\alpha) \cdot M_{t-1} \quad L<t \leq n\\
\end{aligned} $$

به این ترتیب، مقدار در هر زمان با توجه به زمان‌های قبلی محاسبه می‌شود.

به سادگی می‌توانیم وزن مربوط به هر روز را به شکل زیر محاسبه کنیم:

$$ \large \begin{aligned}
&W_{t}=\alpha\\
&W_{t-1}=\alpha \cdot(1-\alpha)\\
&W_{t-2}=\alpha \cdot(1-\alpha)^{2}\\
&\vdots \\
&W_{t-\infty}=\alpha \cdot(1-\alpha)^{\infty}=0
\end{aligned} $$

که مجموع وزن‌ها به صورت زیر خواهد بود:

$$ \large \begin{align}
\text { Sum } & =\sum_{i=0}^{\infty} W_{t-i}=\sum_{i=0}^{\infty} \alpha \cdot(1-\alpha)^{i}=\alpha \sum_{i=0}^{\infty}(1-\alpha)^{i}=\alpha \cdot\left(\frac{1}{1-(1-\alpha)}\right) \\
& =\alpha \cdot\left(\frac{1}{\alpha}\right)=1
\end{align} $$

بنابراین اثبات می‌شود که مجموع وزن‌ها برابر $$1$$ است.

ضریب $$\alpha$$ میزان حساسیت بر داده‌های جدید را تعیین می‌کند. اگر $$\alpha = 0 $$، مقدار میانگین متحرک همواره ثابت خواهد بود و اگر $$\alpha = 1 $$، مقدار میانگین متحرک همواره برابر آخرین مقدار سیگنال خواهد بود.

معمولاً به منظور محاسبه میانگین متحرک نمایی با طول $$L$$ از رابطه زیر استفاده می‌کنیم:

$$ \large \alpha =\frac {1 +r } { L + r}$$

که در این رابطه اغلب حالت $$r=1$$ در نظر گرفته می‌شود. با افزایش $$r$$ وزن مربوط به روز‌های اخیر افزایش می‌یابد.

برای یادگیری برنامه‌نویسی با زبان پایتون، پیشنهاد می‌کنیم به مجموعه آموزش‌های مقدماتی تا پیشرفته پایتون فرادرس مراجعه کنید که لینک آن در ادامه آورده شده است.

پیاده‌سازی میانگین متحرک نمایی در پایتون

برای پیاده‌سازی میانگین متحرک نمایی، وارد محیط برنامه‌نویسی شده و کتابخانه‌های مورد نیاز را فراخوانی می‌کنیم:

1import numpy as np
2import matplotlib.pyplot as plt

این دو کتابخانه برای کار با آرایه و مصورسازی استفاده خواهند شد.

حال Style مربوط به نمودارها را نیز تنظیم می‌کنیم:

1plt.style.use('ggplot')

حال یک مجموعه داده مصنوعی به شکل زیر تولید می‌کنیم:

1N = 200 # Signal Size
2T = np.linspace(0, 80, num=N) # Time
3S = 5*np.sin(8 + T/7) - 3*np.sin(5 + T/5) + 2*np.sin(3 + T/3) - np.sin(2 + T/2)

برای بررسی نمودار سیگنال ایجاد شده، یک Line Plot ایجاد می‌کنیم:

1plt.plot(T, S, ls='-', lw=1, marker='o', ms=2, label='Signal')
2plt.axhline(lw=1.2, c='k')
3plt.axvline(lw=1.2, c='k')
4plt.xlabel('T')
5plt.ylabel('Value')
6plt.legend()
7plt.show()

که در خروجی فوق نمودار زیرحاصل می‌شود.

نمودار در پایتون

حال برای پیاده‌سازی میانگین متحرک نمایی، ابتدا یک تابع ایجاد می‌کنیم و در ورودی سیگنال، طول بازه و مقدار $$r$$ را دریافت می‌کنیم:

1def EMA(S:np.ndarray, L:int, r:float=1):

حال مقدار $$\alpha$$ را محاسبه می‌کنیم:

1def EMA(S:np.ndarray, L:int, r:float=1):
2    a = (1 + r) / (L + r)

توجه داشته باشید که چون غالباً حالت $$r=1$$ استفاده می‌شود، مقدار پیش‌فرض برای این ورودی تعریف شده است.

حال ابتدا اندازه سیگنال را تعیین کرده و سپس اندازه میانگین متحرک حاصل را محاسبه می‌کنیم:

1def EMA(S:np.ndarray, L:int, r:float=1):
2    a = (1 + r) / (L + r)
3    nD0 = S.size
4    nD = nD0 - L + 1

حال یک آرایه خالی برای ذخیره مقادیر میانگین متحرک ایجاد می‌کنیم:

1def EMA(S:np.ndarray, L:int, r:float=1):
2    a = (1 + r) / (L + r)
3    nD0 = S.size
4    nD = nD0 - L + 1
5    M = np.zeros(nD)

حال اولین مقدار را به صورت میانگین معمولی از $$L$$ داده ابتدایی محاسبه می‌کنیم:

1def EMA(S:np.ndarray, L:int, r:float=1):
2    a = (1 + r) / (L + r)
3    nD0 = S.size
4    nD = nD0 - L + 1
5    M = np.zeros(nD)
6    M[0] = np.mean(S[:L])

حال می‌توانیم سایر مقادیر را داخل یک حلقه با استفاده از رابطه بازگشتی محاسبه کنیم:

1def EMA(S:np.ndarray, L:int, r:float=1):
2    a = (1 + r) / (L + r)
3    nD0 = S.size
4    nD = nD0 - L + 1
5    M = np.zeros(nD)
6    M[0] = np.mean(S[:L])
7    for i in range(1, nD):
8        M[i] = a*S[i+L-1] + (1-a)*M[i-1]
9    return M

به این ترتیب، مقادیر بعدی محاسبه، در آرایه $$M$$ ذخیره و در خروجی برگردانده می‌شوند.

برای استفاده از تابع به صورت زیر عمل می‌کنیم:

1M = EMA(S, 5)

برای مشاهده رفتار میانگین متحرک حاصل، به شکل زیر نموداری رسم می‌کنیم:

1plt.plot(T, S, ls='-', lw=1, marker='o', ms=2, label='Signal')
2plt.plot(T[-M.size:], M, ls='-', lw=0.9, c='teal', label='EMA(5)')
3plt.axhline(lw=1.2, c='k')
4plt.axvline(lw=1.2, c='k')
5plt.xlabel('T')
6plt.ylabel('Value')
7plt.legend()
8plt.show()

توجه داشته باشید که طول آرایه $$M$$ کوتاه‌تر از $$T$$ است. به همین دلیل، برای رسم نمودار باید تعدادی از اعضای ابتدایی آرایه $$T$$ حذف شود.

پس از اجرا، نمودار زیر نمایش داده می‌شود.

رسم میانگین نمایی در پایتون

به این ترتیب، مشاهده می‌کنیم که تابع نوشته‌ شده به خوبی عملکرد خود را نشان می‌دهد.

با افزایش مقدار $$L$$، تأخیر افزایش و در کنار آن، اعتبار نیز افزایش می‌یابد.

میانگین نمایی در پایتون

به این ترتیب، اثر $$L$$ مشهود است.

برای بررسی اثر $$r$$ نیز به شکل زیر عمل می‌کنیم:

1M1 = EMA(S, 7, r=-0.90)
2M2 = EMA(S, 7, r=0)
3M3 = EMA(S, 7, r=+3)
4
5plt.plot(T, S, ls='-', lw=1, marker='o', ms=2, label='Signal')
6plt.plot(T[-M1.size:], M1, ls='-', lw=0.9, c='teal', label='EMA(5, r=-0.90)')
7plt.plot(T[-M2.size:], M2, ls='-', lw=0.9, c='crimson', label='EMA(5, r=0)')
8plt.plot(T[-M3.size:], M3, ls='-', lw=0.9, c='lime', label='EMA(5, r=+3)')
9plt.axhline(lw=1.2, c='k')
10plt.axvline(lw=1.2, c='k')
11plt.xlabel('T')
12plt.ylabel('Value')
13plt.legend()
14plt.show()

که نمودار زیر برای کد فوق حاصل خواهد شد.

میانگین متحرک نمایی در پایتون

به این ترتیب، مشاهده می‌کنیم که در حالت $$r=-0.9$$ مقدار تأخیر به شدت افزایش یافته است. حالت‌های $$r=0$$ و $$r= 3 $$ هر دو از شرایط مناسبی برخوردار هستند و هر کدام بسته به نیاز می‌توانند استفاده شوند. تنظیم دو پارامتر $$L$$ و $$r$$ می‌تواند میانگین متحرک نمایی متناسب با نیاز ما را ایجاد کند.

حال برای مقایسه رفتار میانگین متحرک نمایی و میانگین متحرک ساده، تابع نوشته‌ شده برای میانگین متحرک ساده را وارد کد می‌کنیم:

1def SMA(S:np.ndarray, L:int):
2    nD0 = S.size
3    nD = nD0 - L + 1
4    M = np.zeros(nD)
5    for i in range(nD):
6        M[i] = np.mean(S[i:i + L])
7    return M

حال هر دو میانگین متحرک را در طول $$L=15$$ محاسبه می‌کنیم:

1sma = SMA(S, 15)
2ema = EMA(S, 15)

و برای رسم نمودار مناسب می‌نویسیم:

1plt.plot(T, S, ls='-', lw=1, marker='o', ms=2, label='Signal')
2plt.plot(T[-sma.size:], sma, ls='-', lw=0.9, c='teal', label='SMA(15)')
3plt.plot(T[-ema.size:], ema, ls='-', lw=0.9, c='lime', label='EMA(15)')
4plt.axhline(lw=1.2, c='k')
5plt.axvline(lw=1.2, c='k')
6plt.xlabel('T')
7plt.ylabel('Value')
8plt.legend()
9plt.show()

که در نهایت نمودار مورد نظر حاصل می‌شود.

پیاده سازی میانگین متحرک نمایی در پایتون

به این ترتیب، مشاهده می‌کنیم که میانگین متحرک نمایی زودتر از میانگین متحرک ساده به تغییرات روند واکنش می‌دهد.

جمع‌بندی

در این آموزش، میانگین متحرک نمایی در پایتون را مورد بررسی قرار دادیم. برای مطالعه بیشتر می‌توان موارد زیر را بررسی کرد:

  1. حذف روند از سیگنال (Detrending) با استفاده از میانگین متحرک نمایی
  2. بررسی اختلاف بین دو میانگین متحرک نمایی با طول‌های متفاوت و ارتباط آن با رفتار سیگنال
  3. آموزش دادن یک مدل خودهمبسته (Autoregressive) روی سیگنال و میانگین متحرک حاصل از آن و مقایسه دقت‌ها
  4. پیدا کردن روش‌هایی که می‌توان تأخیر میانگین متحرک نمایی را بیشتر از مقدار موجود کاهش داد

مطلبی که در بالا مطالعه کردید بخشی از مجموعه مطالب «آموزش پیاده‌سازی انواع میانگین های متحرک‌ در پایتون» است. در ادامه، می‌توانید فهرست این مطالب را ببینید:

بر اساس رای ۷ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
مجله فرادرس
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *