跳至主要内容

时间序列预测教程

一份详尽的时间序列预测指南。学习使用 Python 及配套框架,了解所涉及的统计建模方法。
· 15分钟 阅读

使用 AI 探索

时间序列数据是指在不同时间点上对同一对象进行采集的数据,例如一个国家按年份统计的 GDP、某家公司在一段时间内的股票价格,或每秒记录的心跳。凡是可以在不同时间间隔持续采集的数据,都属于时间序列数据。

下图展示了时间序列数据的一个示例:美国向 CDC 报告的 COVID-19 病例数。x 轴表示时间的推移,y 轴表示以千为单位的病例数。


time series data showing the number of COVID-19 cases in the United States as reported to CDC

另一方面,更常见的数据集是在某一时点存储信息,例如关于客户、产品和公司的信息等,这类数据被称为横截面数据。下面的数据集就是一个示例:在对所有国家均保持固定一致的时间段内,追踪 COVID-19 病例数最多的国家。


tracking countries with the most COVID-19 cases

区分横截面数据与时间序列数据并不困难,因为两者的分析目标截然不同。在我们的例子中,我们首先关注在一段时间内追踪 COVID-19 病例数,然后再在一个固定的时间段内按国家分析病例数。

典型的真实世界数据集很可能是上述两种格式的混合体。以沃尔玛这样的零售商为例,它每天销售数千种商品。如果您在某一天按商品分析销量,这是横截面分析。例如,您可能想知道平安夜当天的销量冠军是哪个商品。相较之下,如果您想在一段时间内(比如过去 5 年)分析某个特定商品的销售,这就是时间序列分析。

在分析时间序列与横截面数据时,目标是不同的,而真实世界的数据集往往同时包含时间序列与横截面两种维度。

什么是时间序列预测?

时间序列预测,顾名思义,就是预测未知的数值。时间序列预测包括收集历史数据,对其进行预处理以便算法使用,然后基于从历史数据中学习到的模式来预测未来的数值。

企业可能对预测未来数值(例如 GDP、月度销售、库存、失业率和全球气温等)有诸多动机:

  • 零售商可能希望在 SKU(库存量单位)层面预测未来销售,以便进行计划与预算。
  • 小商户可能希望按门店预测销售,从而合理安排资源(繁忙时段多排人手,反之亦然)。
  • 像 Google 这样的软件巨头可能希望了解一天中或一周中最繁忙的时段,以便据此安排服务器资源。
  • 卫生部门可能希望预测 COVID 疫苗累计接种量,以进一步推断群体免疫预计何时生效。

时间序列预测的类型

时间序列预测大致分为三类。应使用哪一类取决于您所处理的数据类型以及具体用例:

单变量预测

单变量时间序列,顾名思义,是只有一个随时间变化的变量。例如,若您跟踪某个地区的每小时气温,并希望利用历史气温预测未来气温,这就是单变量时间序列预测。您的数据可能如下所示:

Univariate Forecast

多变量预测

相反,多变量时间序列包含多个随时间变化的变量。每个变量不仅依赖自身的历史值,还会受其他变量的影响。这种依赖关系可用于预测未来值。

仍以上述示例为例,假设我们的数据集在同一时间段内还包含其他与天气相关的属性,如降水百分比、露点、风速等,同时记录了气温。在这种情况下,为了更优地预测气温,需要考虑多个变量。这类序列属于多变量时间序列。您的数据集现在会变成这样:

Multivariate Forecast

您依然是在预测未来的气温,但现在可以将其他可用信息纳入预测中,因为我们假设气温也依赖这些因素。

multivariate time series forecasting 图片来源:Van Nguyen

在进行多变量时间序列预测时,输入变量可分为两类:

  • 外生变量(Exogenous):不受其他输入变量影响、但输出变量依赖其上的输入变量。
  • 内生变量(Endogenous):受其他输入变量影响、且输出变量依赖其上的输入变量。

本教程将讨论若干经典模型,但并非所有模型都支持多变量时间序列预测。在这种情况下,机器学习模型可以发挥作用,因为您可以将任何时间序列预测问题建模为回归问题。我们将在后文看到一个示例。

时间序列预测方法

时间序列预测大体可分为以下几类:

  • 经典/统计模型 —— 移动平均、指数平滑、ARIMA、SARIMA、TBATS
  • 机器学习 —— 线性回归、XGBoost、随机森林,或结合降维/归约方法的任意 ML 模型
  • 深度学习 —— RNN、LSTM

本教程聚焦前两类:经典/统计模型与机器学习。深度学习方法不在本教程范围内。

统计模型

在使用统计模型进行时间序列预测时,有不少流行且公认的算法。它们的数学形式各不相同,并伴随一组必须满足的假设条件。本教程不深入数学原理,而是提供一些直观理解,期望对您有所帮助。

ARIMA

ARIMA 是时间序列预测中最常用的经典方法之一,其全称为自回归积分滑动平均(autoregressive integrated moving average)。它是一类基于自身历史值(即自身滞后项)以及滞后预测误差来对给定时间序列进行预测的模型。ARIMA 包含三个组成部分:

  • 自回归(AR):指变量对其自身滞后(过去)值进行回归的模型。
  • 积分(I):指对原始观测进行差分,使时间序列平稳(即用当前值与前一时刻值之差替代原始数据)。
  • 移动平均(MA):将观测与对滞后观测应用移动平均模型所得的残差之间的依赖关系纳入模型。

ARIMA 中的 “AR” 表明关注的变量对其自身的滞后(过去观测)值进行回归;“MA” 表明回归误差实际上是由同时期及过去不同时间点的误差项线性组合而成;“I”(积分)表明数据已用与前一时刻值的差分所替代(差分可能执行不止一次)。这些特征的目的都是为了让模型尽可能贴合数据。

SARIMA

SARIMA 是 ARIMA 的扩展,支持对序列中的季节性成分进行直接建模。ARIMA 的问题在于不支持季节性数据,即具有重复周期的时间序列。ARIMA 期望数据要么无季节性,要么季节性已被移除(例如通过季节性差分等方法进行季调)。SARIMA 为序列的季节性部分新增了三个超参数,分别用于指定季节性的自回归(AR)、差分(I)和移动平均(MA)。

Forecast using SARIMA model 使用 SARIMA 模型的预测

指数平滑

指数平滑是一种针对单变量数据的时间序列预测方法。它可以扩展以支持具有趋势或季节性成分的数据,可作为流行的 ARIMA 系列模型的替代方案。

对时间序列进行指数平滑,会按时间远近对观测赋予指数递减的权重。数据越久远,权重越小;数据越新,权重越大。

  • 简单(单一)指数平滑使用带有指数递减权重的加权移动平均。
  • Holt 指数平滑通常更适合处理呈现趋势的数据。
  • 三次指数平滑(也称乘法 Holt-Winters)更适用于抛物线趋势,或同时具有趋势与季节性的数据。

comparison results between a single exponential smoothing (ES), double ES, and two-stage EWMA 单次 ES、双次 ES 与两阶段 EWMA 的比较结果

TBATS

TBATS 模型适用于具有多重季节性的时间序列。例如,零售销售数据可能同时具有日度、周度以及年度模式。

在 TBATS 中,会对原始时间序列进行 Box-Cox 变换,然后将其建模为指数平滑趋势、季节性成分与 ARMA 成分的线性组合。

Forecast using TBATS 使用 TBATS 的预测

机器学习

如果您不想使用统计模型,或其表现不佳,可以尝试这种方法。机器学习是一种对时间序列数据进行建模以进行预测的替代方式。在这种方法中,我们从日期中提取特征,作为“X 变量”,而时间序列的取值为“y 变量”。来看一个示例:

本教程使用了可从 Kaggle 下载的美国航空旅客数据集。

数据集样例

我们可以从 “Date” 列中提取特征,例如月份、年份、年度周数等。示例如下:

# extract month and year from dates
data['Month'] = [i.month for i in data['Date']]
data['Year'] = [i.year for i in data['Date']]

# create a sequence of numbers
data['Series'] = np.arange(1,len(data)+1)

# drop unnecessary columns and re-arrange
data.drop(['Date', 'MA12'], axis=1, inplace=True)
data = data[['Series', 'Year', 'Month', 'Passengers']]

# check the head of the dataset
data.head()

Sample rows after extracting features 提取特征后的示例行

需要注意的是,时间序列数据的训练/测试集划分有其特殊性。由于不能打乱表格顺序,必须避免随机采样;测试集中的数据点应位于训练集数据点之后(时间永远向前)。

# split data into train-test set
train = data[data['Year'] < 1960]
test = data[data['Year'] >= 1960]

# check shape
train.shape, test.shape
>>> ((132, 4), (12, 4))

完成训练/测试划分后,我们即可在训练集上训练机器学习模型,在测试集上评分,并评估模型性能。此示例中,我将使用 PyCaret——一个开源、低代码的 Python 机器学习库,可自动化机器学习流程。要使用 PyCaret,需先通过 pip 安装。

# install pycaret
pip install pycaret

如需安装帮助,请参阅官方文档。

假设您已成功安装 PyCaret:

# import the regression module
from pycaret.regression import *

# initialize setup
s = setup(data = train, test_data = test, target = 'Passengers', fold_strategy = 'timeseries', numeric_features = ['Year', 'Series'], fold = 3, transform_target = True, session_id = 123)

现在,训练机器学习模型只需要一行代码:

best = compare_models(sort = 'MAE')

Output from compare_models compare_models 的输出

基于 3 折交叉验证且以平均绝对误差(MAE)为准则,表现最佳的模型是 Leased Angle Regression。我们现在可以使用该模型来预测未来。为此,我们需要构造未来的 “X 变量”。这可以通过创建未来日期并从中提取特征来实现。

既然我们的模型训练到了 1960 年,不妨预测未来五年,直到 1965 年。要使用最终模型生成未来预测,首先需要创建一个包含未来日期的 Month、Year、Series 列的数据集。下面的代码将创建这个未来的 “X” 数据集。

future_dates = pd.date_range(start = '1961-01-01', end = '1965-01-01', freq = 'MS')
future_df = pd.DataFrame()
future_df['Month'] = [i.month for i in future_dates]
future_df['Year'] = [i.year for i in future_dates]    
future_df['Series'] = np.arange(145,(145+len(future_dates)))
future_df.head()

Sample rows from future_df future_df 的示例行

现在我们可以用 future_df 进行预测:

predictions_future = predict_model(best, data=future_df)
predictions_future.head()

Output from predictions_future.head() predictions_future.head() 的输出

接下来我们可以绘图:

concat_df = pd.concat([data,predictions_future], axis=0)
concat_df_i = pd.date_range(start='1949-01-01', end = '1965-01-01', freq = 'MS')
concat_df.set_index(concat_df_i, inplace=True)
fig = px.line(concat_df, x=concat_df.index, y=["Passengers", "Label"], template = 'plotly_dark')
fig.show()

Actual (1949–1960) and Predicted (1961–1964) US airline passengers 实际值(1949–1960)与预测值(1961–1964):美国航空旅客

这里有几点需要注意。处理单变量时间序列时,您总可以将其转换为回归问题,像本例一样求解。不过,交叉验证需格外谨慎。时间序列模型不能使用随机交叉验证,必须采用适用于时间序列的方法。在本例中,PyCaret 使用的是 scikit-learn 库中的 TimeSeriesSplit。

用于预测的 Python 框架

Facebook Prophet

Prophet 是由 Facebook 核心数据科学团队发布的开源软件,可在 CRAN 与 PyPI 上获取。

Prophet 基于加性模型对时间序列数据进行预测,其中非线性趋势结合了年度、每周与每日季节性,并包含节假日效应。它最适用于具有强季节效应且拥有若干季历史数据的时间序列。Prophet 对缺失数据与趋势变动具有鲁棒性,通常也能较好地处理异常值。

要了解更多,请参阅此链接。

Forecast using FB Prophet 使用 FB Prophet 的预测

sktime

sktime 是一个开源的、用于时间序列机器学习的统一框架。它提供了易用、灵活且模块化的平台,覆盖广泛的时间序列机器学习任务。它提供与 scikit-learn 兼容的接口与模型组合工具,旨在提升整个生态的可用性与互操作性。

sktime 如需了解更多 sktime,请参阅此链接。

pmdarima

Pmdarima 是一个统计库,旨在补足 Python 在时间序列分析能力方面的空白。包括:

  • 与 R 的 auto.arima 等效的功能
  • 一系列关于平稳性与季节性的统计检验
  • 季节性时间序列分解
  • 交叉验证工具
  • 丰富的内置时间序列数据集,便于原型开发与示例演示

如需了解更多 pmdarima,请查看此页面。

Kats

Kats 是 Facebook 的另一个优秀开源项目,由其基础设施数据科学团队发布,可在 PyPI 上获取。

Kats 是一个用于分析时间序列数据的工具包;它轻量、易用且通用,可用于执行时间序列分析。Kats 旨在提供一站式时间序列分析能力,包括检测、预测、特征提取/嵌入以及多变量分析等。

Forecast using KATS 使用 KATS 的预测

如需了解更多 KATS,请参阅此链接。

Orbit

Orbit 是 Uber 的优秀开源项目。它是一个用于贝叶斯时间序列预测的 Python 库。在底层利用概率编程语言的同时,为时间序列任务提供熟悉直观的 initialize-fit-predict 接口。

Forecast using Orbit 使用 Orbit 的预测

如需了解更多 Orbit,请查看此链接。

PyCaret

PyCaret 是一个开源、低代码的 Python 机器学习库,可自动化机器学习工作流。使用 PyCaret,您能减少编码时间、投入更多精力进行分析。您可以训练模型、进行分析、更快地迭代,并可即时将其部署为 REST API,甚至直接在 Notebook 中构建简单的前端 ML 应用。

PyCaret Time Series Module Architecture. Developed by Lead Developer Nikhil Gupta. PyCaret 时间序列模块架构。由首席开发者 Nikhil Gupta 设计。

接下来,我们将通过 PyCaret 库演示一个端到端的时间序列预测任务示例。

端到端示例

PyCaret 的时间序列模块目前为测试版,您可以通过 pip 下载。

pip install pycaret-ts-alpha

本教程在 Colab 的链接。

数据集

这里我们使用 airline 数据集,该数据集也可在 PyCaret 的数据仓库中获取。

from pycaret.datasets import get_data
data = get_data('airlineer')

Dataset

data.plot()

data.plot()

实验设置

在 PyCaret 中,通过 setup 函数启动实验。该函数会处理所有预处理步骤、训练/测试划分、交叉验证策略以及其他若干任务。

from pycaret.time_series import *
s = setup(data, session_id = 123)

Output from the setup function setup 函数输出

探索性数据分析(EDA)

对单变量时间序列进行 EDA 时,有一些事情您可以也应当去做。

若要使用经典与统计模型,您可以运行一些统计检验。PyCaret 中有一个函数可为您完成这些工作。

check_stats()

Output from check_stats function check_stats 函数输出

如果您处理的是单变量时间序列,也可以将其分解为趋势、季节性与误差成分,以便进一步分析。

plot_model(plot = 'decomp_classical")

Output from plot_model function ## Model Training and Selection ## 来自 plot_model 函数的输出<c/entre> ## 模型训练与选择 ## 若要训练多个模型并从中选出一个,我们只需运行一行代码。 best = compare_models() Output from compare_models function compare_models 函数输出

PyCaret 使用适用于时间序列的交叉验证训练了 25 个以上的模型,并按性能由高到低给出列表。基于本数据集的实验结果,表现最佳的模型为指数平滑,其平均绝对误差为 7.781。

模型分析

在将该模型部署用于未来预测之前,我们可以查看几个分析图来评估模型质量。

plot_model(best, plot = 'forecast', data_kwargs = {'fh': 24})

Time Series Forecast Plot 时间序列预测图

plot_model(best, plot = 'residuals')

Time-Series residuals plot 时间序列残差图

plot_model(best, plot = 'insample')

Time-Series insample plot 时间序列样本内拟合图

模型部署

此时,我们已准备好将模型定型并保存以供后续使用。

# finalize model
final_best = finalize_model(best)

# save model
final_best = finalize_model(best)

Model Deployment

要重新加载该文件并在未来数据上生成预测:

# load model
loaded_model = load_model('my_best_model')

# generate predictions for the next 48 months
predict_model(loaded_model, fh = 48)

Output truncated 输出已截断

结语

时间序列预测是一项非常值得学习的技能。许多现实问题都具有时间序列特性。预测在各行业有着广泛应用,涵盖大量实践场景,例如:天气预测、经济预测、医疗预测、金融预测、零售预测、商业预测、环境研究、社会研究等。

基本上,任何具有一致时间间隔的历史数据都可以使用时间序列分析方法进行分析,从而形成一个学习历史、预测未来的任务。总而言之,时间序列预测大致有三大类方法:

  • 统计模型——指数平滑、ARIMA、SARIMA、TBATS 等
  • 机器学习——线性回归、XGBoost、随机森林等
  • 深度学习——RNN、LSTM

如果您希望进一步提升时间序列预测的知识与技能,不妨看看 Datacamp 推出的 “Time Series with Python” 学习路径。在该路径中,您将学习如何使用 pandas 处理时间序列数据,结合 NumPy 和 statsmodels 等统计库进行分析,并用 Matplotlib、SciPy 与 seaborn 提升可视化能力。完成该路径后,您将掌握使用 ARIMA 类模型进行未来预测,以及利用机器学习模型生成预测与洞见的方法。

主题