29.7.7 自回归模型
自回归(AR)模型是一种用于时间序列分析的统计模型,通过利用过去的观测值来预测未来的值。自回归(AR)模型是一种回归分析,其中输出变量(Y)被建模为其自身过去值的线性函数加上一个噪声项(epsilon)。AR模型用于描述和分析时间相关的现象,如股票价格、经济周期和生态现象。
在本项目中,通过下面的代码绘制自相关函数(ACF)和偏自相关函数(PACF)的可视化图表,以帮助确定时间序列数据中的自回归(AR)和移动平均(MA)模型的阶数。
fig, (ax1, ax2, ax3) = plt.subplots(3, figsize=(15, 10))
# 绘制原始序列的偏自相关函数图
plot_pacf(df.High, auto_ylims=True, lags=5, ax=ax1, title='Original Series')
ax1.spines['top'].set_visible(False)
# 绘制一次差分序列的偏自相关函数图
plot_pacf(df.High.diff().dropna(), auto_ylims=True, lags=5, ax=ax2, title='1st Order Differencing')
ax2.spines['top'].set_visible(False)
# 绘制二次差分序列的偏自相关函数图
plot_pacf(df.High.diff().diff().dropna(), auto_ylims=True, lags=5, ax=ax3, title='2nd Order Differencing')
ax3.spines['top'].set_visible(False)
plt.tight_layout()
plt.show()
执行效果如图29-21所示,绘制了原始序列、一次差分序列和二次差分序列的PACF图,并设置了图表的样式以增强可读性。
图29-21 自相关函数(ACF)和偏自相关函数(PACF)的可视化图
此时可以在ACF和PACF图中观察到明显的截止点,或当滞后的相关值降到某个阈值以下时,停止寻找p和q值。这通常表示数据的模式已经被捕捉,添加更多的滞后值不会提供额外的信息。
注意:为什么ARIMA比ARMA更好?
在技术上,我们可以使用ARMA模型,但ARIMA(自回归积分滑动平均)模型比ARMA模型更好,具体原因如下:
- 更灵活:ARIMA模型能够处理非平稳数据,而ARMA模型无法做到这一点。
- 差分处理:ARIMA模型可以使用差分来去除趋势和季节性,而ARMA模型无法做到这一点。
- 季节性成分:ARIMA模型还允许在模型中包含季节性成分,而ARMA模型无法做到这一点。
29.7.8 数据分割
在拟合模型之前需要对数据进行分割,时间序列数据的分割方法与典型的机器学习算法数据集分割有所不同。由于时间序列具有顺序性,我们不希望模型学习到由于随机分割而产生的空隙,因此我们将基于年份进行数据分割。
(1)在本项目中,选择了2020年之前的数据作为训练数据,剩余的数据用于测试工作。
train = df[df.index.year<2020]
test = df[df.index.year>=2020]
(2)下面代码绘制了训练集和测试集的“High”列数据,并通过一个垂直虚线来标识训练集和测试集的分界点。
# 创建一个图形和一个轴
fig, ax = plt.subplots()
# 在轴上绘制训练集的High列数据
train['High'].plot(ax=ax, label='Training Set', title='Data Train/Test Split', linewidth=3)
# 在轴上绘制测试集的High列数据
test['High'].plot(ax=ax, label='Test Set', linewidth=3)
# 移除图表的顶部和右边的边框
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False);
# 在图表上添加一个垂直线,表示分界点
ax.axvline('2020-01-01', color='black', ls='--', lw=2)
# 添加图例
ax.legend(['Training Set', 'Test Set'])
# 标记x轴
ax.set_xlabel('Years')
# 显示图表
plt.show()
执行后绘制了一个时间序列图,显示了训练集和测试集的“High”列数据,执行效果如图29-22所示。展示了训练集和测试集的线条,移除了图表的上边框和右边框,并在图表上添加了图例和x轴标签,最后展示了图表。
图29-22 时间序列图
(3)通过下面的代码打印输出了训练集和测试集的行数,这可以帮助确认数据集的分割情况以及确保数据被正确地划分为训练集和测试集。
print(f'There are {train.shape[0]} rows in the training set and {test.shape[0]} rows in the test set')
执行后会输出:
There are 3867 rows in the training set and 770 rows in the test set
(4)下面这段代码的功能是选择数据集中用于训练和测试的特征列,并查看训练集的前几行数据。具体步骤如下:
- 定义外生特征(exogenous_features),即用于模型的额外特征列,包括 'Open'、'High' 和 'Low'。
- 从训练集 (train) 和测试集 (test) 中选择前四列作为分析数据列。
- 显示训练集的前几行数据,以检查数据的格式和内容。
# 选择用于模型的外生特征
exogenous_features = ['Open', 'High', 'Low']
# 从训练集和测试集中选择前四列
train = train[train.columns[:4]]
test = test[test.columns[:4]]
# 显示训练集的前几行数据
train.head()
执行后会输出:
Date Open High Low Close
2004-08-23 00:00:00-04:00 2.527778 2.729730 2.515015 2.710460
2004-08-24 00:00:00-04:00 2.771522 2.839840 2.728979 2.737738
2004-08-25 00:00:00-04:00 2.783784 2.792793 2.591842 2.624374
2004-08-26 00:00:00-04:00 2.626627 2.702703 2.599600 2.652653
2004-08-27 00:00:00-04:00 2.626376 2.701451 2.619119 2.700450
(5)下面代码的功能是使用 ARIMA 模型拟合训练数据,具体实现步骤如下:
- 忽略警告信息:使用 warnings.filterwarnings('ignore') 来忽略警告信息,避免干扰输出。
- 定义并拟合 ARIMA 模型:创建一个 ARIMA 模型实例,指定自变量(endog)为训练集的 'Close' 列,外生变量(exog)为定义的外生特征列('Open'、'High' 和 'Low'),并设置 ARIMA 模型的参数为 (1, 1, 1),即 p=1,d=1,q=1。
- 查看模型摘要:使用 model_fit.summary() 输出模型的详细拟合结果。
import warnings
warnings.filterwarnings('ignore')
# 创建并拟合 ARIMA 模型
model = sm.tsa.arima.ARIMA(endog=train['Close'], exog=train[exogenous_features], order=(1, 1, 1)) #(p,d,q)
model_fit = model.fit()
# 输出模型摘要
model_fit.summary()
执行后会输出:
SARIMAX Results
Dep. Variable: Close No. Observations: 3867
Model: ARIMA(1, 1, 1) Log Likelihood 1770.361
Date: Mon, 23 Jan 2023 AIC -3528.722
Time: 21:35:07 BIC -3491.163
Sample: 0 HQIC -3515.385
- 3867
Covariance Type: opg
coef std err z P>|z| [0.025 0.975]
Open -0.5615 0.006 -96.368 0.000 -0.573 -0.550
High 0.7750 0.005 150.396 0.000 0.765 0.785
Low 0.7870 0.005 166.993 0.000 0.778 0.796
ar.L1 -0.0658 0.008 -7.778 0.000 -0.082 -0.049
ma.L1 -0.9998 0.003 -307.385 0.000 -1.006 -0.993
sigma2 0.0234 0.000 89.339 0.000 0.023 0.024
Ljung-Box (L1) (Q): 0.19 Jarque-Bera (JB): 9485.55
Prob(Q): 0.67 Prob(JB): 0.00
Heteroskedasticity (H): 8.22 Skew: 0.10
Prob(H) (two-sided): 0.00 Kurtosis: 10.67
现在模型已经训练完成,我们可以开始在训练数据上进行预测,随后在测试数据上进行预测。
(6)下面代码的功能是对训练集数据进行预测,并将实际的“Close”值与预测值绘制在同一图上,以便可视化模型在训练集上的表现。
# 在训练集中添加预测结果
train['Predictions'] = model_fit.predict()
# 绘制实际值与预测值的最后50行数据
train[['Close', 'Predictions']][-50:].plot()
plt.title('Predictions on Training Set') # 标题:训练集上的预测
plt.gca().spines['right'].set_visible(False) # 隐藏右侧边框
plt.gca().spines['top'].set_visible(False) # 隐藏顶部边框
plt.show()
上述代码的实现步骤包括:将预测结果添加到训练集中,绘制实际值和预测值的最后50行数据,并显示可视化图表,执行效果如图29-23所示。在图中显示了训练集数据中“Close”列的实际值与模型预测的“Predictions”值的对比。图表展示了这两个系列的最后50个数据点,以便比较实际值与预测值的差异。
图29-23 训练集上的预测图
(7)下面代码用于在测试集上进行预测,利用已经训练好的 ARIMA 模型对测试集进行逐步预测,并将预测结果存储在“Forecast”列中。然后,通过可视化图表展示了测试集中“Close”列的实际值与预测的“Forecast”值的对比,展示了最后50个数据点,以便进行可视化比较。
# 在测试集上进行预测
forecast = [model_fit.forecast(exog=test[exogenous_features].iloc[i]).values[0] for i in range(len(test))]
test['Forecast'] = forecast
test[['Close','Forecast']][-50:].plot()
plt.title('预测在测试集上')
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.show()
执行效果如图29-24所示。
图29-24 测试集上的预测图
(8)计算多变量 ARIMAX 模型的均方根误差(RMSE),用于评估模型在测试集上的预测性能。通过计算预测值与实际值之间的均方根误差,提供了模型预测准确度的量化指标,并将结果打印出来。
rmse = np.sqrt(mean_squared_error(test['Close'],test['Forecast']))
print(f'The RMSE for Multivariate ARIMAX is {round(rmse,4)}')
执行后会输出:
The RMSE for Multivariate ARIMAX is 0.7477
(9)下面的test 是一个数据框(DataFrame),通过test存储模型在测试集上的预测结果和实际值。
test
执行后会输出:
Open High Low Close Forecast
Date
2020-01-02 00:00:00-05:00 66.789497 67.032997 66.606499 66.969498 66.876931
2020-01-03 00:00:00-05:00 67.420502 68.433998 67.324501 68.433998 68.173472
2020-01-06 00:00:00-05:00 67.400002 68.687500 67.365997 68.075996 68.414108
2020-01-07 00:00:00-05:00 67.581497 69.916000 67.550003 69.890503 69.409122
2020-01-08 00:00:00-05:00 70.023003 70.175003 69.578003 69.755501 69.834908
... ... ... ... ... ...
2023-01-17 00:00:00-05:00 90.849998 92.190002 90.129997 92.120003 91.376339
2023-01-18 00:00:00-05:00 92.059998 92.250000 90.050003 91.290001 90.680473
2023-01-19 00:00:00-05:00 92.139999 92.800003 90.639999 91.120003 91.526122
2023-01-20 00:00:00-05:00 90.720001 93.610001 90.629997 93.050003 92.943345
2023-01-23 00:00:00-05:00 95.099998 98.300003 95.019997 98.019997 97.573603
这样,我们就完成了使用 ARIMA 模型的预测工作。接下来,将使用深度学习来预测股票价格。