基于Facebook开发的Prophet项目预测铁路货运量(实例)

好久没有写东西了,今天写个之前做过的预测工作练手。

前情提要:Prophet项目是Facebook开发的一个时间序列的预测算法。算法使用起来简单,且对有明显内在规律的商业行为数据很有效, 因此常常被用于商业领域。上网看了很多关于Prophet代码和算法内核的介绍,但是缺少实际案例的应用。正好,之前我做过一些预测工作,当时将手上的数据放进Prophet代码中跑过一次,现在将经验和结论分享给大家。欢迎大家一起探讨。

今天我主要从3个方面来展这篇文章,主要是

一、算法原理(2个学习资料)

二、实例练习(全流程实操)

三、结论


话不多说,我们首先开始第一部分

一、算法原理

先去看官方介绍和Github上的情况,判断你是否对这个项目感兴趣。反正我当时正在做LSTM网络预测,一下子就被这个简单好上手以及出图的质量给吸引了,看看这个项目的测试数据的主图。

看到介绍页面的一顿操作之后,我开始疯狂搜索关于这个项目的算法介绍。后来对学习资料的甄别之后,主要用到的就是下面我提到的这两个链接,首先建议去看Facebook团队写的论文,文章的链接我放在这里:

https://peerj.com/preprints/3190/

然后,还想对算法原理进一步研究的话,可以参考张戎在知乎上分享的一篇文章,关于Prophet的数学模型介绍,很专业很完整很容易读懂。博主很赞!链接如下:

Facebook 时间序列预测算法 Prophet 的研究 - 知乎 (zhihu.com)


二、实例练习

数据描述:该数据来源于研究生师门的项目,是某路局的其中一个路段的日度集装箱运输量,时间跨度从2011年到2018年左右,一共2500余条。数据是从路局的原始数据计算合并得来的。首先,根据fbprophet的quick_start(Quick Start | Prophet)中将数据格式整理为下述的样子:

这里有两个要注意的地方:1、两列的名字必须是dsy 2、ds这一列必须是时间格式,y这一列必须是数字格式。准备好之后,导入Prophet以及实验数据。代码开始:

# 保证ds列的数据是时间格式
import pandas as pd
from fbprophet import Prophet
from pandas import read_excel
from datetime import datetime#我的数据ds列不是时间格式因此做一点处理
data=pd.read_excel(r'C:\Users\daily.xls',parse_dates = [0],index_col = [0])
df = data.reset_index()

在安装好Prophet项目的情形下,数据导入成功之后直接使用Prophet拟合数据,代码如下:

#使用prophet包拟合数据
import holidays
m = Prophet(daily_seasonality=False)
m.fit(df)

 下面会出现两行提示语:

INFO:numexpr.utils:NumExpr defaulting to 8 threads.

Out[6]:<fbprophet.forecaster.Prophet at 0x2399c64e8e0>

这是正常的,继续使用模型进行预测,代码运行后,出现预测期最后5天的预测时间点(本文输入数据最后日期是2018-2-4)。

# 预测未来30天,更改periods可以修改预测天数
future = m.make_future_dataframe(periods=30)
future.tail()

然后,你可以查看预测数据以及上下限等情况,下图是代码运行后,预测期的最后5天的结果,如果想查看更多数据可以在tail()的括号内填上查看的天数。:

# 预测数据,包含预测值,预测上下限
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()

然后查看预测结果:

# 绘制预测结果,黑色点是真实值,深蓝色是预测值,包含在外面的是置信区间
fig1 = m.plot(forecast)

 运行之后的图像包含2011年-2018年的数据,黑色点是实际数据,深蓝色点是预测数据,浅蓝色是预测数据的置信区间。可以注意到的是,2018年的尾部没有黑色点,那就是预测值。相较于Prophet项目案例中,我的预测时间较短。

 接着,就是fbprophet对原数据的趋势项、周内趋势以及年度趋势的计算结果:

# 查看预测的趋势项、每周的趋势项、每年的趋势项
fig2 = m.plot_components(forecast)

 

 接下来就是花式展实数据。他们设计了一个交互窗口很有意思,可以截取部分数据进行查看,也对数据进行了趋势、周期的过滤处理:

#这个部分就是对上述数据分析的衍生,绘图和交互的方式高级
from fbprophet.plot import plot_plotly, plot_components_plotly
plot_plotly(m, forecast)
plot_components_plotly(m, forecast)

上述就是基本预测结束了,Prophet预测之所以还是比较准确的原因在于季节性、节假日效应和回归量的分析。代码继续:

#内设函数对数据断点的处理
from fbprophet.plot import add_changepoints_to_plot
fig = m.plot(forecast)
a = add_changepoints_to_plot(fig.gca(), m, forecast)

 

 可以看到上述红线就是模型计算出来的断点位置,这些红线都位于数据的前80%,这是为了防止数据过度拟合,在适应自己数据的情况下可以修改。修改的内容包括上面代码中的changepoint_rang的范围,以及下面代码提到的趋势的灵活性:

#原代码是changepoint_prior_scale=0.05,值越大越灵活
m = Prophet(changepoint_prior_scale=0.001)
forecast = m.fit(df).predict(future)
fig = m.plot(forecast)

此时预测结果在图片上并没有显示出过多的变化,但是将预测结果导出之后可以对比其变化。

Prophet项目的优越点还在于,断点的位置除了可以模型计算出来外,还可以自行设置,尤其适合我们明知对货运量影响较大的日期,增加预测的准确性:

#我设置的是元旦节
m = Prophet(changepoints=['2011-01-01'])
forecast = m.fit(df).predict(future)
fig = m.plot(forecast)

 此时,预测又会根据输入的调整进行一些变化,但是大致图像不变,所以图片就不放了。

接下来就是一串比较长的代码,主要是为了设置节假日以及节假日的窗口(这个概念在论文和张戎

的知乎文章都可以去理解):

#这里我直接套用了项目介绍中的节假日日期,实际使用时更改成自己国家的时间,prophet里面也有一些内嵌假期设置
playoffs = pd.DataFrame({
  'holiday': 'playoff',
  'ds': pd.to_datetime(['2008-01-13', '2009-01-03', '2010-01-16',
                        '2010-01-24', '2010-02-07', '2011-01-08',
                        '2013-01-12', '2014-01-12', '2014-01-19',
                        '2014-02-02', '2015-01-11', '2016-01-17',
                        '2016-01-24', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
})
superbowls = pd.DataFrame({
  'holiday': 'superbowl',
  'ds': pd.to_datetime(['2010-02-07', '2014-02-02', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
})
holidays = pd.concat((playoffs, superbowls))

接着,又是数据趋势、假期、周度、年度的组成分析:

fig = m.plot_components(forecast)

 其中上图的第4个小图是关于年度周期的计算结果,Prophet是通过傅里叶来实现的。单独将此项挑出来调整参数:

from fbprophet.plot import plot_yearly
m = Prophet().fit(df)
a = plot_yearly(m)

 

 当上述曲线看起来太过平滑而丢失了部分信息时,可以将参数调整到20,此时图像看见就波动更多:

from fbprophet.plot import plot_yearly
m = Prophet(yearly_seasonality=20).fit(df)
a = plot_yearly(m)

也可以通过下面的参数调整,对季节性的频率进行调整,比如说可以调整为每周、每年、每月甚至每天:

#这里采用的是傅里叶指数是5=每月的周期性
m = Prophet(weekly_seasonality=False)
m.add_seasonality(name='monthly', period=30.5, fourier_order=5)
forecast = m.fit(df).predict(future)
fig = m.plot_components(forecast)

 同时,我们还可以考虑淡旺季对周期性的影响,通过添加一列布尔值来指示每个日期是旺季还是淡季:

def is_nfl_season(ds):
    date = pd.to_datetime(ds)
    return (date.month > 8 or date.month < 2)

df['on_season'] = df['ds'].apply(is_nfl_season)
df['off_season'] = ~df['ds'].apply(is_nfl_season)
#拒绝内置季节性的判断
m = Prophet(weekly_seasonality=False)
m.add_seasonality(name='weekly_on_season', period=7, fourier_order=3, condition_name='on_season')
m.add_seasonality(name='weekly_off_season', period=7, fourier_order=3, condition_name='off_season')

future['on_season'] = future['ds'].apply(is_nfl_season)
future['off_season'] = ~future['ds'].apply(is_nfl_season)
forecast = m.fit(df).predict(future)
fig = m.plot_components(forecast)

 此外,fbprophet还可以增加额外因素的回归,但是限制条件较多,感兴趣的话可以看看这个代码:https://nbviewer.org/github/nicolasfauchereau/Auckland_Cycling/blob/master/notebooks/Auckland_cycling_and_weather.ipynb

三、结论

1、总的来说,Prophet非常好上手,如果你有近千条数据就有不错的预测和分析效果。

2、我还做了ARIMA对数据的预测和分析,因为本质上这两种方式都是根据时间序列本身,来对未来的时间序列进行预测。结果是预测效果和季节性的分析结果都不如Prophet,不知道是不是因为数据集本身的适应性问题。

3、如果是作为商业用途,Prophet的图表很好看,适合展示。但是在由于已编程的内嵌函数,以及输入输出的数据结构的限制,使得Prophet的灵活性不足。要考虑将其他影响因素时,较难实现。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值