第一篇文章咱们用一个小时从安装prophet 到完成预测,而且咱们将结果对比了naive的季节信号分解(这个能够看做是简版的prohet,纯原创),发现效果略逊色于naive的方法。bash
后来咱们用lightgbm 再次预测一样的数据集,发现结果远远赛过prophet。post
咱们也对比了咱们的预测结果与kaggle上公开的方法与测试结果,发现咱们的lightgbm的结果更好,多是由于咱们作了必定参数调优。学习
咱们的prophet 结果和kaggle大神的预测结果同样“好” (相比于lghtgbm都不好)。测试
可是facebook 大厂旗下prophet的super power 真的弱爆了吗?ui
对于深度学习来讲,没有标准化会直接让你的模型跑飞。url
对于树模型来讲,异常值有的时候让你的feature 变得莫名奇妙的重要。spa
对于prohet 来讲,彷佛没有什么特殊的注意事项,也没有要清理数据的必要。3d
可是问题每每就隐藏在其中。code
代码依然保持不变。component
import pandas as pd
from fbprophet import Prophet, make_holidays
from sklearn.metrics import mean_absolute_error
from fbprophet.plot import add_changepoints_to_plot
file = 'data/PJME_hourly.csv'
raw_df = pd.read_csv(file)
print(raw_df.head(5))
复制代码
将string 时间转成pandas Timestamp 格式,而且预览head 和tail。
dt_format = '%Y-%m-%d %H:%M:%S'
raw_df['Datetime'] = pd.to_datetime(raw_df['Datetime'], format=dt_format)
raw_df.rename({'PJME_MW': 'y', 'Datetime': 'ds'}, axis=1, inplace=True)
print(raw_df.tail(5))
print(raw_df.describe())
复制代码
能够看到时间从2002年到2018年,并且顺序彷佛也对。
ds y
8734 2002-01-01 01:00:00 30393.0
8735 2002-01-01 02:00:00 29265.0
8736 2002-01-01 03:00:00 28357.0
8737 2002-01-01 04:00:00 27899.0
8738 2002-01-01 05:00:00 28057.0
ds y
140250 2018-08-02 20:00:00 44057.0
140251 2018-08-02 21:00:00 43256.0
140252 2018-08-02 22:00:00 41552.0
140253 2018-08-02 23:00:00 38500.0
140254 2018-08-03 00:00:00 35486.0
复制代码
为了保险起见,咱们对时间轴进行排序。
raw_df = raw_df.sort_values(by=['ds']) # sort by time
print(raw_df.tail(5))
print(raw_df.describe())
复制代码
split_dt = pd.Timestamp('2015-01-01 00:00:00')
train_df = raw_df[raw_df['ds'] < split_dt]
test_df = raw_df[raw_df['ds'] >= split_dt]
复制代码
这里咱们依然采用默认的参数。
model = Prophet()
model= model.fit(train_df)
y_hat = model.predict(test_df)
y_test = model.predict(test_df)
fig = model.plot(y_test)
a = add_changepoints_to_plot(fig.gca(), model, y_test)
print(mean_absolute_error(test_df['y'].values, y_test['yhat']))
model.plot_components(y_test)
复制代码
咱们能够看到偏差结果居然是3105。相比于咱们第2篇的模型以及kaggle大神的5183要好不少。
可是咱们的代码几乎没有变,也没有任何的参数调优啊?
3105.4775855473995
复制代码
其实提升的缘由很简单,数据有bug!!
其实不是bug,应该是脏数据,并且隐藏的有点深。 通常咱们预览head(5) 和tail(5),看到顺序是一致的,咱们内心就会默认这个数据是按照时间顺序排列的。其实不是!
当咱们预览前30行数据的时候就会发现,日期居然是倒序的。并且中间还加了莫名其妙的1月1号。
咱们代码中不起眼的一句 sort_value 居然让偏差巨幅下降。
而prophet 须要根据时间的前后顺序,提取必定的周期信号。因此观测对象在时间轴上的前后顺序无疑起着决定性做用。
相比于第3 篇介绍的lightgbm,它不会考虑时间的连续性,所以须要咱们显式的告诉它一些时间特性,好比咱们提取day,year 等信息。
因此prophet 没有那么差,只不过咱们须要对数据进行清理,尤为是时间前后顺序。
Datetime,PJME_MW
2002-12-31 01:00:00,26498.0
2002-12-31 02:00:00,25147.0
2002-12-31 03:00:00,24574.0
2002-12-31 04:00:00,24393.0
2002-12-31 05:00:00,24860.0
2002-12-31 06:00:00,26222.0
2002-12-31 07:00:00,28702.0
2002-12-31 08:00:00,30698.0
2002-12-31 09:00:00,31800.0
2002-12-31 10:00:00,32359.0
2002-12-31 11:00:00,32371.0
2002-12-31 12:00:00,31902.0
2002-12-31 13:00:00,31126.0
2002-12-31 14:00:00,30368.0
2002-12-31 15:00:00,29564.0
2002-12-31 16:00:00,29098.0
2002-12-31 17:00:00,30308.0
2002-12-31 18:00:00,34017.0
2002-12-31 19:00:00,34195.0
2002-12-31 20:00:00,32790.0
2002-12-31 21:00:00,31336.0
2002-12-31 22:00:00,29887.0
2002-12-31 23:00:00,28483.0
2003-01-01 00:00:00,27008.0
2002-12-30 01:00:00,27526.0
2002-12-30 02:00:00,26600.0
2002-12-30 03:00:00,26241.0
2002-12-30 04:00:00,26213.0
2002-12-30 05:00:00,26871.0
复制代码
默认的prophet 配置可让咱们快速的创建baseline,可是对于时间序列,咱们还能够自定义参数。 这里咱们贴出代码,方便你们参考。
好比咱们能够添加美国的假日信息。该假日信息会传递给prophet holiday 参数
holidays = make_holidays.make_holidays_df(range(2002, 2018, 1), 'US')
复制代码
对于同一个周期,咱们能够定义一些条件进行区分。
raw_df['on_season'] = (raw_df['ds'].dt.month > 4) | (raw_df['ds'].dt.month < 8)
raw_df['off_season'] = ~(raw_df['ds'].dt.month > 4) | (raw_df['ds'].dt.month < 8)
raw_df['night'] = (raw_df['ds'].dt.hour > 20) | (raw_df['ds'].dt.hour < 8)
raw_df['daylight'] = ~(raw_df['ds'].dt.hour > 20) | (raw_df['ds'].dt.hour < 8)
raw_df['weekend'] = raw_df['ds'].dt.week > 5
raw_df['workday'] = ~raw_df['weekend']
复制代码
咱们能够将模型默认的yearly_seasonality,weekly_seasonality,daily_seasonality 设置为False,而后添加本身定义的周期,或者叠加咱们定义的条件。 此外,对于每个周期,咱们能够定义他的权重(prior_scale)。
model = Prophet(
holidays=holidays,
changepoint_prior_scale=0.5,
holidays_prior_scale=10,
seasonality_prior_scale=10,
yearly_seasonality=False,
weekly_seasonality=False,
daily_seasonality=False)
model.add_country_holidays(country_name='US')
model.add_seasonality(name='half_daily',period=0.5,fourier_order=12)
model.add_seasonality(name='daylight_daily',period=1,fourier_order=24,condition_name='daylight')
model.add_seasonality(name='night_daily',period=1,fourier_order=24,condition_name='night')
model.add_seasonality(name='weekend_weekly',period=7,fourier_order=14,prior_scale=10,condition_name='weekend')
model.add_seasonality(name='workday_weekly',period=7,fourier_order=14,prior_scale=10,condition_name='workday')
model.add_seasonality(name='on_season_half_year',period=182.625,fourier_order=24,prior_scale=10,condition_name='on_season')
model.add_seasonality(name='off_season_half_year',period=182.625,fourier_order=24,prior_scale=10,condition_name='off_season')
model.add_seasonality(name='on_season_year',period=365.25,fourier_order=24,prior_scale=10,condition_name='on_season')
model.add_seasonality(name='off_season_year',period=365.25,fourier_order=24,prior_scale=10,condition_name='off_season')
复制代码
运行结果为:
3043.2366637873324
复制代码
咱们依然附上分解的结果:
本文成功解决了prophet 建模时遇到的坑,将偏差从5000多下降到3000多。 经过本文能够了解到: