节假日效应是指在特定的节假日或纪念日期间,人们的行为和活动发生变化,从而影响到相应的时间序列数据(股票或者其他)。这种效应可能在多个领域产生影响,包括销售、交通、能源消耗等。
完整代码和数据可关注gzh’finance褪黑素’回复关键词【1013】免费+无套路 获取!
在时间序列分析中,考虑到节假日效应是十分重要的,因为这些特殊的日期通常伴随着与平时不同的消费模式、工作模式等。以下是一些关于节假日效应的要点:
-
季节性波动: 节假日通常会导致一些季节性波动,即短期内数据的波动。例如,在圣诞节、感恩节、新年等节日,零售销售通常会增加,这可能会在相关数据中表现为一个突然的峰值。
-
异常事件: 节假日也可能伴随着一些异常事件,例如促销活动、大型庆典等,这些事件可能对数据产生短期内的影响。
-
变化的消费模式: 人们在假期通常有不同的消费习惯。例如,春节期间,中国人通常有大量的购物和走亲访友,这可能导致交通、零售、物流等方面的数据出现波动。
在进行时间序列预测或分析时,应该考虑到这些节假日效应,以更好地理解和准确地预测数据的变化。通常的做法是在模型中引入虚拟变量(dummy variables)来表示是否是特定的节假日或纪念日,或者使用专门处理节假日效应的模型。这有助于模型更好地捕捉这些特殊日期的影响。
添加节假日
接下来,我们将看看添加节假日指标是否有助于提高模型的准确性。Prophet 自带了一个 Holiday Effects 参数,可以在训练之前提供给模型。
我们将使用 pandas 内置的 "USFederalHolidayCalendar "来获取节假日列表
from pandas.tseries.holiday import USFederalHolidayCalendar as calendar
cal = calendar()
train_holidays = cal.holidays(start=pjme_train.index.min(),
end=pjme_train.index.max())
test_holidays = cal.holidays(start=pjme_test.index.min(),
end=pjme_test.index.max())
# Create a dataframe with holiday, ds columns
pjme['date'] = pjme.index.date
pjme['is_holiday'] = pjme.date.isin([d.date() for d in cal.holidays()])
holiday_df = pjme.loc[pjme['is_holiday']] \
.reset_index() \
.rename(columns={'Datetime':'ds'})
holiday_df['holiday'] = 'USFederalHoliday'
holiday_df = holiday_df.drop(['PJME_MW','date','is_holiday'], axis=1)
holiday_df.head()
holiday_df['ds'] = pd.to_datetime(holiday_df['ds'])
# Setup and train model with holidays
model_with_holidays = Prophet(holidays=holiday_df)
model_with_holidays.fit(pjme_train.reset_index() \
.rename(columns={'Datetime':'ds',
'PJME_MW':'y'}))
<fbprophet.forecaster.Prophet at 0x7b177264eb00>
预测假期
# Predict on training set with model
pjme_test_fcst_with_hols = \
model_with_holidays.predict(df=pjme_test.reset_index() \
.rename(columns={'Datetime':'ds'}))
节假日效应
fig2 = model_with_holidays.plot_components(pjme_test_fcst_with_hols)
添加节假日后的错误指标
令人惊讶的是,添加节假日后错误变得更严重了。
mean_squared_error(y_true=pjme_test['PJME_MW'],
y_pred=pjme_test_fcst_with_hols['yhat'])
43854192.0930847
mean_absolute_error(y_true=pjme_test['PJME_MW'],
y_pred=pjme_test_fcst_with_hols['yhat'])
5188.894101894623
def mean_absolute_percentage_error(y_true, y_pred):
"""Calculates MAPE given y_true and y_pred"""
y_true, y_pred = np.array(y_true), np.array(y_pred)
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
mean_absolute_percentage_error(y_true=pjme_test['PJME_MW'],
y_pred=pjme_test_fcst_with_hols['yhat'])
16.54692166186205
比较节假日预测模型
让我们绘制有节假日和无节假日的 7 月 4 日预测模型。有节假日的预测模型看起来更准确。
# Plot the forecast with the actuals
f, ax = plt.subplots(1)
f.set_figheight(5)
f.set_figwidth(15)
ax.scatter(pjme_test.index, pjme_test['PJME_MW'], color='r')
fig = model.plot(pjme_test_fcst, ax=ax)
ax.set_xbound(lower='07-01-2015', upper='07-7-2015')
ax.set_ylim(0, 60000)
plot = plt.suptitle('Week of July 4th Forecast vs Actuals non-Holiday Model')
# Plot the forecast with the actuals
f, ax = plt.subplots(1)
f.set_figheight(5)
f.set_figwidth(15)
ax.scatter(pjme_test.index, pjme_test['PJME_MW'], color='r')
fig = model.plot(pjme_test_fcst_with_hols, ax=ax)
ax.set_xbound(lower='07-01-2015', upper='07-7-2015')
ax.set_ylim(0, 60000)
plot = plt.suptitle('Week of July 4th Forecast vs Actuals Holiday Model')
7月4日的比较错误
该日期的错误率已经下降。
jul4_test = pjme_test.query('Datetime >= 20160407 and Datetime < 20160408')
jul4_pred = pjme_test_fcst.query('ds >= 20160407 and ds < 20160408')
jul4_pred_holiday_model = pjme_test_fcst_with_hols.query('ds >= 20160407 and ds < 20160408')
mean_absolute_error(y_true=jul4_test['PJME_MW'],
y_pred=jul4_pred['yhat'])
2160.288544948389
mean_absolute_error(y_true=jul4_test['PJME_MW'],
y_pred=jul4_pred_holiday_model['yhat'])
2159.810721702265
所有假日的错误
- 假日错误率上升!这是意料之外的。
holiday_list = holiday_df['ds'].tolist()
hols_test = pjme_test.query('Datetime in @holiday_list')
hols_pred = pjme_test_fcst.query('ds in @holiday_list')
hols_pred_holiday_model = pjme_test_fcst_with_hols.query('ds in @holiday_list')
mean_absolute_error(y_true=hols_test['PJME_MW'],
y_pred=hols_pred['yhat'])
5225.708533591265
mean_absolute_error(y_true=hols_test['PJME_MW'],
y_pred=hols_pred_holiday_model['yhat'])
5110.4751401038375
按节假日识别错误
- 我们可以看到,在这个模型中,不同的节假日有不同的反应。如果我们能具体识别节假日,而不是把它们都归结为 “USFederalHolidays”,模型的表现会更好。
holiday_df['date'] = holiday_df['ds'].dt.date
for hol, d in holiday_df.groupby('date'):
holiday_list = d['ds'].tolist()
hols_test = pjme_test.query('Datetime in @holiday_list')
if len(hols_test) == 0:
continue
hols_pred = pjme_test_fcst.query('ds in @holiday_list')
hols_pred_holiday_model = pjme_test_fcst_with_hols.query('ds in @holiday_list')
non_hol_error = mean_absolute_error(y_true=hols_test['PJME_MW'],
y_pred=hols_pred['yhat'])
hol_model_error = mean_absolute_error(y_true=hols_test['PJME_MW'],
y_pred=hols_pred_holiday_model['yhat'])
diff = non_hol_error - hol_model_error
print(f'Holiday: {hol:%B %d, %Y}: \n MAE (non-holiday model): {non_hol_error:0.1f} \n MAE (Holiday Model): {hol_model_error:0.1f} \n Diff {diff:0.1f}')
每种预测的误差图
- 我们可以看到,我们的两个模型都能很好地概括,但在高峰需求日时却很难做到。
- 似乎对很多天的预测都不足。
ax = pjme_test_fcst.set_index('ds')['yhat'].plot(figsize=(15, 5),
lw=0,
style='.')
pjme_test['PJME_MW'].plot(ax=ax,
style='.',
lw=0,
alpha=0.2)
plt.legend(['Forecast','Actual'])
plt.title('Forecast vs Actuals')
plt.show()
完整代码和数据可关注gzh’finance褪黑素’回复关键词【1013】免费+无套路 获取!