LagFeatures#

滞后特征在数据科学中常用于使用传统方法预测时间序列。

机器学习模型,如线性回归或随机森林。滞后特征是包含时间序列前一时间步信息的特征。

在预测一个变量的未来值时,该变量的过去值很可能是具有预测性的。其他预测特征的过去值也可能对我们的预测有用。因此,在预测中,从时间序列数据中创建滞后特征并将其用作机器学习算法或预测模型的输入是一种常见做法。

什么是滞后特征?#

滞后特征是时间序列在过去 k 个周期(期)的值,其中 k 是滞后量,由用户设置。例如,滞后为 1 的特征包含时间序列前一个时间点的值。滞后为 3 的特征包含 3 个时间点之前的值,依此类推。通过改变 k,我们可以创建具有多个滞后的特征。

在Python中,我们可以使用pandas方法`shift`来创建滞后特征。例如,通过执行`X[my_variable].shift(freq=”1H”, axis=0)`,我们创建了一个新的特征,该特征由`my_variable`的滞后值组成,滞后时间为1小时。

Feature-engine 的 LagFeatures 自动从多个变量和使用多个滞后项创建滞后特征。它在底层使用 pandas 的 shift,并自动将新特征连接到输入数据框。

自动化滞后特征创建#

我们可以通过两种方式使用 LagFeatures 来指示滞后 k。就像使用 pandas 的 shift 一样,我们可以使用参数 periods 来指示滞后。该参数接受表示特征将滞后的行数的整数。

另外,我们可以使用参数 freq,它接受一个包含周期和频率的字符串,并根据日期时间索引滞后特征。例如,如果我们传递 freq="1D",特征的值将向前移动1天。

这个 LagFeatures 转换器的工作方式与 pandas.shift 非常相似,但与 pandas.shift 不同的是,我们可以使用 periodsfreq 来指示滞后,但不能同时使用两者。此外,与 pandas.shift 不同,我们只能将特征向前滞后。

LagFeatures 相对于 pandas.shift 有几个优势:

  • 首先,它可以同时创建具有多个 k 值的特征。

  • 其次,它将带有名称的特征添加到原始数据框中。

  • 第三,它有 fit()transform() 方法,这使得它与 Scikit-learn 的 Pipeline 和交叉验证函数兼容。

请注意,在当前的实现中,LagFeatures 仅适用于索引(包含时间序列时间戳)包含唯一值且无 NaN 的数据框。

示例#

让我们创建一个玩具数据集来展示如何使用 LagFeatures 添加滞后特征。该数据框包含3个数值变量、一个分类变量和一个日期时间索引。我们还创建了一个任意目标。

import pandas as pd

X = {"ambient_temp": [31.31, 31.51, 32.15, 32.39, 32.62, 32.5, 32.52, 32.68],
     "module_temp": [49.18, 49.84, 52.35, 50.63, 49.61, 47.01, 46.67, 47.52],
     "irradiation": [0.51, 0.79, 0.65, 0.76, 0.42, 0.49, 0.57, 0.56],
     "color": ["green"] * 4 + ["blue"] * 4,
     }

X = pd.DataFrame(X)
X.index = pd.date_range("2020-05-15 12:00:00", periods=8, freq="15min")
y = pd.Series([1, 2, 3, 4, 5, 6, 7, 8])
y.index = X.index

X.head()

下面我们看到我们的玩具数据框的输出:

                     ambient_temp  module_temp  irradiation  color
2020-05-15 12:00:00         31.31        49.18         0.51  green
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue

在这里我们打印并展示目标变量:

y
2020-05-15 12:00:00    1
2020-05-15 12:15:00    2
2020-05-15 12:30:00    3
2020-05-15 12:45:00    4
2020-05-15 13:00:00    5
2020-05-15 13:15:00    6
2020-05-15 13:30:00    7
2020-05-15 13:45:00    8
Freq: 15min, dtype: int64

向前移动一行#

现在我们将通过将所有数值变量向前滞后1行来创建滞后特征。注意,LagFeatures 会自动找到所有数值变量。

from feature_engine.timeseries.forecasting import LagFeatures

lag_f = LagFeatures(periods=1)

X_tr = lag_f.fit_transform(X)

X_tr.head()

我们可以在数据框的右侧找到滞后特征。请注意,这些值已向前移动了一行。

                     ambient_temp  module_temp  irradiation  color  \
2020-05-15 12:00:00         31.31        49.18         0.51  green
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue

                     ambient_temp_lag_1  module_temp_lag_1  irradiation_lag_1
2020-05-15 12:00:00                 NaN                NaN                NaN
2020-05-15 12:15:00               31.31              49.18               0.51
2020-05-15 12:30:00               31.51              49.84               0.79
2020-05-15 12:45:00               32.15              52.35               0.65
2020-05-15 13:00:00               32.39              50.63               0.76

需要滞后的变量存储在 LagFeaturesvariables_ 属性中。

lag_f.variables_
['ambient_temp', 'module_temp', 'irradiation']

我们可以使用 get_feature_names_out() 方法获取原始变量的名称以及转换后的数据框中返回的滞后特征:

lag_f.get_feature_names_out()
['ambient_temp',
 'module_temp',
 'irradiation',
 'color',
 'ambient_temp_lag_1',
 'module_temp_lag_1',
 'irradiation_lag_1']

当我们创建滞后特征时,我们会在训练数据集的前几行引入 nan 值,因为这些数据点没有过去的值。我们可以使用任意值来填补这些 nan 值,如下所示:

lag_f = LagFeatures(periods=1, fill_value=0)

X_tr = lag_f.fit_transform(X)

print(X_tr.head())

我们看到 nan 值被替换为 0:

                     ambient_temp  module_temp  irradiation  color  \
2020-05-15 12:00:00         31.31        49.18         0.51  green
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue

                     ambient_temp_lag_1  module_temp_lag_1  irradiation_lag_1
2020-05-15 12:00:00                0.00               0.00               0.00
2020-05-15 12:15:00               31.31              49.18               0.51
2020-05-15 12:30:00               31.51              49.84               0.79
2020-05-15 12:45:00               32.15              52.35               0.65
2020-05-15 13:00:00               32.39              50.63               0.76

或者,我们可以删除在滞后特征中存在缺失值的行,如下所示:

lag_f = LagFeatures(periods=1, drop_na=True)

X_tr = lag_f.fit_transform(X)

print(X_tr.head())
                     ambient_temp  module_temp  irradiation  color  \
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue
2020-05-15 13:15:00         32.50        47.01         0.49   blue

                     ambient_temp_lag_1  module_temp_lag_1  irradiation_lag_1
2020-05-15 12:15:00               31.31              49.18               0.51
2020-05-15 12:30:00               31.51              49.84               0.79
2020-05-15 12:45:00               32.15              52.35               0.65
2020-05-15 13:00:00               32.39              50.63               0.76
2020-05-15 13:15:00               32.62              49.61               0.42

我们也可以删除在滞后特征中包含 nan 的行,然后像这样调整目标变量:

X_tr, y_tr = lag_f.transform_x_y(X, y)

X_tr.shape, y_tr.shape, X.shape, y.shape

我们创建了一个滞后特征为1,因此只有1行包含nan,该行已从训练集和目标中移除:

((7, 7), (7,), (8, 4), (8,))

创建多个滞后特征#

我们可以通过在列表中传递滞后期来使用一个转换器创建多个滞后特征。

lag_f = LagFeatures(periods=[1, 2])

X_tr = lag_f.fit_transform(X)

X_tr.head()

注意如何为每个数值变量创建了多个滞后特征,并添加到数据框的右侧。

                     ambient_temp  module_temp  irradiation  color  \
2020-05-15 12:00:00         31.31        49.18         0.51  green
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue

                     ambient_temp_lag_1  module_temp_lag_1  irradiation_lag_1  \
2020-05-15 12:00:00                 NaN                NaN                NaN
2020-05-15 12:15:00               31.31              49.18               0.51
2020-05-15 12:30:00               31.51              49.84               0.79
2020-05-15 12:45:00               32.15              52.35               0.65
2020-05-15 13:00:00               32.39              50.63               0.76

                     ambient_temp_lag_2  module_temp_lag_2  irradiation_lag_2
2020-05-15 12:00:00                 NaN                NaN                NaN
2020-05-15 12:15:00                 NaN                NaN                NaN
2020-05-15 12:30:00               31.31              49.18               0.51
2020-05-15 12:45:00               31.51              49.84               0.79
2020-05-15 13:00:00               32.15              52.35               0.65

我们可以按如下方式获取结果数据框中的特征名称:

lag_f.get_feature_names_out()
['ambient_temp',
 'module_temp',
 'irradiation',
 'color',
 'ambient_temp_lag_1',
 'module_temp_lag_1',
 'irradiation_lag_1',
 'ambient_temp_lag_2',
 'module_temp_lag_2',
 'irradiation_lag_2']

我们也可以替换在滞后特征中引入的 nan。这次,我们将使用一个字符串。这并不是训练机器学习算法的合适解决方案,但这里的想法是展示 LagFeatures 的功能。

lag_f = LagFeatures(periods=[1, 2], fill_value='None')

X_tr = lag_f.fit_transform(X)

print(X_tr.head())

在这种情况下,我们将滞后特征中的 nan 替换为字符串 None:

                     ambient_temp  module_temp  irradiation  color  \
2020-05-15 12:00:00         31.31        49.18         0.51  green
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue

                    ambient_temp_lag_1 module_temp_lag_1 irradiation_lag_1  \
2020-05-15 12:00:00               None              None              None
2020-05-15 12:15:00              31.31             49.18              0.51
2020-05-15 12:30:00              31.51             49.84              0.79
2020-05-15 12:45:00              32.15             52.35              0.65
2020-05-15 13:00:00              32.39             50.63              0.76

                    ambient_temp_lag_2 module_temp_lag_2 irradiation_lag_2
2020-05-15 12:00:00               None              None              None
2020-05-15 12:15:00               None              None              None
2020-05-15 12:30:00              31.31             49.18              0.51
2020-05-15 12:45:00              31.51             49.84              0.79
2020-05-15 13:00:00              32.15             52.35              0.65

或者,我们可以删除包含滞后特征中的 nan 的行,然后调整目标变量:

lag_f = LagFeatures(periods=[1, 2], drop_na=True)

lag_f.fit(X)

X_tr, y_tr = lag_f.transform_x_y(X, y)

X_tr.shape, y_tr.shape, X.shape, y.shape

我们看到从训练集和目标中删除了2行:

((6, 10), (6,), (8, 4), (8,))

基于日期时间的滞后特征#

我们也可以利用数据框的时间戳信息来延迟特征,时间戳通常被转换为datetime格式。

例如,让我们通过将两个数值变量向前滞后30分钟来创建特征。

lag_f = LagFeatures(variables = ["module_temp", "irradiation"], freq="30min")

X_tr = lag_f.fit_transform(X)

X_tr.head()

请注意,功能已提前30分钟。

                     ambient_temp  module_temp  irradiation  color  \
2020-05-15 12:00:00         31.31        49.18         0.51  green
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue

                     module_temp_lag_30min  irradiation_lag_30min
2020-05-15 12:00:00                    NaN                    NaN
2020-05-15 12:15:00                    NaN                    NaN
2020-05-15 12:30:00                  49.18                   0.51
2020-05-15 12:45:00                  49.84                   0.79
2020-05-15 13:00:00                  52.35                   0.65

我们可以用一个数字来替换滞后特征中的 nan,如下所示:

lag_f = LagFeatures(
    variables=["module_temp", "irradiation"], freq="30min", fill_value=100)

X_tr = lag_f.fit_transform(X)

print(X_tr.head())

这里,我们将 nan 替换为 100:

                     ambient_temp  module_temp  irradiation  color  \
2020-05-15 12:00:00         31.31        49.18         0.51  green
2020-05-15 12:15:00         31.51        49.84         0.79  green
2020-05-15 12:30:00         32.15        52.35         0.65  green
2020-05-15 12:45:00         32.39        50.63         0.76  green
2020-05-15 13:00:00         32.62        49.61         0.42   blue

                     module_temp_lag_30min  irradiation_lag_30min
2020-05-15 12:00:00                 100.00                 100.00
2020-05-15 12:15:00                 100.00                 100.00
2020-05-15 12:30:00                  49.18                   0.51
2020-05-15 12:45:00                  49.84                   0.79
2020-05-15 13:00:00                  52.35                   0.65

另外,我们可以移除滞后特征中引入的 nan 并调整目标:

lag_f = LagFeatures(
    variables=["module_temp", "irradiation"], freq="30min", drop_na=True)

lag_f.fit(X)

X_tr, y_tr = lag_f.transform_x_y(X, y)

X_tr.shape, y_tr.shape, X.shape, y.shape

从训练数据集和目标中删除了两行:

((6, 6), (6,), (8, 4), (8,))

在滞后特征后删除变量#

同样地,我们可以将多个时间间隔滞后,但这次,在创建滞后特征后,让我们删除原始变量。

lag_f = LagFeatures(variables="irradiation",
                    freq=["30min", "45min"],
                    drop_original=True,
                    )

X_tr = lag_f.fit_transform(X)

X_tr.head()

我们现在可以看到在数据框末尾的多个滞后特征,并且原始变量也不存在于输出数据框中。

                     ambient_temp  module_temp  color  irradiation_lag_30min  \
2020-05-15 12:00:00         31.31        49.18  green                    NaN
2020-05-15 12:15:00         31.51        49.84  green                    NaN
2020-05-15 12:30:00         32.15        52.35  green                   0.51
2020-05-15 12:45:00         32.39        50.63  green                   0.79
2020-05-15 13:00:00         32.62        49.61   blue                   0.65

                     irradiation_lag_45min
2020-05-15 12:00:00                    NaN
2020-05-15 12:15:00                    NaN
2020-05-15 12:30:00                    NaN
2020-05-15 12:45:00                   0.51
2020-05-15 13:00:00                   0.79

这在时间序列预测中非常有用,因为原始变量通常是我们试图预测的变量,即目标变量。原始变量还包含在我们进行预测的时间点上**不**可用的值。

使用 pandas 系列#

如果你的时间序列是一个 pandas Series 而不是一个 pandas Dataframe,你需要在使用 LagFeatures 之前将其转换为 dataframe。

以下是一个 pandas Series:

X['ambient_temp']
2020-05-15 12:00:00    31.31
2020-05-15 12:15:00    31.51
2020-05-15 12:30:00    32.15
2020-05-15 12:45:00    32.39
2020-05-15 13:00:00    32.62
2020-05-15 13:15:00    32.50
2020-05-15 13:30:00    32.52
2020-05-15 13:45:00    32.68
Freq: 15T, Name: ambient_temp, dtype: float64

我们可以使用 LagFeatures 来创建,例如,通过滞后 pandas Series 来创建3个特征,如果我们使用 to_frame() 方法将其转换为 pandas Dataframe 的话:

lag_f = LagFeatures(periods=[1, 2, 3])

X_tr = lag_f.fit_transform(X['ambient_temp'].to_frame())

X_tr.head()
                     ambient_temp  ambient_temp_lag_1  ambient_temp_lag_2  \
2020-05-15 12:00:00         31.31                 NaN                 NaN
2020-05-15 12:15:00         31.51               31.31                 NaN
2020-05-15 12:30:00         32.15               31.51               31.31
2020-05-15 12:45:00         32.39               32.15               31.51
2020-05-15 13:00:00         32.62               32.39               32.15

                     ambient_temp_lag_3
2020-05-15 12:00:00                 NaN
2020-05-15 12:15:00                 NaN
2020-05-15 12:30:00                 NaN
2020-05-15 12:45:00               31.31
2020-05-15 13:00:00               31.51

如果我们不希望返回的数据框中包含时间序列的原始值,我们只需要记住在转换后删除原始序列:

lag_f = LagFeatures(periods=[1, 2, 3], drop_original=True)

X_tr = lag_f.fit_transform(X['ambient_temp'].to_frame())

X_tr.head()
                     ambient_temp_lag_1  ambient_temp_lag_2  \
2020-05-15 12:00:00                 NaN                 NaN
2020-05-15 12:15:00               31.31                 NaN
2020-05-15 12:30:00               31.51               31.31
2020-05-15 12:45:00               32.15               31.51
2020-05-15 13:00:00               32.39               32.15

                     ambient_temp_lag_3
2020-05-15 12:00:00                 NaN
2020-05-15 12:15:00                 NaN
2020-05-15 12:30:00                 NaN
2020-05-15 12:45:00               31.31
2020-05-15 13:00:00               31.51

获取滞后特征的名称#

我们可以通过 get_feature_names_out 方法轻松获取原始和新变量的名称。通过使用默认参数的方法,我们可以获取输出数据框中的所有特征。

lag_f = LagFeatures(periods=[1, 2])

lag_f.fit(X)

lag_f.get_feature_names_out()
['ambient_temp',
 'module_temp',
 'irradiation',
 'color',
 'ambient_temp_lag_1',
 'module_temp_lag_1',
 'irradiation_lag_1',
 'ambient_temp_lag_2',
 'module_temp_lag_2',
 'irradiation_lag_2']

确定合适的滞后#

我们可以通过利用各种滞后项来创建多个滞后特征。但我们如何决定哪个滞后项是好的滞后项呢?

有多种方法可以做到这一点。

我们可以通过使用多个滞后项来创建特征,然后通过特征选择来确定最佳特征。

或者,我们可以通过评估时间序列的自相关或偏自相关来通过时间序列分析确定最佳滞后。

关于如何为预测创建滞后特征的教程,请查看课程 时间序列预测的特征工程。在课程中,我们还展示了如何检测和移除时间序列数据中的异常值,如何使用捕捉季节性和趋势的特征,以及更多内容。

目标变量的滞后 vs 预测变量的滞后#

通常,我们希望预测的只是一个时间序列的值。例如,我们想要预测下个月的销售额。销售额变量是我们的目标变量,我们可以通过滞后过去的销售额值来创建特征。

我们也可以从伴随的预测变量中创建滞后特征。例如,如果我们想预测未来几小时的污染物浓度,我们可以从过去的污染物浓度中创建滞后特征。此外,我们还可以从伴随的时间序列值中创建滞后特征,比如其他气体的浓度,或温度和湿度。

另见#

查看额外的转换器,通过使用滚动窗口(WindowFeatures)或扩展窗口(ExpandingWindowFeatures)来创建窗口特征。

如果你想将 LagFeatures 作为特征工程管道的一部分使用,请查看 Feature-engine 的 Pipeline

教程和课程#

关于此方法及其他时间序列预测的特征工程方法的教程,请查看我们的在线课程:

../../../_images/fetsf.png

时间序列预测的特征工程#

../../../_images/fwml.png

使用机器学习进行预测#











我们的课程适合初学者和希望使用传统机器学习模型(如线性回归或梯度提升机)进行时间序列预测的更高级数据科学家。

通过购买它们,您正在支持 Feature-engine 的主要开发者 Sole。