1.5.11 时间序列验证
(1)下面的代码首先创建了一个名为 'base' 的新列,该列包含了实际 'Close' 值的前一个时间点的值,用于与 ARIMA 模型的预测值进行比较。然后,通过使用指数函数(np.exp)将结果中的 'forecast'、'lower_interval' 和 'upper_interval' 列转换回原始价格的形式,以便进行可视化。这个操作将 DataFrame 的最后五行转换为原始价格。
result['base'] = result['Close'].shift()
result['base'].iloc[0] = y_train.to_frame().Close[-1]
result.apply(lambda x: np.exp(x).astype('float16')).tail(5)
执行后会输出:
(2)定义函数plot_forecast,该函数用于绘制ARIMA模型的预测结果,接受如下所示的参数。
- fc:预测的时间序列。
- train:训练集的时间序列。
- test:测试集的时间序列。
- upper:可选参数,预测的上界时间序列。
- lower:可选参数,预测的下界时间序列。
函数plot_forecast将这些序列转换为 pd.Series 对象,然后使用 Plotly 创建一个线图,显示训练集、测试集、以及 ARIMA 模型的预测结果。图表包括标题、坐标轴标签等设置,并且使用不同的颜色表示不同的时间序列。
def plot_forecast(fc, train, test, upper=None, lower=None):
# shift train predictions for plotting
is_confidence_int = isinstance(upper, np.ndarray) and isinstance(lower, np.ndarray)
# Prepare plot series
fc_series = pd.Series(fc, index=test.index)
lower_series = pd.Series(upper, index=test.index) if is_confidence_int else None
upper_series = pd.Series(lower, index=test.index) if is_confidence_int else None
names = cycle(['Training data','Actual close price','Forecast close price'])
plotdf = pd.DataFrame({'date': log_df.index,
'training': train,
'actual': test,
'Forecast': fc_series})
fig = px.line(plotdf,x=plotdf['date'], y=[plotdf['training'],plotdf['actual'],
plotdf['Forecast']],
labels={'value':'Close price','date': 'Date'})
fig.update_layout(title_text='Prediction avec ARIMA (1, 1, 1)',
plot_bgcolor='white', font_size=15, font_color='black', legend_title_text='Close Price')
fig.for_each_trace(lambda t: t.update(name = next(names)))
fig.update_xaxes(showgrid=False)
fig.update_yaxes(showgrid=False)
fig.show()
(3)下面的代码调用了函数plot_forecast,传递了 ARIMA 模型的预测结果 (result.forecast)、训练集 (y_train)、测试集 (y_test) 以及上下界的时间序列。
plot_forecast(result.forecast, y_train, y_test, np.array(result.upper_interval), np.array(result.lower_interval))
执行后将绘制一个使用 Plotly 创建的曲线图,展示了训练集、测试集和 ARIMA 模型的预测结果,同时显示了预测的上下界。如图7-8所示,曲线图具有标题、坐标轴标签等设置信息,并使用不同的颜色表示不同的时间序列。
图7-8 训练集、测试集和 ARIMA 模型的预测结果曲线图
(4)函数get_mape(y_true, y_pred)用于计算平均绝对百分比误差(MAPE),通过计算公式
来衡量预测值与真实值之间的平均百分比误差。
def get_mape(y_true, y_pred):
'''takes y_true, y_pred (pandas series)
returns mean absolute percentage error'''
mape = 100*((y_true - y_pred)/y_true).abs().mean()
return round(mape, 2)
(5)函数get_mase(y_true, y_pred, y_train)用于计算平均绝对缩放误差(MASE),通过计算公式
来衡量测试集上的平均绝对误差与训练集上的平均绝对误差的比例。
def get_mase(y_true, y_pred, y_train):
'''takes y_true, y_pred (pandas series)
returns mean absolute scaled error'''
mae_test = (y_true - y_pred).abs().mean()
y_t = y_train
y_t_1 = y_train.shift(-1)
mae_train = (y_t - y_t_1).abs().mean()
return round(mae_test/mae_train, 2)
(6)下面的代码计算了模型和基线在测试数据上的 MAPE(平均绝对百分比误差)和 MASE(平均绝对尺度误差)。MAPE 衡量了预测值与实际值之间的百分比误差,而 MASE 衡量了模型相对于基线的性能,考虑了数据的尺度。
print('mape model:', get_mape(result.Close, result.forecast))
print('mape baseline:', get_mape(result.Close, result.base))
print('')
print('mase model:', get_mase(result.Close, result.forecast, y_train.to_frame().Close))
print('mase baseline', get_mase(result.Close, result.base, y_train.to_frame().Close))
比较这两个指标可以帮助评估模型相对于简单基线的预测效果,执行后会输出:
mape model: 0.25
mape baseline: 0.24
mase model: 0.97
mase baseline 0.95
(7)下面的这段代码将之前使用对数转换的数据进行指数还原,以便进行可视化。然后,通过调用函数plot_forecast生成了包含原始数据、训练数据、测试数据、预测值以及置信区间上下界的交互式图,用于直观地比较模型的预测效果。
forecast_recons = np.exp(result.forecast)
train_recons = np.exp(y_train)
test_recons = np.exp(y_test)
lower_recons = np.array(np.exp(result.lower_interval))
upper_recons = np.array(np.exp(result.upper_interval))
# plt
plot_forecast(forecast_recons, train_recons, test_recons, upper_recons, lower_recons)
执行效果如图7-8所示。
图7-8 比较模型的预测效果