独家 | 手把手教你用Python的Prophet库进行时间序列预测

时间:2022-07-28
本文章向大家介绍独家 | 手把手教你用Python的Prophet库进行时间序列预测,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

作者:Jason Brownlee

翻译:殷之涵

校对:吴振东

本文长度为4800字,建议阅读10+分钟

本文为大家介绍了如何在Python中使用由Facebook开发的Prophet库进行自动化的时间序列预测,以及如何评估一个由Prophet库所搭建的时间序列预测模型的性能。

时间序列预测通常具有十足的挑战性,这是由时间序列预测的方法众多、且每种方法都包含很多不同的超参数所造成的。

Prophet是一个专门为预测单变量时间序列数据集而设计的开源库。如果你想要自动化地寻找一组好的模型超参数,从而对拥有趋势及季节性周期变化结构的数据做出有效预测,使用Prophet来处理是一件轻而易举的事情——它本来就是为此而设计的。

在本教程中,你将去探索如何使用这个由Facebook开发的Prophet库进行时间序列预测。

完成这个教程后,你将会学到:

  • Prophet是一个由Facebook开发的开源库,专为单变量时间序列数据的自动化预测而设计;
  • 如何拟合Prophet模型,并使用模型进行样本内及样本外预测;
  • 如何使用通过留出法所划分出的不参与训练的数据集来评估Prophet模型的性能。

那我们就开始吧。

教程概览

本教程共有3个部分,它们分别是:

  • Prophet预测库介绍
  • 汽车销量数据集
    1. 加载数据并进行统计描述
    2. 加载数据并进行图表绘制
  • 使用Prophet进行汽车销量预测
    1. 拟合Prophet模型
    2. 进行样本内预测
    3. 进行样本外预测
    4. 手动对预测模型进行性能评估

Prophet预测库介绍

Prophet,或称“Facebook Prophet”,是一个由Facebook开发的用于单变量时间序列预测的开源库。

Prophet实现的是一个可加的时间序列预测模型,支持趋势、季节性周期变化及节假日效应。

“该模型所实现的是一个基于可加模型的时间序列数据预测过程,拟合了年度、周度、日度的季节性周期变化及节假日效应的非线性趋势。”

— Package ‘prophet’, 2019.

Prophet的设计初衷就是简单易用、完全自动,因此适合在公司内部场景中使用,例如预测销量、产能等。

这里有一篇不错的概览,介绍了Prophet及它的功能:

Prophet: forecasting at scale, 2017

https://research.fb.com/blog/2017/02/prophet-forecasting-at-scale/

这个库的接口在R和Python中均可被调用,本篇将会聚焦于Python中的使用方法。

第一步是使用Pip对Prophet库进行安装,操作如下:

sudo pip install fbprophet

接下来,我们需要确认Prophet库已经被正确安装。

我们可以在Python中导入该库并打印它的版本号。完整的例子见下方:

# check prophet version
import fbprophet
# print version number
print('Prophet %s' % fbprophet.__version__)

运行上述例子并打印Prophet库的版本号。你应该安装的是如下或更高的版本。

Prophet 0.5

现在我们已经安装好了Prophet,接下来就选择一个数据集并使用这个库来进行探索。

汽车销量数据集

我们将会使用汽车月度销量数据集。

这是一个标准的单变量时间序列数据集,同时包含趋势及季节性周期变化。它包含108个月的汽车销量数据,使用基准模型对其进行预测便能达到3235(辆汽车)的平均绝对误差,从而提供了较低的误差限制。

无需下载数据集,我们会在每个例子中自动下载它。

Monthly Car Sales Dataset (csv)

https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv

Monthly Car Sales Dataset Description

https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.names

1. 加载数据并进行统计描述

首先,让我们来加载数据并且对它进行统计描述。

Prophet要求输入的数据为Pandas DataFrames的形式。所以我们要用Pandas库进行数据加载和统计描述。

我们可以通过调用Pandas库中的read_csv()函数,从而直接通过URL加载数据。接下来我们可以对数据集的行数和列数进行统计,并查看一下前几行数据。

完整的例子如下:

# load the car sales dataset
from pandas import read_csv
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# summarize shape
print(df.shape)
# show first few rows
print(df.head())

运行示例代码,我们将会得到数据集的行数和列数,以及前5行数据。

正如我们预期的一样,数据集包含108行(分别代表108个月)及2列(字段)的数据。第一列是日期,第二列是销量。

需要注意的是,输出中的第一列所显示的行标(index)并不是原始数据集中的一部分,而是Pandas中对数据行进行排列时使用的一个颇有帮助的工具而已。

(108, 2)
     Month  Sales
0  1960-01   6550
1  1960-02   8728
2  1960-03  12026
3  1960-04  14395
4  1960-05  14587

2. 加载数据并绘制图表

一个时间序列数据集只有被绘制出来后才会有意义。

绘制时间序列能够让我们观察到趋势、季节性周期、异常波动等变化是否真的存在。它能带给我们一些对数据的“感觉”。

我们可以调用Pandas库中的plot()函数轻松地对DataFrame进行绘制。

完整的示例见下方:

# load and plot the car sales dataset
from pandas import read_csv
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# plot the time series
df.plot()
pyplot.show()

运行示例代码,我们能够得到一张显示时间序列的图。

我们能够清晰地观察到销量随时间变化的趋势以及月度周期变化规律。这些都是我们希望预测模型能够考虑在内的规律。

现在我们已经熟悉了这一数据集,那么就来探索一下如何使用Prophet库进行预测吧。

使用Prophet进行汽车销量预测

在这一部分中,我们将会探索如何使用Prophet进行汽车销量数据预测。

让我们从将数据拟合成模型开始吧。

1. 拟合Prophet模型

想要使用Prophet进行预测,首先我们需要定义和配置一个Prophet()对象,然后通过调用fit()函数并将数据传入该函数,从而对数据集进行拟合。

Prophet()对象会使用所传入的参数来配置你想要的模型,例如增长和季节性周期等变化的类型。默认情况下,模型几乎会自动找出所有的内容。

fit()函数接受时间序列数据以DataFrame的形式被传入,同时对这个DataFrame也有特殊的格式要求:第一列必须被命名为“ds”并包含日期信息;第二列必须被命名为“y”并包含观测结果。

这就意味着我们需要修改原数据集中的列名,同时把第一列转为日期时间对象(date-time objects)——前提是如果你没有事先做好这一步的话(可以在调用read_csv函数时通过输入正确的参数来完成这个操作)。

举例来说,我们可以把已加载的汽车销量数据集修改成自己想要的样式,如下所示:

...
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])

关于如何将汽车销量数据集拟合成一个Prophet模型,完整的示例如下:

# fit prophet model on the car sales dataset
from pandas import read_csv
from pandas import to_datetime
from fbprophet import Prophet
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# define the model
model = Prophet()
# fit the model
model.fit(df)

运行示例代码,加载数据集,将DataFrame调整成需要的格式,并拟合出一个Prophet模型。

默认情况下,这个库会输出拟合过程中所产生的大量结果信息——我通常觉得这样不是很好,因为这容易让开发者忽略输出中那些真正重要的信息。

但不管怎么说,输出信息还是总结了模型拟合过程中发生的情况,尤其是运行的优化过程。

INFO:fbprophet:Disabling weekly seasonality. Run prophet with weekly_seasonality=True to override this.
INFO:fbprophet:Disabling daily seasonality. Run prophet with daily_seasonality=True to override this.
Initial log joint probability = -4.39613
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
      99       270.121    0.00413718       75.7289           1           1      120
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
     179       270.265    0.00019681       84.1622   2.169e-06       0.001      273  LS failed, Hessian reset
     199       270.283   1.38947e-05       87.8642      0.3402           1      299
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
     240       270.296    1.6343e-05       89.9117   1.953e-07       0.001      381  LS failed, Hessian reset
     299         270.3   4.73573e-08       74.9719      0.3914           1      455
    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes
     300         270.3   8.25604e-09       74.4478      0.3522      0.3522      456
Optimization terminated normally:
  Convergence detected: absolute parameter change was below tolerance

在接下来的部分中,我就不再展示如上输出了。那么我们就开始预测吧。

2. 进行样本内预测

对历史数据进行预测可能是有用的。

也就是说,我们可以对那些被当作训练模型时的输入数据进行预测。理想情况下,模型之前就已经见过了这些数据从而能做出完美的预测。

然而,情况并非如此,因为模型在试图对数据中的所有情况进行归纳总结。

这叫做样本内(训练集的样本内)预测,通过观察它的结果我们能够得知模型的性能如何——模型对训练数据的学习效果如何。

通过调用predict()函数并传入一个DataFrame就可以进行预测了,该DataFrame包含一个名为“ds”的列及所有待预测日期时间的行。

创建预测DataFrame有很多种方式。在这里,我们循环一年中的所有日期(即数据集中的最后12个月),并为每一个月创建一个字符串。接下来我们把这个日期列表转为DataFrame,并把字符串转为日期时间对象。

...
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1968-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])

这样我们就有了可以作为predict()函数所需的参数被传入的DataFrame,然后进行预测计算。

Predict()函数的计算结果是一个包含多个列的DataFrame,其中最重要的列或许是被预测的日期时间(“ds”列)、预测值(“yhat”列)以及预测值的上下限(“yhat_lower”列和“yhat_upper”列)——为预测的不确定性提供区间估计。

如下方示例,我们可以打印出预测结果的前几行:

...
# summarize the forecast
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())

Prophet同样提供了一个内置工具,用于对训练数据预测结果进行可视化。

对模型调用plot()函数并传入预测结果DataFrame即可实现。训练数据集的图将会被绘制出来,被预测日期的预测值及其上下限也会被展示在图中。

...
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())
# plot forecast
model.plot(forecast)
pyplot.show()

汇总以上代码,一个样本内预测的完整示例如下:

# make an in-sample forecast
from pandas import read_csv
from pandas import to_datetime
from pandas import DataFrame
from fbprophet import Prophet
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# define the model
model = Prophet()
# fit the model
model.fit(df)
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1968-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])
# use the model to make a forecast
forecast = model.predict(future)
# summarize the forecast
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())
# plot forecast
model.plot(forecast)
pyplot.show()

运行示例代码,我们将得到数据集最后12个月的预测值。

前5个月的预测如下,我们能够观察到这些预测值和原数据集中的真实值相差并不大。

          ds          yhat    yhat_lower    yhat_upper
0 1968-01-01  14364.866157  12816.266184  15956.555409
1 1968-02-01  14940.687225  13299.473640  16463.811658
2 1968-03-01  20858.282598  19439.403787  22345.747821
3 1968-04-01  22893.610396  21417.399440  24454.642588
4 1968-05-01  24212.079727  22667.146433  25816.191457

接下来是绘制一个结果图,我们可以观察到训练数据被使用黑色圆点显示在图中,预测值被使用蓝线显示,预测值的上下限为蓝色阴影区域。

3. 进行样本外预测

在实践中,我们往往是想构建一个预测模型来对训练数据以外的情况进行预测。这被称为样本外预测。

我们可以通过和进行样本内预测时同样的方法来实现这一目标,只要指定一段不同的预测期间即可。

在本例中,训练数据集以外的日期区间从1969-01开始。

...
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1969-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])

汇总以上代码,一个样本外预测的完整示例如下:

# make an out-of-sample forecast
from pandas import read_csv
from pandas import to_datetime
from pandas import DataFrame
from fbprophet import Prophet
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# define the model
model = Prophet()
# fit the model
model.fit(df)
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1969-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds']= to_datetime(future['ds'])
# use the model to make a forecast
forecast = model.predict(future)
# summarize the forecast
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())
# plot forecast
model.plot(forecast)
pyplot.show()

运行示例代码,我们将得到汽车销售量样本外数据的预测值。

下方是前5行预测的打印结果,可是我们很难知道它们是不是合理的预测值。

          ds          yhat    yhat_lower    yhat_upper
0 1969-01-01  15406.401318  13751.534121  16789.969780
1 1969-02-01  16165.737458  14486.887740  17634.953132
2 1969-03-01  21384.120631  19738.950363  22926.857539
3 1969-04-01  23512.464086  21939.204670  25105.341478
4 1969-05-01  25026.039276  23544.081762  26718.820580

绘制一张图能帮助我们评估由训练数据得到的预测值是否合理。

至少从肉眼上来看,我们对下一年(1969年)的预测还是比较合理的。

4. 手动对预测模型进行性能评估

对预测模型的性能进行客观评估至关重要。

这一目标可以通过留出一部分数据不参与模型训练来实现,例如最后12个月的数据。接下来,我们就可以用一部分的数据对模型进行拟合,然后对事先预留不参与训练的数据进行预测,并计算误差度量,例如预测中的平均绝对误差——这是模拟出的样本外预测过程。

这个误差度量的值能够帮助我们评估模型在进行样本外预测时的表现水准。

我们可以通过创建一个在原数据集基础上去除最后12个月数据的新DataFrame来实现这一过程。

...
# create test dataset, remove last 12 months
train = df.drop(df.index[-12:])
print(train.tail())

然后对最后12个月进行预测。

我们可以提取出预测值和来自原始数据集中的期望值(真实值),使用scikit-learn库计算它们之间的平均绝对误差度量。

...
# calculate MAE between expected and predicted values for december
y_true = df['y'][-12:].values
y_pred = forecast['yhat'].values
mae = mean_absolute_error(y_true, y_pred)
print('MAE: %.3f' % mae)

同样的,如果把期望值(真实值)和预测值绘制在一张图中,它会帮助我们了解样本外预测和已知真实值之间的匹配程度。

...
# plot expected vs actual
pyplot.plot(y_true, label='Actual')
pyplot.plot(y_pred, label='Predicted')
pyplot.legend()
pyplot.show()

汇总以上代码,以下示例演示了如何使用留出集来评估一个Prophet模型的性能。

# evaluate prophet time series forecasting model on hold out dataset
from pandas import read_csv
from pandas import to_datetime
from pandas import DataFrame
from fbprophet import Prophet
from sklearn.metrics import mean_absolute_error
from matplotlib import pyplot
# load data
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv'
df = read_csv(path, header=0)
# prepare expected column names
df.columns = ['ds', 'y']
df['ds']= to_datetime(df['ds'])
# create test dataset, remove last 12 months
train = df.drop(df.index[-12:])
print(train.tail())
# define the model
model = Prophet()
# fit the model
model.fit(train)
# define the period for which we want a prediction
future = list()
for i in range(1, 13):
date = '1968-%02d' % i
future.append([date])
future = DataFrame(future)
future.columns = ['ds']
future['ds'] = to_datetime(future['ds'])
# use the model to make a forecast
forecast = model.predict(future)
# calculate MAE between expected and predicted values for december
y_true = df['y'][-12:].values
y_pred = forecast['yhat'].values
mae = mean_absolute_error(y_true, y_pred)
print('MAE: %.3f' % mae)
# plot expected vs actual
pyplot.plot(y_true, label='Actual')
pyplot.plot(y_pred, label='Predicted')
pyplot.legend()
pyplot.show()

运行示例代码,我们能看到训练数据集的后几行。

这就确认了模型训练过程止于1967年的最后一个月,而1968整年的数据被用作了留出集。

           ds      y
91 1967-08-01  13434
92 1967-09-01  13598
93 1967-10-01  17187
94 1967-11-01  16119
95 1967-12-01  13713

接下来,我们来计算预测日期区间的绝对平均误差。

在本例中,我们可以看到误差大约为1336辆(汽车),与对同一日期区间的销售量进行预测基准模型的3235辆(汽车)相比,我们所训练出的模型误差更低,既表现更好。

MAE: 1336.814

最后,我们来绘制一张真实值vs预测值的对比图。在本例中,我们能观察到预测结果很好地拟合了真实情况。模型表现得不错,给出的预测也比较合理。

Prophet库同样提供了一些能够评估模型性能及绘制预测结果的自动化工具,尽管它们在本例的数据上并不是很有效。

更多阅读

如果你想对本文主题做更深入的了解,这里有更多资料可供学习参考:

Prophet Homepage

https://facebook.github.io/prophet/

Prophet GitHub Project

https://github.com/facebook/prophet

Prophet API Documentation

https://facebook.github.io/prophet/docs/

Prophet: forecasting at scale, 2017

https://research.fb.com/blog/2017/02/prophet-forecasting-at-scale/

Forecasting at scale, 2017

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

Car Sales Dataset

https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-car-sales.csv

Package ‘prophet’, R Documentation

https://cran.r-project.org/web/packages/prophet/prophet.pdf

总结

在本教程中,你将探索如何使用这个由Facebook开发的Prophet库进行时间序列预测。

完成这个教程后,你将会学到:

  • Prophet是一个由Facebook开发的开源库,专为单变量时间序列数据的自动化预测而设计;
  • 如何拟合Prophet模型,并使用模型进行样本内及样本外预测;
  • 如何使用通过留出法所划分出的不参与训练的数据集来评估Prophet模型的性能。

原文标题:

Time Series Forecasting With Prophet in Python

原文链接:

https://machinelearningmastery.com/time-series-forecasting-with-prophet-in-python/

编辑:黄继彦

校对:林亦霖

译者简介

殷之涵(Jane),研究生毕业于康奈尔大学生物统计与数据科学专业,本科毕业于普渡大学精算与应用统计专业。目前在腾讯担任数据科学家,主要负责腾讯视频用户增长&市场营销数据科学方面的工作;此前在京东任数据分析师一年半,负责通过指标体系搭建、统计分析、数据挖掘和机器学习建模来驱动决策、制定并落地亿级用户的精细化运营策略。对数据科学充满兴趣和热情,希望通过多年勤恳深耕成长为真正的领域专家。

翻译组招募信息

工作内容:需要一颗细致的心,将选取好的外文文章翻译成流畅的中文。如果你是数据科学/统计学/计算机类的留学生,或在海外从事相关工作,或对自己外语水平有信心的朋友欢迎加入翻译小组。

你能得到:定期的翻译培训提高志愿者的翻译水平,提高对于数据科学前沿的认知,海外的朋友可以和国内技术应用发展保持联系,THU数据派产学研的背景为志愿者带来好的发展机遇。

其他福利:来自于名企的数据科学工作者,北大清华以及海外等名校学生他们都将成为你在翻译小组的伙伴。

点击文末“阅读原文”加入数据派团队~

转载须知

如需转载,请在开篇显著位置注明作者和出处(转自:数据派ID:DatapiTHU),并在文章结尾放置数据派醒目二维码。有原创标识文章,请发送【文章名称-待授权公众号名称及ID】至联系邮箱,申请白名单授权并按要求编辑。

发布后请将链接反馈至联系邮箱(见下方)。未经许可的转载以及改编者,我们将依法追究其法律责任。

点击“阅读原文”拥抱组织