在上一章中,我们学习了一些可以用来理解时间序列数据、进行探索性数据分析(EDA)等的技术。但现在,让我们进入问题的核心——时间序列预测。理解数据集、查看模式、季节性等内容的目的是让预测工作变得更容易。而在任何机器学习任务中,开始之前我们需要做的第一件事就是建立一个基线。
基线是一个简单的模型,它能够提供合理的结果,而不需要花费太多时间去得出这些结果。许多人认为基线是由常识推导出来的,比如平均值或某些经验法则。但作为最佳实践,基线可以是我们想要的任何复杂度,只要它能够快速且容易地实现。我们要取得的任何进一步进展,都将基于该基线的表现。
在本章中,我们将讨论几种可以作为强基线的经典技术。有些人可能认为我们将在本章讨论的预测技术不应被视为基线,但我们之所以保留这些技术,是因为它们经受住了时间的考验——而且有充分的理由。它们也非常成熟,并且由于有出色的开源库实现这些技术,可以非常轻松地应用它们。对于很多类型的问题或数据集,很难超越我们将在本章讨论的基线技术,在这些情况下,坚守其中一种基线技术并没有什么可耻的。
在本章中,我们将涵盖以下主题:
- 设置测试工具
- 生成强基线预测
- 评估时间序列的可预测性
技术要求
你需要按照本书前言中的说明设置 Anaconda 环境,以便获得一个包含所有必需库和数据集的工作环境。任何额外的库将在运行笔记本时安装。
在使用本章代码之前,你需要运行以下笔记本:
- 第2章的
02-Preprocessing_London_Smart_Meter_Dataset.ipynb预处理笔记本
本章的代码可以在以下地址找到:github.com/PacktPublis…
设置测试工具
在我们开始进行预测和建立基线之前,我们需要设置一个测试工具。在软件测试中,测试工具是为了在各种情况中测试程序而配置的代码和输入集合。在机器学习中,测试工具是用于评估算法的一组代码和数据。设置测试工具非常重要,因为它可以帮助我们以标准化且快速的方式评估所有未来的算法。
首先,我们需要创建 holdout(测试) 和 验证数据集。
创建 holdout(测试)和验证数据集
作为标准实践,在机器学习中,我们会将数据集分为两个部分,命名为验证数据和测试数据,并且完全不用于训练模型。验证数据用于建模过程中的模型质量评估。在选择不同的模型类别、调节超参数、执行特征选择等过程中,我们需要使用验证数据。测试数据则像是你所选择模型的最终测试,它告诉你模型在未见过数据中的表现。如果验证数据像是期中考试,那么测试数据就是期末考试。
在常规回归或分类问题中,我们通常会随机抽取一些记录并将其分开。但在处理时间序列时,我们需要尊重数据的时间性。因此,最佳实践是将数据集中最新的部分设置为测试数据。另一个经验法则是将验证数据和测试数据设置为相等的大小,以便基于验证数据做出的关键建模决策能尽可能接近测试数据。本书第二章中介绍的伦敦智能能源数据集包含了2011年11月到2014年2月之间伦敦家庭的能源消耗数据。因此,我们将2014年1月作为验证数据,2014年2月作为测试数据。
现在,我们可以打开 01-Setting_up_Experiment_Harness.ipynb 文件并运行它。在这个笔记本中,我们必须在填充缺失值后,分别为训练集和测试集创建数据拆分,并保存它们。运行完笔记本后,您将在预处理文件夹中生成以下文件,其中2014年的数据会被单独保存:
selected_blocks_train.parquetselected_blocks_val.parquetselected_blocks_test.parquetselected_blocks_train_missing_imputed.parquetselected_blocks_val_missing_imputed.parquetselected_blocks_test_missing_imputed.parquet
现在我们有了一个固定的数据集,可以用来公平地评估多个算法。接下来,我们需要一个方法来评估不同的预测结果。
选择评估指标
在机器学习中,我们有一些可以用来衡量连续输出的评估指标,主要是 平均绝对误差(MAE) 和 均方误差(MSE) 。但在时间序列预测领域,有很多评估指标,并没有真正的共识来决定使用哪些指标。导致这种评估指标数量繁多的原因之一是,没有一个指标能够衡量预测的所有特征。因此,我们有一整章专门讨论这个话题(第19章《评估预测误差——预测指标调查》)。现在,我们只会回顾一些指标,所有这些指标都将用于衡量预测效果。我们将仅仅按照其字面意义考虑这些指标:
- 平均绝对误差(MAE) :MAE 是一个非常简单的指标,它是时间步 ttt 处的预测值 f(t)f(t)f(t) 与实际值 y(t)y(t)y(t) 之间的绝对误差的平均值。公式如下:
其中,N 是时间序列的数量,L 是时间序列的长度(在本例中是测试周期的长度),f 和 y 分别是预测值和实际值。
- 均方误差(MSE) :MSE 是预测值 f(t)f(t)f(t) 和实际值 y(t)y(t)y(t) 之间误差的平方的平均值:
- 平均绝对缩放误差(MASE) :MASE 比 MSE 和 MAE 稍微复杂一些,但它为我们提供了一种稍好的度量,克服了之前两种方法的规模依赖性。如果我们有多个具有不同平均值的时间序列,MAE 和 MSE 会对高值时间序列显示出更高的误差,而对低值时间序列则相反。MASE 通过根据朴素预测方法(最基本的预测方法之一,稍后会在本章中回顾)中的样本内 MAE 对误差进行缩放来解决这个问题。直观地,MASE 给出了我们的预测相对于朴素预测的表现:
-
预测偏差(FB) :这个指标与我们之前看到的其他指标有所不同。虽然其他指标有助于评估预测的准确性,而不考虑误差的方向,预测偏差则帮助我们理解模型的整体偏差。预测偏差是一个度量,它帮助我们理解预测是否持续高估或低估。
我们通过计算预测的总和与实际值的总和之间的差异,并将其表示为实际值总和的百分比来计算预测偏差:
现在,我们的测试工具已准备就绪。我们也知道如何使用一组预定的指标来评估和比较通过不同模型生成的预测结果,并在一个固定的测试数据集上进行评估。接下来,开始预测吧。
生成强基线预测
时间序列预测自1920年代初期就已经存在,经过多年,许多杰出的人才提出了不同的模型,其中一些是统计模型,另一些则是基于启发式的方法。我将这些模型统称为经典统计模型或计量经济学模型,尽管它们严格来说并不完全属于统计学/计量经济学范畴。
在本节中,我们将回顾几种可以作为强基线的经典模型,当我们希望尝试现代预测技术时,这些模型将非常有用。作为练习,我们将使用一个非常出色的开源库——NIXTLA(github.com/Nixtla)来生成预测。本节代码可以在 02-Baseline_Forecasts_using_NIXTLA.ipynb 笔记本中找到,您可以跟着一起操作。
在开始讨论预测技术之前,我们先快速了解如何使用 NIXTLA 库来生成预测。我们将从数据集中选择一个消费者,并依次尝试所有基线技术,在验证数据集上进行测试。
首先,我们需要使用每个客户的唯一 ID(LCLid 列,来自数据的扩展形式),并将时间戳设置为 DataFrame 的索引:
ts_train = train_df.loc[train_df.LCLid=="MAC000193",['LCLid',"timestamp","energy_consumption"]]
ts_val = val_df.loc[val_df.LCLid=="MAC000193", ['LCLid',"timestamp","energy_consumption"]]
ts_test = test_df.loc[test_df.LCLid=="MAC000193", ['LCLid',"timestamp","energy_consumption"]]
NIXTLA 具有与 pandas 或 Polars DataFrame 直接合作的灵活性。默认情况下,NIXTLA 查找三个列:
- id_col:默认期望一个名为
unique_id的列。此列唯一标识时间序列。如果您只有一个时间序列,可以添加一个虚拟列,并使用相同的唯一标识符。 - time_col:默认期望一个名为
ds的列,这是您的时间戳列。 - target_col:默认期望一个名为
y的列,这是您希望 NIXTLA 预测的目标。
这非常方便,因为从数据到建模的过程不需要进一步的操作。NIXTLA 遵循 scikit-learn 的样式,具有 .fit() 和 .predict() 方法,并且还采用了 .forecast() 方法,这是一种节省内存的方法,不会存储部分模型输出,而 scikit-learn 接口会存储拟合的模型:
sf = StatsForecast(
models=[model],
freq=freq,
n_jobs=-1,
fallback_model=Naive()
)
sf.fit(df=_ts_train, id_col='LCLid', time_col='timestamp', target_col='energy_consumption')
baseline_test_pred_df = sf.predict(len(ts_test))
NIXTLA 也有 .forecast() 方法,这是一个节省内存的方法,不会存储部分模型输出,而 scikit-learn 接口会存储拟合的模型:
# Efficiently fit and predict without storing memory
y_pred = sf.forecast(
h=len(ts_test),
df=ts_train,
id_col='LCLid',
time_col='timestamp',
target_col='energy_consumption',
)
当我们调用 .predict() 或 .forecast() 时,我们必须告诉模型预测多长时间。这被称为预测的时间范围。在我们的案例中,我们需要预测我们的测试周期,简单地通过获取 ts_test 数组的长度就可以实现。
我们还可以使用 NIXTLA 的类轻松计算我们之前讨论的测试工具中的指标。为了更好的灵活性,我们可以循环遍历一组指标,为每个预测结果获取多个度量:
# 计算指标
metrics = [mase, mae, mse, rmse, smape, forecast_bias]
for metric in metrics:
metric_name = metric.__name__
if metric_name == 'mase':
evaluation[metric_name] = metric(results[target_col].values,
results[model_name].values,
ts_train[target_col].values,
seasonality=48)
else:
evaluation[metric_name] = metric(results[target_col].values,
results[model_name].values)
注意,对于 MASE,训练集也被包含在内。
为了方便实验,我们将所有这些功能封装到一个名为 evaluate_performance 的函数中,您可以在笔记本中找到它。这个函数将返回预测值和计算出的指标,保存在一个 DataFrame 中。
现在,让我们开始看一下几个非常简单的预测方法。
朴素预测
朴素预测是最简单的预测方法。预测值只是时间序列中的最后一个/最近的观测值。如果时间序列中的最新观测值是10,那么对所有未来的时间步的预测值都是10。我们可以使用 NIXTLA 中的 Naive 类来实现这一点:
from statsforecast.models import Naive
models = Naive()
初始化模型后,我们可以在笔记本中调用我们有用的 evaluate_performance 函数来运行并记录预测和指标。
让我们可视化我们刚刚生成的预测:
在这里,我们可以看到预测是一条直线,完全忽略了序列中的任何模式。这是目前为止最简单的预测方式,因此称之为“朴素”预测。现在,让我们看一下另一种简单的方法。
移动平均预测
虽然朴素预测记住的是最近的过去,但它也会记住任何时间步的噪声。移动平均预测是另一种简单的方法,试图克服朴素方法纯粹的记忆方式。它不是取最新的观测值,而是取最新 n 个时间步的平均值作为预测。移动平均不是 NIXTLA 中的内置模型,但我们在本书的 GitHub 仓库中的 Chapter04 文件夹中实现了一个与 NIXTLA 兼容的模型:
from src.forecasting.baselines import NaiveMovingAverage
# 对48个时间步进行移动平均,即一天
naive_model = NaiveMovingAverage(window=48)
让我们看看我们生成的预测:
这个预测也几乎是一条直线。现在,让我们看一下另一种简单的方法,但它考虑了季节性。
季节性朴素预测
季节性朴素预测是对简单朴素方法的一个变种。在朴素方法中,我们取的是最后一个观测值(Yt-1),而在季节性朴素预测中,我们取的是 Yt-k 的观测值。因此,每次预测时,我们回顾 k 步。这使得算法能够模仿最后一个季节性周期。例如,如果我们设置 k=48*7,我们将能够模仿最新的季节性每周周期。
这种方法在 NIXTLA 中已经实现,我们可以像下面这样使用它:
from statsforecast.models import SeasonalNaive
seasonal_naive = SeasonalNaive(season_length=48*7)
让我们看看这个预测的效果:
在这里,我们可以看到,预测试图模仿季节性模式。然而,它并不是非常准确,因为它盲目地跟随了上一个季节性周期。
现在,我们已经看过了一些简单的方法,让我们来看几种统计模型。
指数平滑法
指数平滑法是生成预测的最流行的方法之一。它自20世纪50年代末以来就已经存在,并且证明了它的有效性,经受住了时间的考验。ETS(指数平滑法)有几种不同的变体——单一指数平滑法、双重指数平滑法、霍尔特-温特季节平滑法等。但它们都有一个关键思想,这一思想以不同的方式得到了应用。在朴素方法中,我们只使用了最新的观测值,这就像是在说只有历史中最新的数据点重要,之前的数据点都不重要。另一方面,移动平均方法认为最近的 n 个观测值同样重要,并取它们的平均值。
指数平滑法结合了这两种直觉,认为所有的历史数据都很重要,但最近的历史数据更为重要。因此,预测是通过加权平均来生成的,其中权重随着历史的推移呈指数下降:
在这里,0<=<=1 是平滑参数,它让我们决定权重衰减的速度,是时间步 t 的实际值,是时间步 t 的预测值。
简单指数平滑(SES)就是将这种平滑过程直接应用于历史数据。这更适用于没有趋势或季节性的时间序列,并且预测结果将是一条平坦的线。预测是使用以下公式生成的:
双重指数平滑(DES)将平滑的思想扩展到趋势建模。它有两个平滑方程——一个用于水平(level),另一个用于趋势(trend)。一旦你有了水平和趋势的估计值,就可以将它们结合起来。这种预测不一定是平坦的,因为估计的趋势被用来将其外推到未来。预测是根据以下公式生成的:
首先,我们使用水平方程(Level Equation)通过可用观测值估计水平(ltl_tlt)。然后,我们使用趋势方程(Trend Equation)估计趋势(btb_tbt)。最后,为了得到预测值,我们将 ltl_tlt 和 btb_tbt 结合使用预测方程。
研究人员发现实证证据表明,这种恒定的外推方法可能会导致长期预测的过度预测。这是因为在现实世界中,时间序列数据不可能永远以恒定的速度增长。基于这个动机,已经引入了一种方法,通过一个系数来抑制趋势, 使得当 α=1\alpha = 1α=1 时,趋势不被抑制,这与双重指数平滑(DES)相同。
三重指数平滑或霍尔特-温特(Holt-Winters,HW)方法则通过引入另一个平滑项来建模季节性,迈出了这一步。它有三个平滑参数(),并使用季节性周期(m)作为输入参数。你还可以选择加法季节性或乘法季节性。加法模型的预测方程如下:
这些公式与双重指数平滑中的公式类似。不同之处在于,我们不是估计水平和趋势,而是分别估计水平、趋势和季节性。
ETS(指数平滑)方法家族不仅限于我们刚刚讨论的三种方法。我们可以根据这些模型的趋势和季节性成分来理解不同的模型。趋势可以是没有趋势、加法趋势或加法衰减趋势。季节性可以是没有季节性、加法季节性或乘法季节性。这些参数的每种组合都是该家族中的一种不同技术,如下表所示:
| 趋势成分 | 季节性成分 |
|---|---|
| N (None) | A (Additive) |
| N (None) | M (Multiplicative) |
| A (Additive) | Simple Exponential Smoothing |
| A (Additive) | Double Exponential Smoothing |
| A (Additive) | Additive Holt-Winters |
| M (Multiplicative) | Multiplicative Holt-Winters |
| Ad (Additive damped) | Damped Double Exponential Smoothing |
| - | Damped Holt-Winters |
表 4.1:指数平滑家族
NIXTLA 拥有一个完整的 ETS 方法家族。
让我们看看如何在 NIXTLA 中初始化 ETS 模型:
from statsforecast.models import (SimpleExponentialSmoothing, Holt, HoltWinters, AutoETS)
exp_smooth = HoltWinters(error_type='A', season_length=48)
在这里,error_type='A' 指的是加法误差。用户可以选择加法误差或乘法误差,可以使用 error_type='M' 来调用乘法误差。NIXTLA 模型还可以使用 AutoETS()。该模型会自动选择最佳的指数平滑模型:简单指数平滑、双重指数平滑(霍尔特法)或三重指数平滑(霍尔特-温特法)。它还会为每个时间序列选择最合适的参数和误差类型。有关如何使用 AutoETS() 的示例,请参考 GitHub 上的笔记本。
让我们看看使用 ETS 进行预测的效果,如图 4.4 所示:
从预测结果来看,它已经捕捉到季节性,但未能准确捕捉到峰值。然而,我们可以看到 MAE 已经有所改善。
现在,让我们来看一下另一个非常流行的预测方法。
自回归积分滑动平均(ARIMA)
ARIMA 模型是与 ETS 一样,经受住了时间考验的另一类方法,并且是最流行的经典预测方法之一。ETS 方法族围绕趋势和季节性进行建模,而 ARIMA 则依赖于自相关(即 yty_tyt 与 yt−1y_{t-1}yt−1、yt−2y_{t-2}yt−2 等之间的相关性)。
该家族中最简单的模型是 AR(p),它使用线性回归模型与过去的 ppp 个时间步的数据,或者换句话说,使用 ppp 个滞后。数学表达式如下:
其中,ccc 是截距,ϵt\epsilon_tϵt 是时间步 ttt 的噪声或误差。
接下来是 MA(q) 模型,它不是使用过去的观测值,而是使用过去 qqq 个预测误差(假设为纯白噪声)来生成预测:
其中,ϵt\epsilon_tϵt 是白噪声,ccc 是截距。
这种方法通常不是单独使用,而是与 AR(p) 模型结合使用,这就形成了下一个模型 ARMA(p, q) 模型。ARMA(自回归滑动平均)模型被定义为:
在所有 ARIMA 模型中,有一个基本假设——时间序列是平稳的(我们在第1章《引入时间序列》中讨论过平稳性,并将在第6章《时间序列预测特征工程》中详细阐述)。有许多方法可以使时间序列平稳,其中一种方法是取相邻值的差异,这称为差分。 有时我们只需要进行一次差分,而有时需要进行连续的差分,直到时间序列变为平稳。我们进行差分操作的次数称为差分阶数。ARIMA 中的 I 代表积分,表示我们在时间序列变为平稳之前需要进行的差分次数,记作 ddd。
因此,完整的 ARIMA(p, d, q) 模型表示我们进行 ddd 次差分,然后考虑过去的 ppp 个自回归项,再加上过去的 qqq 个移动平均项,来生成预测。
到目前为止,我们讨论的 ARIMA 模型仅适用于非季节性时间序列。然而,使用我们之前讨论的相同概念,在季节性周期上应用,我们就得到了季节性 ARIMA。我们对 ppp、ddd 和 qqq 做了适当的调整,使其适用于季节性周期 mmm。为了区分它们与普通的 ppp、ddd 和 qqq,我们将季节性参数称为 PPP、DDD 和 QQQ。例如,如果 ppp 表示取最近的 ppp 个滞后,PPP 表示取最近的 PPP 个季节性滞后。如果 p1p_1p1 是 yt−1y_{t-1}yt−1,那么 P1P_1P1 就是 yt−my_{t-m}yt−m。同样,DDD 表示季节性差分的阶数。
选择正确的 ppp、ddd 和 qqq,以及 PPP、DDD 和 QQQ 的值并不是直观的,我们需要依靠统计检验来找到它们。然而,当你预测多个时间序列时,这就变得有些不切实际了。一种自动化的方法是通过不同的参数迭代,找到最佳的 ppp、ddd 和 qqq,以及 PPP、DDD 和 QQQ 值,这种方法叫做 AutoARIMA。在 Python 中,NIXTLA 实现了这种方法,AutoARIMA()。NIXTLA 还实现了普通的 ARIMA 模型,这种模型更快,但需要手动输入 ppp、ddd 和 qqq。
实际考虑:
尽管 ARIMA 和 AutoARIMA 在许多情况下可以提供良好的预测模型,但当季节周期很长且时间序列很长时,它们的运行速度可能会非常慢。在我们的案例中,历史数据有近 27000 个观测值,ARIMA 会变得非常慢并且占用大量内存。即使对数据进行子集化处理,单次运行 AutoARIMA 也需要大约 60 分钟。去掉季节性参数会显著缩短运行时间,但对于像能源消耗这样的季节性时间序列来说,这样做是没有意义的。AutoARIMA 包含许多此类拟合,以识别最佳的参数,因此对于长时间序列数据集来说,它变得不切实际。几乎所有 Python 生态系统中的实现都有这个缺点。NIXTLA 声称其 AutoARIMA 是最快且最准确的版本,甚至比原始的 R 方法还要快。
让我们看看如何使用 NIXTLA 应用 ARIMA 和 AutoARIMA:
from statsforecast.models import (ARIMA, AutoARIMA)
# ARIMA 模型,通过指定参数
arima_model = ARIMA(order=(2,1,1), seasonal_order=(1,1,1), season_length=48)
# AutoARIMA 模型,通过指定参数的最大限制,并让算法找到最佳参数
auto_arima_model = AutoARIMA(max_p=2, max_d=1, max_q=2, max_P=2, max_D=1, max_Q=2, stepwise=True, season_length=48)
要查看 AutoARIMA 的所有参数列表,请访问 NIXTLA 文档:nixtlaverse.nixtla.io/statsforeca…。
让我们看看我们实验中的家庭的 ETS 和 ARIMA 预测效果:
在 NIXTLA 中,ETS 和 ARIMA 都很好地捕捉了季节性和峰值。生成的 MAE 分数也非常相似,分别为 0.191 和 0.203。现在,让我们看一下另一种方法——Theta 预测。
Theta 预测
Theta 预测是在 2002 年举行的 M3 预测竞赛中表现最好的方法。该方法依赖于一个参数 θ\thetaθ,根据选择的值,它可以放大或平滑时间序列的局部曲率。使用 θ\thetaθ,我们可以平滑或放大原始时间序列。这些平滑的线称为 Theta 线。V. Assimakopoulos 和 K. Nikolopoulos 提出了这种方法,作为一种用于预测的分解方法。虽然理论上可以使用任意数量的 Theta 线,但最初提出的方法使用了两条 Theta 线,θ1\theta_1θ1 和 θ2\theta_2θ2,并将这两条 Theta 线的预测平均值作为最终预测。
M 系列竞赛是由领先的预测研究人员 Spyros Makridakis 组织的预测竞赛。通常,这些竞赛会策划一个时间序列数据集,设定用于评估预测的指标,并向全球研究人员开放这些竞赛,以获得最佳的预测结果。这些竞赛被认为是世界上最大和最受欢迎的时间序列预测竞赛之一。截至撰写时,已经完成了六个此类竞赛。要了解最新的竞赛,请访问此网站:mofc.unic.ac.cy/the-m6-comp…。
在 2002 年,Rob Hyndman 等人简化了 Theta 方法,并表明我们可以使用带有漂移项的 ETS 来获得与原始 Theta 方法等效的结果,这也是今天大多数 Theta 方法实现所采用的方式。Theta 预测的主要步骤(NIXTLA 中已实现)如下:
- 去季节化:应用经典的乘法分解来去除时间序列中的季节性成分(如果存在)。这将分析集中于基础趋势和周期成分。去季节化使用
statsmodels.tsa.seasonal.seasonal_decompose来完成。这一步生成一个新的去季节化时间序列。 - Theta 系数应用:使用系数 θ1\theta_1θ1 和 θ2\theta_2θ2 将去季节化序列分解为两条“Theta”线。这些系数修改时间序列的第二差分,以衰减(或强调)局部波动。
- Theta 线的外推:将每条 Theta 线视为一个独立的序列,并将它们外推到未来。这是通过线性回归应用于 θ1\theta_1θ1 线(生成直线)和对 θ2\theta_2θ2 线使用简单指数平滑来完成的。
- 重新组合:将两条 Theta 线的预测结果结合起来。原始方法对两条线进行等权重组合,有效地集成了长期趋势和短期波动。
- 重新季节化:如果数据在开始时已经去季节化。
NIXTLA 提供了不同版本的 Theta 方法。有关 NIXTLA 实现的具体信息,请访问:nixtlaverse.nixtla.io/statsforeca…。
让我们看看如何实际使用它:
theta_model = Theta(season_length=48, decomposition_type='additive')
这里的关键参数是 season_length 和 decomposition_type。这些参数用于初始的季节性分解。如果不设置,NIXTLA 实现会自动检测季节性,并使用乘法分解自动去季节化时间序列。如果我们知道季节性参数,建议根据领域知识设置这些参数。decomposition_type 可以是乘法(默认)或加法。
让我们可视化我们刚刚生成的 Theta 预测:
参考检查:
V. Assimakopoulos 和 K. Nikolopoulos 提出的 Theta 方法的研究论文在参考文献部分被引用为参考文献 1,而 Rob Hyndman 随后的简化则被引用为参考文献 2。
季节性模式已被捕捉到,但它并未准确地捕捉到峰值。让我们看看另一个非常强大的方法,TBATS。
TBATS
有时,时间序列具有多个季节性模式或非整数季节周期,通常称为复杂季节性。例如,一个小时级的预测可能有一个日间季节性(一天中的时间),一个周季节性(每周的某一天),以及一个年季节性(每年的某一天)。此外,大多数时间序列模型设计用于较小的整数季节周期,如月度(12)或季度(4)数据,但年度季节性可能会带来问题,因为一年有 364.25 天。TBATS 旨在应对这些对许多预测模型构成挑战的问题。然而,任何自动化方法有时也容易导致不准确的预测。
TBATS 代表:
- 三角函数季节性(Trigonometric seasonality)
- Box-Cox 变换(Box-Cox transformation)
- ARMA 误差(ARMA errors)
- 趋势(Trend)
- 季节性成分(Seasonal components)
该模型首次由 Rob J. Hyndman、Alysha M. De Livera 和 Ralph D. Snyder 于 2011 年提出。TBATS 还有一个变体,称为 BATS,没有三角函数季节性成分。TBATS 来自状态空间模型(state space model)家族。在状态空间预测模型中,观察到的时间序列被假定为潜在状态变量和一个测量方程的组合,该方程将状态变量与观测数据联系起来。状态变量捕捉数据中的基本模式、趋势和关系。
BATS 模型有以下参数,表示 Box-Cox 参数、衰减参数、ARMA 参数(p, q)和季节性周期(m1, m2, …, mt)。由于其灵活性,BATS 模型可以被视为一个包含许多我们之前见过的模型的家族。例如:
- BATS(1, 1, 0, 0, m1) = 霍尔特-温特加法季节性(Holt-Winters Additive Seasonality)
- BATS(1, 1, 0, 0, m2) = 霍尔特-温特加法双重季节性(Holt-Winters Additive Double Seasonality)
BATS 模型支持多重季节性,但仅限于整数季节周期,且在有多个季节性时,它可能会有大量状态,从而增加模型复杂度。这正是 TBATS 旨在解决的问题。
对于参考,TBATS 的参数空间如下所示:
TBATS 的主要优点如下:
- 适用于单一、复杂的和非整数季节性(例如三角函数季节性)
- 处理常见的非线性模式(例如 Box-Cox 变换)
- 处理残差中的自相关(自回归滑动平均误差)
为了更好地理解 TBATS 的内部工作原理,让我们分解每个步骤。
使用 TBATS 时操作的顺序(与缩写中的顺序不同)是:
- Box-Cox 变换
- 指数平滑趋势
- 使用傅里叶级数的季节性分解(即三角函数季节性)
- 自回归滑动平均(ARMA)
- 通过基于似然的方法进行参数估计
Box-Cox 变换
Box-Cox 是幂变换(power transformation)家族中的一种变换。
在时间序列中,使数据平稳化是预测前的重要步骤(如第1章所讨论)。平稳性确保我们的数据在统计上不会随时间变化,因此更准确地模拟概率分布。有多种可能的变换可以应用。有关各种目标变换的更多详细信息,包括 Box-Cox 变换,可以在第7章找到。
作为预览,这里是 Box-Cox 变换的一个示例输出。经过变换后,我们的数据更接近正态分布。Box-Cox 变换仅能用于正数据,但在实践中,这通常是常见的情况。
图4.7 展示了时间序列在进行 Box-Cox 变换前后的样子。
指数平滑趋势
使用局部估计散点图平滑(LOESS),从时间序列中提取平滑的趋势:
LOESS 通过对数据点应用局部加权、低阶多项式回归,创建一条平滑的流线。该技术在捕捉局部趋势变化方面非常有效,而不假设数据的全局形式,这使得它特别适用于具有变化趋势或季节性波动的数据。这与我们在第3章中用于将时间序列分解为趋势的 LOESS 方法相同。
使用傅里叶级数进行季节性分解(三角函数季节性)
然后,剩余的误差项使用傅里叶项(在第3章中讨论过)进行建模,以分解季节性成分。
使用傅里叶建模季节性的主要优势是它能够建模多个季节性以及非整数季节性,例如在日常数据中建模年度季节性,因为一年有 364.25 天。大多数其他分解方法无法处理非整数周期,只能将其四舍五入为 365,这可能会导致无法识别真实的季节性。以下是使用傅里叶进行分解的时间序列示例。该示例中的观测时间序列是小时数据。因此,我们的季节性周期为:
- 日季节性 = 24
- 周季节性 = 24 * 7 = 168
在这里,您可以清晰地看到定义的季节性模式、趋势和剩余的误差项。图4.8展示了趋势和季节性的分解,之后剩余的误差项将使用 ARMA 过程进行建模。
ARMA
ARMA 作为 ARIMA 家族的一个子集,之前已经讨论过:
在 TBATS 模型中,ARMA 用于建模剩余的误差项,以捕捉滞后变量的自相关性。自回归(AR)部分捕捉一个观测值与多个滞后观测值之间的相关性,处理序列的动量或延续性。滑动平均(MA)部分将误差项建模为前期误差的线性组合,捕捉 AR 部分无法单独解释的信息。
参数优化
为了选择最佳的参数空间,TBATS 会拟合多个模型并自动选择最佳的参数。TBATS 内部拟合的一些模型包括:
- 有无 Box-Cox 变换
- 有无趋势
- 有无趋势衰减
- 季节性和非季节性模型
- ARMA (p, q) 参数
最终模型的选择是通过哪个参数组合最小化赤池信息量准则(AIC)来确定的,并且使用 AutoARIMA 来确定 ARMA 参数。
与所有预测方法一样,不同模型之间有其优点和权衡。虽然 TBATS 在许多其他模型的不足之处提供了一些增强功能,但其权衡之处在于需要构建多个模型,这会导致更长的计算时间。如果需要对多个时间序列进行建模,这可能会成为一个问题。此外,TBATS 不支持包括外生变量。
实践者注意事项:
TBATS 无法处理外生回归,因为它与 ETS 模型相关,正如 Hyndman 本人所说,他认为不太可能包含协变量(Hyndman,2014;参考文献7)。如果需要使用外部回归变量,应使用 ARIMAX 或 SARIMAX 等方法。如果时间序列具有复杂的季节性,可以将傅里叶特征作为协变量添加到 ARIMAX 或 SARIMAX 模型中,以帮助捕捉季节性模式。
这一功能已在 NIXTLA 中实现,我们可以使用下面显示的实现方式:
TBATS_model = TBATS(seasonal_periods=48, use_trend=True, use_damped_trend=True)
在 NIXTLA 中,你还可以使用 AutoTBATS 让系统优化如何处理不同的参数。
让我们看看 TBATS 预测的效果:
多重季节性趋势分解(MSTL)
回想一下我们在第3章进行的时间序列分解?如果我们能够使用相同的技术来进行预测呢?这正是 MSTL 所做的。让我们再次看看时间序列的组成部分:
- 趋势
- 周期性
- 季节性
- 不规则性
趋势和周期性成分可以通过 LOESS 回归提取。如果我们对趋势值拟合一个简单的模型,可以用它来进行外推。而季节性成分由于是重复的模式,可以很容易地进行外推。将这些结合在一起,我们得到一个有效的预测模型。
NIXTLA 中的 MSTL 方法应用 LOESS 技术将时间序列分解为不同的季节性成分。经过这种分解后,MSTL 使用专门的非季节性模型来预测趋势,并使用季节性 Naive 模型来预测每个季节性成分。这种方法允许对具有复杂季节性模式的时间序列进行详细分析和预测:
MSTL_model = MSTL(season_length = 48)
让我们看看 MSTL 预测的效果:
让我们也来看看我们为这些预测选择的不同指标在我们实验中的家庭数据上的表现(来自笔记本):
在我们尝试的所有基准算法中,AutoETS 在 MAE 和 MSE 上表现最好。ARIMA 是第二好的模型,紧随其后的是 TBATS。然而,如果你查看时间消耗列,TBATS 脱颖而出,仅需 7.4 秒,而 ARIMA 需要 19 秒。由于它们的表现相似,我们将选择 TBATS 作为优于 ARIMA 的基准模型,并与 AutoETS 一起作为基准,在我们选择的 399 个家庭(包括验证集和测试集)上运行它们(相关代码可在 02-Baseline_Forecasts_using_NIXTLA.ipynb 笔记本中找到)。
评估基准预测
由于我们已经生成了来自 ETS 和 TBATS 的基准预测,我们还应该评估这些预测。这两种方法在所有选定家庭的聚合指标如下:
看起来 AutoETS 在所有三个指标上表现都要好得多。我们还可以在家庭级别计算这些指标。让我们看看在验证数据集中,所有选定家庭的这些指标的分布:
ETS 的 MASE 直方图似乎比 TBATS 的波动范围更小。ETS 的中位数 MASE 也低于 TBATS。我们也可以看到类似的模式,在预报偏差方面,ETS 的预报偏差集中在零附近,并且波动较小。
回到第1章《介绍时间序列》,我们了解了为什么并非每个时间序列都是同样可预测的,并且探讨了三个因素来帮助我们思考这个问题——理解数据生成过程(DGP)、数据量和模式的充分重复。在大多数情况下,前两个因素比较容易评估,但第三个因素则需要一些分析。尽管基准方法的表现可以为我们提供一些关于时间序列可预测性的信息,但它们仍然依赖于模型。因此,与其衡量时间序列的可预测性,我们可能更好地衡量所选模型如何逼近时间序列。这时,一些更基本的技术(依赖于时间序列的统计性质)就显得尤为重要。
评估时间序列的可预测性
尽管有许多统计方法可以用来评估时间序列的可预测性,但我们这里只讨论一些更容易理解且在处理大规模时间序列数据集时实用的方法。相关代码可以在《02-Forecastability.ipynb》笔记本中找到。
变异系数(CoV)
变异系数(CoV)基于这样一个事实:你在时间序列中发现的变异性越大,预测它就越困难。那么,我们如何衡量一个随机变量的变异性呢?标准差。
在许多真实世界的时间序列中,我们看到的变化取决于时间序列的尺度。假设有两个零售产品,A 和 B。A 的平均月销量为 15,而 B 的月销量为 50。如果我们观察这样的真实世界例子,我们会发现,如果 A 和 B 的标准差相同,那么 B(具有较高的平均值)比 A 更容易预测。为了适应这种现象并确保将数据集中的所有时间序列带到统一的尺度,我们可以使用变异系数(CoV):
这里,σ\sigmaσ 是标准差,μ\muμ 是时间序列的均值,nnn 是时间序列的长度。
变异系数(CoV)是数据点围绕均值的相对离散程度,这比仅仅查看标准差要好得多。
变异系数的值越大,时间序列的可预测性越差。虽然没有严格的截止值,但通常认为 CoV 值为 0.49 是一个经验法则,可以用来区分相对容易预测的时间序列和难以预测的时间序列。根据数据集的整体难度,我们可以调整这个截止值。一个我发现有用的方法是绘制数据集中 CoV 值的直方图,并基于该图推导出截止值。
尽管变异系数在业界广泛使用,但它也存在一些关键问题:
- 它不考虑季节性。正弦或余弦波的变异系数(CoV)会高于水平线,但我们知道这两者的可预测性是相同的。
- 它不考虑趋势。线性趋势会使一个序列的 CoV 较高,但我们知道它同样是可预测的,就像水平线一样。
- 它不处理时间序列中的负值。如果时间序列中有负值,它会使均值变小,从而抬高 CoV。
为了克服这些缺点,我们提出了另一个衍生度量——残差变异性(RV)。
残差变异性(RV)
残差变异性的思想是尽量测量与变异系数(CoV)相同的变异性,但不受其缺点的影响。我在思考如何避免使用 CoV 的问题,尤其是季节性问题时,尝试将 CoV 应用到季节性分解后的残差上。那时我意识到残差会有一些负值,而 CoV 在这种情况下表现不佳。需求预测和概率预测领域的思想领袖 Stefan de Kok 提出了使用原始实际值的均值来计算,这个方法是有效的。
为了计算残差变异性(RV),你需要执行以下步骤:
- 进行季节性分解。
- 计算残差或不规则成分的标准差。
- 将标准差除以原始观测值的均值(在分解之前)。
数学上可以表示为:
其中,σ\sigmaσ 是分解后的残差标准差,μ\muμ 是原始观测值的均值。
这里的关键假设是季节性和趋势是可以预测的成分。因此,我们对时间序列可预测性的评估应该仅关注残差的变异性。然而,我们不能在残差上使用变异系数(CoV),因为残差可以有负值和正值,因此残差的均值失去了对序列水平的解释,并趋向于零。当残差趋向零时,由于除以均值,CoV度量趋向于无穷大。因此,我们使用原始序列的均值作为缩放因子。
让我们来看一下如何计算我们数据集中所有时间序列的残差变异性(这些序列是以紧凑形式存储的):
block_df["rv"] = block_df.progress_apply(lambda x: calc_norm_sd(x['residuals'], x['energy_consumption']), axis=1)
在本节中,我们查看了基于时间序列标准差的两种度量。现在,让我们来看看评估时间序列可预测性的方法。
基于熵的度量
熵是科学中的一个普遍术语。它出现在物理学、量子力学、社会科学和信息理论中。在这些领域中,熵通常用于描述系统中的混乱或不可预测性。在我们现在关注的领域中,熵来源于信息理论。信息理论涉及量化、存储和传输数字信息。
克劳德·E·香农(Claude E. Shannon)在其开创性的论文《通信的数学理论》中,提出了作为统计过程的定性和定量通信模型。虽然这篇论文介绍了很多概念,但对我们有用的几个概念包括信息熵和比特——信息的基本测量单位。
参考文献检查: 克劳德·E·香农的《通信的数学理论》在参考文献部分被引用为参考文献3。
这一理论内容非常丰富,但为了总结关键信息,可以参考以下简短的术语表:
- 信息不过是一系列符号,这些符号可以通过一个介质(称为信道)从接收者传递到发送者。例如,当我们给某人发短信时,符号序列是我们正在发送的语言中的字母/单词;信道是电子介质。
- 熵可以理解为在给定符号分布的情况下,符号序列中不确定性或惊讶的量。
- 比特,正如我们前面提到的,是信息的单位,是一个二进制数字。它可以是0或1。
现在,如果我们要传输一个比特的信息,它将把接收者的不确定性减少一半。为了更好地理解这一点,我们考虑一次掷硬币的过程。我们将硬币抛向空中,在它旋转的时候,我们不知道它会是正面还是反面。但我们知道,它会是这两者之一。当硬币落地并最终停下时,我们发现它是正面。我们可以用一个比特的信息来表示硬币是正面还是反面(0表示正面,1表示反面)。因此,当硬币落下时传递给我们的信息,将可能的结果从两个(正面和反面)减少到一个(正面)。这种传递可以用一个比特的信息来实现。
在信息理论中,离散随机变量的熵是该变量可能结果中的信息、惊讶或不确定性的平均水平。更技术性地说,它是对随机变量中信息的最佳编码方案所需的比特数的期望值。
额外阅读: 如果你想直观理解熵、交叉熵、Kullback-Leibler散度等内容,可以去进一步阅读部分。那里有一些博客链接(其中一个是我的博客),我们试图阐明这些度量的直观含义。
熵的正式定义如下:
这里,XXX 是具有可能结果 x1,x2,…,xnx_1, x_2, \dots, x_nx1,x2,…,xn 的离散随机变量。每个结果发生的概率分别用 P(x1),P(x2),…,P(xn)P(x_1), P(x_2), \dots, P(x_n)P(x1),P(x2),…,P(xn) 表示。
为了帮助理解这一点,我们可以这样思考:概率分布越分散,分布中的混乱就越多,从而熵值也就越高。我们可以通过一些代码来快速验证这一点:
# 创建一个平衡的概率分布数组
flat = np.array([0.1, 0.2, 0.3, 0.2, 0.2])
# 计算熵
print((-np.log2(flat) * flat).sum())
# 输出:2.2464393446710154
# 创建一个具有概率峰值的数组
sharp = np.array([0.1, 0.6, 0.1, 0.1, 0.1])
# 计算熵
print((-np.log2(sharp) * sharp).sum())
# 输出:1.7709505944546688
在这里,我们可以看到,具有更大概率分布范围的概率分布熵值更高。
在时间序列的背景下,nnn 是时间序列观察值的总数,而 P(xi)P(x_i)P(xi) 是时间序列字母表中每个符号的概率。尖锐的分布意味着时间序列的值集中在一个小范围内,通常更容易预测。另一方面,宽广或平坦的分布意味着时间序列的值可以在更广泛的范围内平等分布,因此更难预测。
如果我们有两个时间序列——一个包含掷硬币的结果,另一个包含掷骰子的结果——掷骰子的结果会有1到6之间的任何一个结果,而掷硬币的结果只能是0或1。掷硬币的时间序列的熵较低,因此比掷骰子的时间序列更容易预测。
然而,由于时间序列通常是连续的,而熵要求是离散的随机变量,我们可以采用一些策略将连续时间序列转换为离散时间序列。可以应用许多策略,如量化或分箱,这会导致多种复杂性度量。我们来回顾一个有用且实用的度量。
谱熵
为了计算时间序列的熵,我们需要对时间序列进行离散化。做法之一是使用快速傅里叶变换(FFT)和功率谱密度(PSD)。对连续时间序列的这种离散化用于计算谱熵。
我们之前在本章中学习了傅里叶变换,并用它生成了一个基线预测。但使用FFT,我们还可以估算一个叫做功率谱密度的量。它回答了这样一个问题:信号在特定频率下的强度是多少?有许多方法可以从时间序列中估算功率谱密度,但最简单的一种方法是使用Welch方法,它是一种基于离散傅里叶变换的非参数方法。Scipy中也实现了一个方便的函数来计算功率谱密度,函数签名为 periodogram(x)。
返回的功率谱密度(PSD)的长度等于估算的频率数,但这些是密度值而不是明确的概率。因此,我们需要将功率谱密度归一化到0和1之间:
这里,FFF 是返回的功率谱密度中包含的频率数量。
现在我们有了概率分布,我们可以将其直接代入熵公式,得到谱熵:
当我们介绍基于熵的度量时,我们看到概率分布的概率质量分布越分散,熵越高。在这个上下文中,谱密度分布越广,谱熵就越高。因此,较高的谱熵意味着时间序列更加复杂,因此更难预测。
由于FFT有平稳性假设,建议在使用谱熵作为度量之前将时间序列转换为平稳序列。我们甚至可以将该度量应用于去趋势和去季节性处理后的时间序列,我们可以称之为残差谱熵。书中的GitHub仓库中包含了谱熵的实现,路径为 src.forecastability.entropy.spectral_entropy。这个实现还有一个参数 transform_stationary,如果设置为True,它会在应用谱熵之前先去趋势。让我们看看如何在我们的数据集中计算谱熵:
from src.forecastability.entropy import spectral_entropy
block_df["spectral_entropy"] = block_df.energy_consumption.progress_apply(lambda x: spectral_entropy(x, transform_stationary=True))
block_df["residual_spectral_entropy"] = block_df.residuals.progress_apply(spectral_entropy)
还有其他基于熵的度量,如近似熵和样本熵,但我们在本书中不会讨论它们。它们的计算量较大,并且通常不适用于少于200个值的时间序列。如果你对这些度量感兴趣,可以参考“进一步阅读”部分。
另一个采取稍微不同路径的度量是Kaboudan度量。
Kaboudan度量
1999年,Kaboudan定义了一个用于时间序列可预测性的度量,称为-度量。其背后的思想非常简单。如果我们对时间序列进行块交换(block-shuffling),本质上是在破坏时间序列中的信息。块交换是将时间序列分成若干块,然后对这些块进行重新排列。因此,如果我们计算基于时间序列生成的预测的平方误差和(SSE),并将其与基于块交换时间序列生成的预测的SSE进行对比,我们就可以推断时间序列的可预测性。计算公式如下:
如果时间序列包含某些可预测的信号,SSEY将小于SSES,而度量值接近1。这是因为有些信息或模式因块交换而被打破。另一方面,如果序列只是白噪声(定义上是不可预测的),SSEY和SSES之间几乎没有区别,度量值接近0。
2002年,Duan在他的论文中研究了这个度量,并建议进行一些修改。他发现,尤其是在较长的时间序列中,度量值通常集中在接近1的狭窄范围内,并建议对公式进行略微修改。这就是我们所说的修改后的Kaboudan度量。度量的下限也被裁剪为0。有时,由于SSES小于SSEY(因为该序列不可预测,且由于纯粹的偶然性,块交换使得SSE较低),该度量值可能会小于零。
参考文献:
Kaboudan度量的研究论文在参考文献部分被列为参考文献4,Duan提出的后续修改被列为参考文献5。
此修改版及原版均已在本书的GitHub仓库中实现。
对于生成预测的模型没有限制,这使得它更加灵活。理想情况下,我们可以选择一种经典的统计方法,它足够快速,可以应用于整个数据集。但这也使得Kaboudan度量依赖于模型,并且模型的局限性固有地体现在度量中。该度量度量的是序列预测的难度以及模型预测序列的难度。
度量的使用:
from src.forecastability.kaboudan import kaboudan_metric, modified_kaboudan_metric
model = Theta(theta=3, seasonality_period=48*7, season_mode=SeasonalityMode.ADDITIVE)
block_df["kaboudan_metric"] = [kaboudan_metric(r[0], model=model, block_size=5, backtesting_start=0.5, n_folds=1) for r in tqdm(zip(*block_df[["energy_consumption"]].to_dict("list").values()), total=len(block_df))]
block_df["modified_kaboudan_metric"] = [modified_kaboudan_metric(r[0], model=model, block_size=5, backtesting_start=0.5, n_folds=1) for r in tqdm(zip(*block_df[["energy_consumption"]].to_dict("list").values()), total=len(block_df))]
虽然我们可以使用许多其他度量来评估可预测性,但我们刚才回顾的这几种度量涵盖了很多常见的使用场景,应该足以衡量任何时间序列数据集的预测难度。我们可以使用这些度量将一个时间序列与另一个时间序列进行比较,或者将数据集中的一组相关时间序列与另一个数据集进行基准测试。
进一步阅读: 如果你想更深入地了解这些度量的行为、它们之间的相似性以及它们在衡量预测能力方面的有效性,可以参考03-Forecastability.ipynb笔记本的末尾。我们计算了这些度量之间的等级相关性,以了解它们的相似性。我们还可以找到与最佳基线方法计算的度量之间的等级相关性,了解这些度量在估计时间序列预测能力方面的效果。我强烈建议你玩玩笔记本,理解不同度量之间的差异。选取一些时间序列,看看不同的度量如何给出稍有不同的解释。
祝贺你生成了基线预测——这是我们用本书生成的第一批预测!欢迎进入笔记本,尝试调整方法的参数,看看预测结果如何变化。这将帮助你形成关于基线方法如何工作的直觉。如果你有兴趣了解更多如何改进这些基线方法的信息,请查看进一步阅读部分,我们提供了链接到F. Petropoulos和E. Spiliotis的论文《The Wisdom of the Data: Getting the Most Out of Univariate Time Series Forecasting》。
总结
至此,我们已经完成了第一部分“了解时间序列”的内容。从最初理解时间序列到生成具有竞争力的基线预测,我们走了很长一段路。在这个过程中,我们学习了如何处理缺失值和异常值,以及如何使用pandas操作时间序列数据。我们将这些技能应用于一个真实的能源消费数据集。我们还研究了时间序列的可视化和分解方法。在这一章中,我们设置了一个测试框架,学习了如何使用NIXTLA库生成基线预测,并查看了可以用来理解时间序列可预测性的几个度量方法。
对于某些读者来说,这一章可能是一个复习,我们希望它在细节和实际考虑方面提供了一些价值。对于其他读者,我们希望你们在基础上已经打下了良好的基础,可以在接下来的部分中开始探索如何使用机器学习的现代技术进行时间序列预测。
在下一章中,我们将讨论机器学习的基础知识,并深入探讨时间序列预测。
参考文献
本章引用的参考文献如下:
- Assimakopoulos, Vassilis 和 Nikolopoulos, K.(2000)。《Theta模型:一种用于预测的分解方法》。国际预测学杂志,16,521-530。www.researchgate.net/publication…
- Rob J. Hyndman, Baki Billah.(2003)。《揭开Theta方法的面纱》。国际预测学杂志,19,287-290。robjhyndman.com/papers/Thet…
- Shannon, C.E.(1948)。《通信的数学理论》。贝尔系统技术杂志,27:379-423。people.math.harvard.edu/~ctm/home/t…
- Kaboudan, M.(1999)。《使用遗传编程应用于股票收益的时间序列可预测性度量》。预测学杂志,18,345-357:www.aiecon.org/conference/…
- Duan, M.(2002)。《时间序列的可预测性》:citeseerx.ist.psu.edu/viewdoc/dow…
- De Livera, A. M., & Hyndman, R. J.(2009)。《使用指数平滑法预测具有复杂季节模式的时间序列》(经济计量学与商业统计工作报告系列15/09)
- Hyndman, Rob. “Rob J Hyndman - TBATS with Regressors.” Rob J Hyndman, 6 Oct. 2014, robjhyndman.com/hyndsight/t…
进一步阅读
要了解更多有关本章内容的资源,请参考以下链接:
- Manu Joseph的《信息理论与熵》:deep-and-shallow.com/2020/01/09/…
- Chris Olah的《视觉信息》:colah.github.io/posts/2015-…
- 《傅里叶变换》:betterexplained.com/articles/an…
- 3blue1brown的《傅里叶变换——视觉介绍》:www.youtube.com/watch?v=spU…
- Richie Vink的《通过例子理解傅里叶变换》:www.ritchievink.com/blog/2017/0…
- Delgado-Bonal A, Marshak A. 《近似熵和样本熵:全面教程》。熵,2019年;21(6):541:www.mdpi.com/1099-4300/2…
- Yentes, J.M., Hunt, N., Schmid, K.K.等。 《适当使用近似熵和样本熵进行短数据集的分析》。生物医学工程学报,41,349–365(2013):doi.org/10.1007/s10…
- Ponce-Flores M, Frausto-Solís J, Santamaría-Bonfil G, Pérez-Ortega J, González-Barbosa JJ.《时间序列的复杂性及其与预测性能的关系》。熵,2020年;22(1):89:www.mdpi.com/1099-4300/2…
- Petropoulos F, Spiliotis E.《数据的智慧:如何充分利用单变量时间序列预测》。预测,2021年;3(3):478-497。doi.org/10.3390/for…