[深度学习项目] - 时间序列预测 (3)


Forecasting Principles And Practice

移动平均法

Moving Average smoothing(MA)
原理: 用过去N个时刻的观测值的平均值作为对未来时刻的预测
y t + h = 1 N ∑ i = 0 N − 1 y t − i y_{t+h}=\frac{1}{N}\sum_{i=0}^{N-1} y_{t-i} yt+h=N1i=0N1yti

使用过去N个时刻的均值来直接作为预测结果,不考虑外部变量的影响。 可以用来判断预测的难易程度。

import pandas as pd

store_sales = pd.read_csv("store_sales.csv", parse_dates=["week"])
stock_price = pd.read_csv("stock_price.csv", parse_dates=["date"])

print(stock_price.head(3))

T = 10 # 10 days for stock price

date_end = pd.to_datetime("2022-01-01")
date_begin = date_end - pd.Timedelta(T-1, unit="D")  # 根据日期,往前推算 pd.Timedelta

FB_price = stock_price[(stock_price["company"]=="FB")&
                       (stock_price["date"]>=date_begin)&
                       (stock_price["date"]<=date_end)]["price"].mean()
print(FB_price)

当我们想要预测未来h天的结果时,我们会将 该过去N个时刻的均值 重复h次 作为未来的预测结果

ETS

简单ETS算法 (SETS)

Exponential Smoothing,指数平滑算法, 简称ETS。


简单平滑预测可以看作 朴素预测法平均预测法 的一种中和。

朴素预测法: y ^ T + h ∣ T = y T \hat{y}_{T+h|T}=y_T y^T+hT=yT,我们认为 对未来的所有预测都等于该序列的最后一个观测值。
平均预测法: y ^ T + h ∣ T = 1 T ∑ t = 1 T y t \hat{y}_{T+h|T}=\frac{1}{T}\sum_{t=1}^T y_t y^T+hT=T1t=1Tyt,我们认为 对未来的所有预测 与之前所有的观测值都有关,而且关联程度相等。 【移动平均和平均预测类似,只不过是选用了一定的时间区间。】

而ETS的思想就是 认为比较近的数据 对于未来 提供的信息更大一些,但是过去的数据也对于未来的数据提供一定的帮助。
因此,ETS:
y ^ T + h ∣ T = α y T + α ( 1 − α ) y T − 1 + α ( 1 − α ) 2 y T − 2 + α ( 1 − α ) 3 y T − 3 + … \hat{y}_{T+h|T}=\alpha y_T+\alpha(1-\alpha)y_{T-1}+\alpha(1-\alpha)^2 y_{T-2}+\alpha (1-\alpha)^3 y_{T-3}+\dots y^T+hT=αyT+α(1α)yT1+α(1α)2yT2+α(1α)3yT3+
其中, 0 ≤ α ≤ 1 0\leq \alpha \leq 1 0α1是平滑参数。

也可以写作:
Forecast equation: y ^ T + h ∣ T = ℓ t \hat{y}_{T+h|T}=\ell_t y^T+hT=t
Smoothing equation: ℓ t = α y t + ( 1 − α ) ℓ t − 1 \ell_t=\alpha y_t +(1-\alpha) \ell_{t-1} t=αyt+(1α)t1


带趋势的ETS算法

(霍尔特的线性趋势法)
我们将预测函数分别成两部分 水平项趋势项
Forecast equation: y ^ t + h ∣ t = ℓ t + h b t \hat{y}_{t+h|t}=\ell_t+hb_t y^t+ht=t+hbt
其中 ℓ t \ell_t t表示水平项,用于描述该时间序列的水平的估计值, b t b_t bt用于描述该时间序列在t时刻的趋势(斜率)的估计, α \alpha α是水平 0 ≤ α ≤ 1 0 \leq \alpha \leq 1 0α1的平滑参数, β ∗ \beta^* β表示趋势 0 ≤ β ∗ ≤ 1 0\leq \beta^* \leq1 0β1的平滑参数。
我们相当于将时间序列看作 当前水平,和上升(下降)趋势【取决于 b t b_t bt的正负】。

水平和趋势的更新也源于数据的迭代:
Level equation: ℓ t = α y t + ( 1 − α ) ( ℓ t − 1 + b t − 1 ) \ell_t=\alpha y_t +(1-\alpha) (\ell_{t-1}+b_{t-1}) t=αyt+(1α)(t1+bt1)

水平项实际上 取决于 时刻t的真值 和 t-1时刻在t时刻的预测值。

Trend equation: b t = β ∗ ( ℓ t − ℓ t − 1 ) + ( 1 − β ∗ ) b t − 1 b_t = \beta^* (\ell_t -\ell_{t-1}) + (1-\beta^*)b_{t-1} bt=β(tt1)+(1β)bt1
趋势项实际上 取决于 当前预测值与上一时刻预测值的差值(当前的上升趋势) 和 之前的趋势。

import pandas as pd
from statsmodels.tsa.exponential_smoothing.ets import ETSModel
import matplotlib.pyplot as plt

stock_price = pd.read_csv("stock_price.csv", parse_dates=["date"])


date_end = pd.to_datetime("2022-01-01")
data_train = stock_price[ (stock_price["company"]=="FB")&
                          (stock_price["date"]<=date_end)]

# 将日期变成 训练集的index
data_train = data_train[ ["date","price"] ].set_index("date")

# 构建模型
model = ETSModel( data_train["price"] )
fit = model.fit()

# 获取数据
data_train["fit_price"] = fit.fittedvalues
data_plot = data_train[ (data_train.index>="2021-09-18")  ]

plt.plot( data_plot["price"], ".-", label="actual" )
plt.plot( data_plot["fit_price"], ",-", label="fit" )
plt.legend()
plt.xticks(rotation=90)
plt.show()

# 预测未来h天
h = 10
pred_price = fit.get_prediction(start=data_train.shape[0]+1,
                                end=data_train.shape[0]+h)

print(pred_price.summary_frame()[ ["mean"] ])

>>>
>            mean
2423  336.763047
2424  336.763047
2425  336.763047
2426  336.763047
2427  336.763047
2428  336.763047
2429  336.763047
2430  336.763047
2431  336.763047
2432  336.763047

在这里插入图片描述

带季节性的ETS算法

(预测效果不佳)

时间序列的平稳性定义

平稳性定义( stationary ): 如果截取某个时间序列中任意一段数值(起码大于5/10个时间点),它的均值,方差不随时间发生变化,那么这个时间序列是平稳的。

平稳时间序列的好处是 可以用移动平均的方式做预测。

可以采用自相关系数来估计序列的平稳性。 如果自相关系数没有迅速衰减到0,那么序列是不平稳的。

使用差分的方式,获取平稳的时间序列

y t 1 = y t − y t − 1 y_t^1=y_t-y_{t-1} yt1=ytyt1

########### difference
FB_price = stock_price[ stock_price["company"]=="FB" ].sort_values("date").reset_index()
FB_price["price_last"] = FB_price["price"].shift(1)
FB_price["price_diff"] = FB_price["price"] - FB_price["price_last"]

plt.plot( FB_price["date"], FB_price["price_diff"] )
plt.show()

plot_acf(FB_price["price_diff"])
plt.show()

FB difference
通过差分一直上升的 FB股价 转换成了 均值近似恒定 (虽然方差变化仍不小) 的时间序列。

FB_difference_ACF
也可以看到 ACF 自相关系数非常小。

随机散步模型(random walk model)

如果时序的差分结果是 稳定的,那么结果就是 带有固定噪音,并且均值恒定的白噪声,我们将白噪声可以记作 c + ϵ t c+\epsilon_t c+ϵt 【也叫残差,表示随机变量影响 所造成的偏差。】其中c表示的是噪声的均值, ϵ t \epsilon_t ϵt表示方差。
如果一阶差分是稳定的,那么:
y t 1 = y t − y t − 1 = c + ϵ t y^1_t=y_t-y_{t-1}=c+\epsilon_t yt1=ytyt1=c+ϵt
那么,我们就可以得到:
y t = y t − 1 + c + ϵ t y_t=y_{t-1}+c+\epsilon_t yt=yt1+c+ϵt

也就是当前时刻的值 只取决于 过去一个时刻的值,和一个上升/下降的趋势 ( c ).

季节差分

当前观测值和过去相同周期的观测值之间的差值 是 季节差分:
y t 1 = y t − y t − m y^1_t=y_t-y_{t-m} yt1=ytytm
如果季节差分数据是 白噪声,那么我们就可以将模型看作
y t = y t − m + ϵ t + c y_t=y_{t-m}+\epsilon_t+c yt=ytm+ϵt+c


有时,数据 整体上有向上或者向下的趋势,同时也有季节性变化,我们可以既进行季节性差分,也进行一阶差分。如果有明显的季节性因素 一般我们先进行季节性差分。 如果差分后结果稳定,那么就不再进行差分,否则再进行差分。【另外,我们在差分之前可以对数据进行处理,Log指数化处理等,让数据的变化趋势呈现线性】


多阶差分

为了获取平稳的时间序列,我们通常会用 差分的方式
一阶差分:
y t 1 = y t − y t − 1 = y t − B y t = ( 1 − B ) y t y^1_t=y_t-y_{t-1}=y_t-By_t=(1-B)y_t yt1=ytyt1=ytByt=(1B)yt
其中B表示 “backshift”,表示回退
B y t By_t Byt表示由 y t y_t yt回退一个时刻,所以表示 y t − 1 y_{t-1} yt1
二阶差分:
y t 2 = y t 1 − y t − 1 1 = y t − y t − 1 − y t − 1 + y t − 2 = y t − 2 B y t + B 2 y t = ( 1 − B ) 2 y t y^2_t=y^1_t-y^1_{t-1}=y_t-y_{t-1}-y_{t-1}+y_{t-2}=y_t-2By_t+B^2y_t=(1-B)^2 y_t yt2=yt1yt11=ytyt1yt1+yt2=yt2Byt+B2yt=(1B)2yt
d阶差分:
y t d = ( 1 − B ) d y t y^d_t=(1-B)^d y_t ytd=(1B)dyt


季节差分和一阶差分同时作用:
( 1 − B ) ( 1 − B m ) y t = ( 1 − B − B m + B m + 1 ) y t = y t − y t − 1 − y t − m + y t − m − 1 (1-B)(1-B^m)y_t=(1-B-B^m+B^{m+1})y_t=y_t-y_{t-1}-y_{t-m}+y_{t-m-1} (1B)(1Bm)yt=(1BBm+Bm+1)yt=ytyt1ytm+ytm1


Arima算法

Autoregressive Integrated Moving Average Model [差分 整合 移动平均 自回归 模型]

Autoregression models

我们可以使用自回归模型来对模型进行预测, A R ( p ) AR(p) AR(p) 表示 p阶的自回归模型。

y t = c + ϕ 1 y t − 1 + ϕ 2 y t − 2 + ⋯ + ϕ p y t − p + ϵ t y_t =c+\phi_1 y_{t-1}+\phi_2 y_{t-2} +\dots +\phi_p y_{t-p} +\epsilon_t yt=c+ϕ1yt1+ϕ2yt2++ϕpytp+ϵt

p阶数表示了 y t y_t yt跟多远的自身变量有关。

  • 在AR(1)中,如果 ϕ 1 = 0 \phi_1=0 ϕ1=0,那么模型就是白噪声。 ϕ 1 = 1 , c = 0 \phi_1=1,c=0 ϕ1=1,c=0,那么 y t y_t yt就是随机漫步算法。 ϕ 1 < 0 \phi_1<0 ϕ1<0,那么 y t y_t yt就在均值c上震荡。
  • AR(1)中, 平稳性的条件: − 1 < ϕ 1 < 1 -1<\phi_1<1 1<ϕ1<1
  • AR(2)中, 平稳性的条件: − 1 < ϕ 2 < 1 , ϕ 1 + ϕ 2 < 1 , ϕ 2 − ϕ 1 < 1 -1<\phi_2<1,\phi_1+\phi_2<1,\phi_2-\phi_1<1 1<ϕ2<1,ϕ1+ϕ2<1,ϕ2ϕ1<1

Moving Average models

该Moving average models和之前的 MA算法完全不同。
该moving average model使用 过去预测的误差来进行回归预测。MA(q) 表示q阶的模型。
y t = c + ϵ t + θ 1 ϵ t − 1 + θ 2 ϵ t − 2 + ⋯ + θ q ϵ t − q y_t=c+\epsilon_t+\theta_1 \epsilon_{t-1}+ \theta_2 \epsilon_{t-2} + \dots + \theta_q \epsilon_{t-q} yt=c+ϵt+θ1ϵt1+θ2ϵt2++θqϵtq
每一个 y t y_t yt都可以看作 过去预测误差的移动平均

我们可以将 任意的 AR(q) 看作 M A ( ∞ ) MA(\infty) MA()模型:
y t = ϕ 1 y t − 1 + ϵ t = ϕ 1 ( ϕ 1 y t − 2 + ϵ t − 1 ) + ϵ t = ϕ 1 2 y t − 2 + ϕ 1 ϵ t − 1 + ϵ t = ϕ 1 3 y t − 3 + ϕ 1 2 ϵ t − 2 + ϕ 1 ϵ t − 1 + ϵ t = ϵ t + ϕ 1 ϵ t − 1 + ϕ 1 2 ϵ t − 2 + ϕ 1 3 ϵ t − 3 + … y_t=\phi_1 y_{t-1}+\epsilon_t =\phi_1(\phi_1 y_{t-2}+\epsilon_{t-1})+\epsilon_t=\phi_1^2 y_{t-2}+\phi_1 \epsilon_{t-1}+\epsilon_t=\phi_1^3 y_{t-3}+\phi_1^2 \epsilon_{t-2}+\phi_1 \epsilon_{t-1}+\epsilon_t=\epsilon_t +\phi_1 \epsilon_{t-1}+\phi_1^2 \epsilon_{t-2}+\phi_1^3 \epsilon_{t-3}+\dots yt=ϕ1yt1+ϵt=ϕ1(ϕ1yt2+ϵt1)+ϵt=ϕ12yt2+ϕ1ϵt1+ϵt=ϕ13yt3+ϕ12ϵt2+ϕ1ϵt1+ϵt=ϵt+ϕ1ϵt1+ϕ12ϵt2+ϕ13ϵt3+

Non-seasonal ARIMA models

y t ′ = c + ϕ 1 y t − 1 ′ + ϕ 2 y t − 2 ′ + ⋯ + ϕ p y t − p ′ + θ 1 ϵ t − 1 + ⋯ + θ q ϵ t − q + ϵ t y'_t=c+\phi_1 y'_{t-1}+ \phi_2 y'_{t-2}+\dots + \phi_p y'_{t-p}+\theta_1 \epsilon_{t-1}+\dots+\theta_{q} \epsilon_{t-q}+\epsilon_t yt=c+ϕ1yt1+ϕ2yt2++ϕpytp+θ1ϵt1++θqϵtq+ϵt
y t ′ y'_t yt已经差分了d次,我们将上述ARIMA 记作 ARIMA(p,d,q)

ARIMA 结合了 差分, 自回归 和 moving average 三部分。

不同的模型对应的ARIMA参数
White noiseARIMA(0,0,0) y t = ϵ t y_t=\epsilon_t yt=ϵt
Random WalkARIMA(0,1,0) with no constant
Random Walk with driftARIMA(0,1,0) with a constant
AutoregressionARIMA(p,0,0)
Moving averageARIMA(0,0,q)

我们使用 backshift 对上式进行修改
( 1 − ϕ 1 B − ⋯ − ϕ p B p ) ( 1 − B ) d y t = c + ( 1 + θ 1 B + ⋯ + θ q B q ) ϵ t (1-\phi_1 B-\dots-\phi_p B^p) (1-B)^dy_t =c +(1+\theta_1 B+\dots+\theta_q B^q)\epsilon_t (1ϕ1BϕpBp)(1B)dyt=c+(1+θ1B++θqBq)ϵt
AR ( p ) · d differences = c + MA(q)

meaning of different parameters

d=0 说明了 y t y_t yt本身就是平稳的,会围绕c上下波动。
d=1 说明了 y t ′ y_t' yt是稳定的, 当c=0时,说明 y t ′ y_t' yt会趋于0,说明 y t y_t yt会趋于稳定值;如果 c ≠ 0 c\neq0 c=0 y t ′ y_t' yt趋于固定值, y t y_t yt会呈现直线。
d=2 说明了 y t ′ ′ y_t'' yt′′是稳定的,当c=0时,说明 y t ′ ′ y_t'' yt′′会趋于0,然后 y t ′ y_t' yt会趋于稳定值, y t y_t yt会呈现直线; 如果 c ≠ 0 c\neq 0 c=0,那么 y t y_t yt会呈现二次曲线。

determine p,q

可以使用 ACF 和 PACF 来确定p和q。
PACF 可以在判断 y t y_t yt y t − k y_{t-k} ytk之间相关性时 排除掉 因为 y t − 1 , y t − 2 , … , y t − k + 1 y_{t-1},y_{t-2},\dots,y_{t-k+1} yt1,yt2,,ytk+1造成的影响。
有时候我们可以使用 auto arima来获取参数,而不是自己直接 定义 p,d,q。 但是auto arima并不能探索到整个范围内,结果可能不是最优解。

Homewrok

分别使用 ETS算法,ARIMA算法 预测所有公司的2022-01-01及以后的股价,每次往后预测1天,并且计算预测MAPE。

ETS 预测

[‘FB’ ‘TWTR’ ‘ETSY’ ‘SNAP’ ‘PINS’]

import pandas as pd
from statsmodels.tsa.exponential_smoothing.ets import ETSModel
from sklearn.metrics import mean_absolute_percentage_error as mape
#####################  ETS
stock_price = pd.read_csv("stock_price.csv", parse_dates=["date"])
COMPANY_NAME = "FB"

date_end = pd.to_datetime("2022-01-01")
pre_data_train = stock_price[ stock_price["company"]==COMPANY_NAME].sort_values("date")
all_predict_date = pre_data_train[ pre_data_train["date"]>=date_end  ]["date"]
# print(predict_date)
predict_result = []
true_result = []
mape_errors = []


for i,predict_date in all_predict_date.items():
    data_train = pre_data_train[pre_data_train["date"]<predict_date][ ["date","price"] ].set_index("date")
    model = ETSModel( data_train["price"] )
    fit = model.fit()
    price = fit.get_prediction(start=data_train.shape[0]+1,end=data_train.shape[0]+1)
    predict_result.append(price.predicted_mean)
    true_result.append(pre_data_train[ pre_data_train["date"]==predict_date ]["price"])
    mape_errors.append(  mape(true_result[-1],predict_result[-1]) )

plt.subplot(2,1,1)
plt.plot(all_predict_date,predict_result,".-",label="fit")
plt.plot(all_predict_date,true_result,".-",label="actual")
plt.xticks(rotation=90)
plt.ylabel(COMPANY_NAME+" stock price predict")
plt.xlabel("date")
plt.legend()

plt.subplot(2,1,2)
plt.plot(all_predict_date,mape_errors,".-")
plt.ylabel("MAPE")
plt.xlabel("date")
plt.xticks(rotation=90)

plt.show()

在这里插入图片描述
在这里插入图片描述

ARIMA

from statsmodels.tsa.arima.model import ARIMA
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

########### difference
COMPANY = "TWTR"
date_end = pd.to_datetime("2022-01-01")
data_price = stock_price[ (stock_price["company"]==COMPANY)&
                          (stock_price["date"]<date_end)  ].sort_values("date").reset_index()
data_price["price_last"] = data_price["price"].shift(1)
data_price["price_diff"] = data_price["price"] - data_price["price_last"]

######## first difference
plt.figure(1)
plt.ylabel("first difference of "+COMPANY)
plt.plot( data_price["date"], data_price["price_diff"] )

###### acf & pacf
# 确定 d, q
plot_acf(data_price["price_diff"])


# 决定 p
plot_pacf(data_price["price"])
plt.show()

first diff
acf
pacf

from statsmodels.tsa.arima.model import ARIMA
predict_result = []
true_result = []
mape_errors = []

for i,predict_date in all_predict_date.items():
    data_train = pre_data_train[pre_data_train["date"]<predict_date]["price"].values
    model = ARIMA(data_train, order=(1, 1, 0)) # p, d, q
    fit = model.fit()

    price = fit.forecast(1)[0]
    predict_result.append(price)
    true_result.append(pre_data_train[ pre_data_train["date"]==predict_date ]["price"])
    mape_errors.append(  mape([true_result[-1]],[predict_result[-1]]) )


plt.subplot(2,1,1)
plt.plot(all_predict_date,predict_result,".-",label="fit")
plt.plot(all_predict_date,true_result,".-",label="actual")
plt.xticks(rotation=90)
plt.ylabel(COMPANY+" stock price predict")
plt.xlabel("date")
plt.legend()

plt.subplot(2,1,2)
plt.plot(all_predict_date,mape_errors,".-")
plt.ylabel("MAPE")
plt.xlabel("date")
plt.xticks(rotation=90)

plt.show()

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值