Python 时间序列的机器学习
什么是时间序列?
由于这是一本关于时间序列数据的书,我们应该首先澄清我们所讨论的内容。在本节中,我们将介绍时间序列及其特性,并深入探讨与机器学习和统计学相关的不同问题和分析类型。
许多学科,如金融、公共行政、能源、零售和医疗保健,主要依赖时间序列数据。微观经济学和宏观经济学的很多领域依赖于应用统计学,特别是侧重于时间序列分析和建模。以下是一些时间序列数据的例子:
-
股票指数的每日收盘值
-
每周某疾病的感染人数
-
每周的火车事故系列
-
每日降水量
-
传感器数据,如每小时的温度测量
-
每年人口增长
-
一家公司若干年内的季度盈利
这仅仅是举几个例子。任何涉及时间变化的数据都可以视为时间序列。
也许有必要简要定义一下什么被视为时间序列。
定义:时间序列是按时间顺序排列的观测数据集。
这是一个非常广泛的定义。或者我们也可以说,时间序列是按时间顺序排列的数据点序列,或者说时间序列是一个随机过程的结果。
从形式上讲,我们可以有两种方式定义时间序列。第一种是将其定义为从时间域到实数域的映射:

由于时间是数据集的主要索引,因此,时间序列数据集描述了世界如何随时间变化。它们通常涉及如何通过过去影响现在或未来的问题。
监控和数据收集的增加带来了对统计学和机器学习技术的需求,这些技术应用于时间序列,以预测和描述复杂系统或系统内组件的行为。与时间序列打交道的一个重要部分是如何基于过去预测未来。这就是所谓的预测。
一些方法允许将商业周期作为额外特征添加。这些额外的特征称为外生特征——它们是依赖于时间的解释性变量。我们将在第三章,时间序列预处理中讨论特征生成的示例。
时间序列的特征
这是一个从 Google 趋势导出的时间序列数据集示例,展示了 Python、R 和 Julia 的搜索量:

图 1.1:时间序列数据集的提取
这是一个多变量时间序列,包含 Python、R 和 Julia 的列。第一列是索引列,是一个日期列,周期为每月。如果我们只有一个变量,我们称其为单变量序列。如果我们只有一种编程语言而不是三种,那么这个数据集将是单变量的。
时间序列通常以离散时间形式出现,其中每个点之间的时间差是相同的。时间序列的最重要特征如下:
-
值的长期变化(趋势)
-
季节性变化(季节性)
-
不规则或周期性成分
趋势是某事物发展或变化的一般方向,例如序列中的长期增加或减少。一个可以观察到趋势的例子是全球变暖,这是过去半个世纪温度持续上升的过程。
下面是来自 NASA 发布的 GISS 表面温度分析数据集中的过去 100 年全球表面温度变化的图表:

图 1.2:1880 年至 2019 年间的 GISS 表面温度分析
如你在图 1.2中所见,温度变化在 20 世纪中期之前围绕 0 波动;然而,从那时起,整体温度逐年上升的趋势变得十分明显。
季节性是指在特定的、不到一年的规律间隔内发生的变化。季节性可以在不同的时间跨度上发生,例如每日、每周、每月或每年。每周季节性的一个例子是冰淇淋的销售量在每个周末都会增加。此外,根据你所居住的地方,冰淇淋可能只在春夏季节出售。这是一种年度变化。
除了季节性变化和趋势外,还有一些变动,它们的频率不固定,或者以非季节性频率的方式波动。我们中的一些人可能能基于现有知识对这些变动进行解释。
作为一个不规则的周期性变化例子,银行假期每年可能会落在不同的日历日期上,而促销活动则可能取决于商业决策,例如推出新产品。作为一个非季节性变化的周期性变化例子,毫秒级别的变化或发生在超过一年时间段的变化,将不被称为季节性效应。
平稳性是指时间序列在时间推移中其分布不会发生变化,如其总结统计量所描述的那样。如果一个时间序列是平稳的,那么它意味着该序列没有趋势,也没有确定性的季节性变化,尽管其他周期性变化是允许的。为了应用这些方法,我们需要通过去除季节性和趋势,将非平稳数据转换为平稳数据。
Python 在时间序列中的应用
对于时间序列,主要有两种语言,R 和 Python,值得简要比较这两者,并描述是什么让 Python 与众不同。Python 是目前最受欢迎的编程语言之一。根据 2021 年 2 月的 TIOBE 数据,它仅次于 C 和 Java。
| 排名 | 语言 | 评分 |
|---|---|---|
| 1 | C | 16.34% |
| 2 | Java | 11.29% |
| 3 | Python | 10.86% |
| 4 | C++ | 6.88% |
| ... | ... | ... |
| 11 | R | 1.56% |
| ... | ... | ... |
| 29 | Julia | 0.52% |
图 1.8:TIOBE 语言使用统计数据
我加入了 R 和 Julia 这两种用于数据科学的其他语言,以支持 Python 是最受欢迎的数据科学语言这一观点。在比较 Python、R 和 Julia 的搜索量时,我们可以看到,Python 远远超过 R,而 Julia 则排在第三位,遥不可及。实际上,Python 的排名类似于 C、Java 和 C++等语言。而 R 的排名则接近汇编语言和 Groovy,Julia 则处于 Prolog 等专业语言的水平。
R 的社区由统计学家和数学家组成,R 的优势在于统计学和绘图(ggplot)。R 的弱点在于其工具支持以及几乎没有一致的代码风格规范。
另一方面,Python 在统计学和科学计算方面追赶上了,通过 NumPy、SciPy 和 pandas 等库,它在数据科学的使用率和可用性方面已经超越了 R。
Python 在机器学习库方面非常突出。以下库完全或主要是用 Python 编写的:
-
Scikit-learn 是用 Python 和 Cython(类似于 C 编程语言的 Python 方言)编写的。它提供了一个非常大范围的算法实现,用于训练和评估机器学习模型。
-
Statsmodels 提供了统计测试和模型,如广义线性模型(GLM)、ARMA 等。
-
Keras 是一个用于训练神经网络的 Python 抽象,它与 TensorFlow 和其他库进行交互。
一些最受欢迎的机器学习框架——那些在开发中得到广泛使用并具有大量可扩展算法的框架,例如 TensorFlow、PyTorch 和 XGBoost——也主要用 Python 编写,或为 Python 提供一流的接口。
pandas
pandas 是 Python 生态系统中数据科学中最重要的库之一,用于数据处理和分析。最初发布于 2008 年,它已成为 Python 成功的重要驱动因素之一。
pandas 具有重要的时间序列功能,如日期范围生成、频率转换、移动窗口统计、日期偏移和滞后。
让我们通过一些基础知识来创建一个时间序列,如下所示:
import pandas as pd
pd.date_range(start='2021-03-24', end='2021-09-01')
这给我们一个像这样的DateTimeIndex:
DatetimeIndex(['2021-03-24', '2021-03-25', '2021-03-26', '2021-03-27',
'2021-03-28', '2021-03-29', '2021-03-30', '2021-03-31',
'2021-04-01', '2021-04-02',
...
'2021-08-23', '2021-08-24', '2021-08-25', '2021-08-26',
'2021-08-27', '2021-08-28', '2021-08-29', '2021-08-30',
'2021-08-31', '2021-09-01'],
dtype='datetime64[ns]', length=162, freq='D')
我们还可以按如下方式创建一个时间序列:
pd.Series(pd.date_range("2021", freq="D", periods=3))
这将给我们一个类似这样的时间序列:
0 2021-01-01
1 2021-01-02
2 2021-01-03
dtype: datetime64[ns]
正如你所见,这种类型称为DatetimeIndex。这意味着我们可以使用这种数据类型来索引数据集。
其中最重要的功能之一是从string或单独的列解析到date或datetime对象:
import pandas as pd
df = pd.DataFrame({'year': [2021, 2022],
'month': [3, 4],
'day': [24, 25]}
)
ts1 = pd.to_datetime(df)
ts2 = pd.to_datetime('20210324', format='%Y%m%d')
我们创建了两个时间序列。
你可以像这样进行滚动窗口计算:
s = pd.Series([1, 2, 3, 4, 5])
s.rolling(3).sum()
你能猜到这个的结果吗?如果不能,为什么不在你的 Python 解释器中试试?
一个时间序列通常会是一个带有时间对象的索引和一个或多个带有数字或其他类型的列,如下所示:
import numpy as np
rng = pd.date_range('2021-03-24', '2021-09-01', freq='D')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
我们可以看一下我们的时间序列:
2021-03-24 -2.332713
2021-03-25 0.177074
2021-03-26 -2.136295
2021-03-27 2.992240
2021-03-28 -0.457537
...
2021-08-28 -0.705022
2021-08-29 1.089697
2021-08-30 0.384947
2021-08-31 1.003391
2021-09-01 -1.021058
Freq: D, Length: 162, dtype: float64
我们可以像处理任何其他 pandas Series 或 DataFrame 一样索引这些时间序列数据集。ts[:2].index会给我们:
DatetimeIndex(['2021-03-24', '2021-03-25'], dtype='datetime64[ns]', freq='D')
有趣的是,我们可以直接使用字符串或 datetime 对象进行索引。例如,ts['2021-03-28':'2021-03-30']会给我们:
2021-03-28 -0.457537
2021-03-29 -1.089423
2021-03-30 -0.708091
Freq: D, dtype: float64
你可以使用shift方法将时间序列的值向前或向后移动。这会改变数据的对齐方式:
ts.shift(1)[:5]
我们还可以改变时间序列对象的分辨率,例如这样:
ts.asfreq('M')
请注意datetime与pd.DateTimeIndex之间的区别。
我们可以使用相同的函数来拟合季节性变化和趋势。由于趋势可能主导季节性变化,因此在估算季节性之前,我们需要去除趋势:
from numpy import polyfit
def fit(X, y, degree=3):
coef = polyfit(X, y, degree)
trendpoly = np.poly1d(coef)
return trendpoly(X)
def get_season(s, yearly_periods=4, degree=3):
X = [i%(365/4) for i in range(0, len(s))]
seasonal = fit(X, s.values, degree)
return pd.Series(data=seasonal, index=s.index)
def get_trend(s, degree=3):
X = list(range(len(s)))
trend = fit(X, s.values, degree)
return pd.Series(data=trend, index=s.index)
让我们在全球气温上升的基础上绘制季节性和趋势图!
import seaborn as sns
plt.figure(figsize=(12, 6))
temperatures['trend'] = get_trend(temperatures['GCAG'])
temperatures['season'] = get_season(temperatures['GCAG'] - temperatures['trend'])
sns.lineplot(data=temperatures[['GCAG', 'season', 'trend']])
plt.ylabel('Temperature change');
这是我们得到的图表:

图 2.9:从 19 世纪末到今天的温度变化
这展示了你可以使用 NumPy 中的插件功能进行曲线拟合,以便找到趋势和季节性。如果你想进一步实验,可以调整多项式的阶数或季节性分量,看看能否得到更好的拟合,或者找到另一个季节性成分。我们本来也可以使用其他库的功能,比如statsmodels中的seasonal.seasonal_decompose(),或者 Facebook 的 Prophet,它通过傅里叶系数来分解季节性成分。
现在我们已经看到了如何估计季节性和趋势,接下来我们将讨论其他统计量和可视化。继续使用污染数据集,并且拿起我们在第一章中看到的 EEG 数据集,我们将在 Python 中实际展示如何获取这些统计量和图表,并如何识别趋势和季节性。
自相关是信号与其滞后版本之间的相关性。自相关图绘制了自相关与滞后之间的函数关系。自相关图有助于发现重复的模式,通常用于信号处理。自相关有助于识别周期性信号。让我们绘制污染数据的自相关:
pollution = pollution.pivot("Year", "City", "SPM")
pd.plotting.autocorrelation_plot(pollution['London'])
这是我们得到的图:

图 2.10:伦敦污染的自相关图
我们可以看到高自相关仅在几年的滞后下出现。在大约 100 年时,有一个负自相关点,之后自相关保持在 0 附近。
SPM 图清楚地表明,空气污染不是一个平稳过程,因为自相关并没有平坦。你还可以比较污染的走势,显示出有趋势,因此均值也发生变化——这是序列非平稳的另一个指示。
我们还可以进行统计检验。检验平稳性的方法是扩展的迪基–富勒检验:
from statsmodels.tsa import stattools
stattools.adfuller(pollution['London'])
(-0.33721640804242853,
0.9200654843183897,
13,
303,
{'1%': -3.4521175397304784,
'5%': -2.8711265007266666,
'10%': -2.571877823851692},
1684.6992663493872)
第二个返回值是 p 值,它表示在给定零假设的情况下,得到至少与观察结果一样极端的测试结果的显著性或概率。当 p 值小于 5%或 0.05 时,我们通常会拒绝零假设,并且可以假设我们的时间序列是平稳的。在我们的案例中,我们不能假设该序列是平稳的。
总结
在本章中,我们介绍了 TSA(时间序列分析)作为从时间序列中提取摘要和其他统计信息的过程。我们将这一过程分解为理解变量、揭示变量之间的关系以及识别趋势和季节性。
我们介绍了 datetime 和 pandas,这两个在 TSA 中不可或缺的库及其在时间序列中的功能;例如,重新采样。在本章中,我们列举并定义了许多摘要统计量,包括均值、标准差、中位数、标准误、置信区间、皮尔逊相关系数和协方差。
我们还讨论了季节性、周期性变化和平稳性等概念。我们讨论了为什么平稳性很重要,以及如何测试平稳性。
- 点赞
- 收藏
- 关注作者
评论(0)