WindowFeatures#

窗口特征在数据科学中常用于使用传统机器学习模型(如线性回归或梯度提升机)来预测时间序列。窗口特征是通过对过去数据的窗口执行数学运算创建的。

例如,前3个月数据的“销售”平均值是一个窗口特征。前3行数据的“收入”最大值是另一个窗口特征。

在时间序列预测中,我们希望预测时间序列的未来值。为此,我们可以通过对时间序列数据的过去值窗口执行数学运算来创建窗口特征。然后,我们将使用这些特征通过任何回归模型来预测时间序列。

使用 pandas 的滚动窗口特征#

窗口特征是变量上窗口操作的结果。滚动窗口操作是对时间序列数据的**滑动分区**的过去值执行聚合的操作。

窗口特征是指在过去的数据显示中,通过计算数学函数(如均值、最小值、最大值等)在一个窗口内创建的特征。

在Python中,我们可以通过使用pandas方法 rolling 来创建窗口特征。例如,通过执行:

X[["var_1", "var_2"].rolling(window=3).agg(["max", "mean"])

通过之前的命令,我们为每个变量 var_1var_2 创建了2个窗口特征,分别是当前行和前2行数据的最大值和平均值。

如果我们想使用这些特征通过传统的机器学习算法进行预测,我们还需要使用 pandas 方法 shift 将窗口向前移动:

X[["var_1", "var_2"].rolling(window=3).agg(["max", "mean"]).shift(period=1)

移位是重要的,以确保我们严格使用过去的数据,相对于我们想要预测的点。

使用 Feature-engine 的滑动窗口特征#

WindowFeatures 可以通过对不同窗口大小下的各种数值变量执行多种数学运算,自动创建并添加窗口特征到数据框中。

因此,WindowFeatures 通过使用历史数据上的窗口自动创建并添加新特征到数据集中。

窗口特性:参数#

要创建窗口特征,我们需要确定一些参数。首先,我们需要确定执行操作的窗口或窗口的大小。例如,我们可以计算变量在3个月或6周内的平均值。

我们还需要确定窗口相对于我们要预测的数据点的位置有多远。例如,我可以取过去3周数据的平均值来预测本周的数据,或者我可以取过去3周数据的平均值来预测未来几周的数据,在窗口特征和预测点之间留下一个窗口的间隔。

WindowFeatures: 内部机制#

WindowFeatures 基于 pandas.rollingpandas.aggregatepandas.shift 工作。通过 pandas.rollingWindowFeatures 确定了操作的窗口大小。通过 pandas.rolling,我们可以用整数、字符串或函数来指定窗口大小。通过 WindowFeatures,此外,我们可以传递一个整数、字符串或函数的列表,以在多个窗口大小上执行计算。

WindowFeatures 使用 pandas.aggregate 在窗口上执行数学运算。因此,您可以使用 pandas 支持的任何操作。有关支持的聚合函数,请参阅滚动窗口 函数

使用 pandas.shiftWindowFeatures 将从前一个窗口得出的值放在我们想要预测的值的位置。因此,如果我们想用过去3周数据的平均值来预测本周,我们应该将值向前移动1周。如果我们想用过去3周的数据来预测下周,我们应该将值向前移动2周。

WindowFeatures 将使用一个代表性的名称将新特性添加到原始数据框中。它还具有 fit()transform() 方法,使其与 Scikit-learn 的 Pipeline 和交叉验证功能兼容。

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

示例#

让我们创建一个时间序列数据集,看看如何使用 WindowFeatures 创建窗口特征。数据框包含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

现在我们将从数值变量中创建窗口特征。通过设置 window=["30min", "60min"],我们分别在30分钟和60分钟的窗口上进行计算。

如果你查看我们的玩具数据框,你会注意到30分钟对应2行数据,60分钟对应4行数据。因此,我们将分别对2行和4行数据进行计算。

functions 中,我们指定了我们希望对这些窗口执行的所有操作。在下面的示例中,我们希望计算这些窗口内数据的均值和标准差,并找出窗口内的最大值。

通过 freq="15min" 我们指示需要将计算向前移动15分钟。换句话说,我们使用的是在预测点之前最多15分钟内定义的窗口中的可用数据。

from feature_engine.timeseries.forecasting import WindowFeatures

win_f = WindowFeatures(
    window=["30min", "60min"], functions=["mean", "max", "std"], freq="15min",
)

X_tr = win_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_window_30min_mean  \
2020-05-15 12:00:00                             NaN
2020-05-15 12:15:00                           31.31
2020-05-15 12:30:00                           31.41
2020-05-15 12:45:00                           31.83
2020-05-15 13:00:00                           32.27

                     ambient_temp_window_30min_max  \
2020-05-15 12:00:00                            NaN
2020-05-15 12:15:00                          31.31
2020-05-15 12:30:00                          31.51
2020-05-15 12:45:00                          32.15
2020-05-15 13:00:00                          32.39

                     ambient_temp_window_30min_std  \
2020-05-15 12:00:00                            NaN
2020-05-15 12:15:00                            NaN
2020-05-15 12:30:00                       0.141421
2020-05-15 12:45:00                       0.452548
2020-05-15 13:00:00                       0.169706

                     module_temp_window_30min_mean  \
2020-05-15 12:00:00                            NaN
2020-05-15 12:15:00                         49.180
2020-05-15 12:30:00                         49.510
2020-05-15 12:45:00                         51.095
2020-05-15 13:00:00                         51.490

                     module_temp_window_30min_max  \
2020-05-15 12:00:00                           NaN
2020-05-15 12:15:00                         49.18
2020-05-15 12:30:00                         49.84
2020-05-15 12:45:00                         52.35
2020-05-15 13:00:00                         52.35

                     module_temp_window_30min_std  ...  \
2020-05-15 12:00:00                           NaN  ...
2020-05-15 12:15:00                           NaN  ...
2020-05-15 12:30:00                      0.466690  ...
2020-05-15 12:45:00                      1.774838  ...
2020-05-15 13:00:00                      1.216224  ...

                     irradiation_window_30min_std  \
2020-05-15 12:00:00                           NaN
2020-05-15 12:15:00                           NaN
2020-05-15 12:30:00                      0.197990
2020-05-15 12:45:00                      0.098995
2020-05-15 13:00:00                      0.077782

                     ambient_temp_window_60min_mean  \
2020-05-15 12:00:00                             NaN
2020-05-15 12:15:00                       31.310000
2020-05-15 12:30:00                       31.410000
2020-05-15 12:45:00                       31.656667
2020-05-15 13:00:00                       31.840000

                     ambient_temp_window_60min_max  \
2020-05-15 12:00:00                            NaN
2020-05-15 12:15:00                          31.31
2020-05-15 12:30:00                          31.51
2020-05-15 12:45:00                          32.15
2020-05-15 13:00:00                          32.39

                     ambient_temp_window_60min_std  \
2020-05-15 12:00:00                            NaN
2020-05-15 12:15:00                            NaN
2020-05-15 12:30:00                       0.141421
2020-05-15 12:45:00                       0.438786
2020-05-15 13:00:00                       0.512640

                     module_temp_window_60min_mean  \
2020-05-15 12:00:00                            NaN
2020-05-15 12:15:00                      49.180000
2020-05-15 12:30:00                      49.510000
2020-05-15 12:45:00                      50.456667
2020-05-15 13:00:00                      50.500000

                     module_temp_window_60min_max  \
2020-05-15 12:00:00                           NaN
2020-05-15 12:15:00                         49.18
2020-05-15 12:30:00                         49.84
2020-05-15 12:45:00                         52.35
2020-05-15 13:00:00                         52.35

                     module_temp_window_60min_std  \
2020-05-15 12:00:00                           NaN
2020-05-15 12:15:00                           NaN
2020-05-15 12:30:00                      0.466690
2020-05-15 12:45:00                      1.672553
2020-05-15 13:00:00                      1.368381

                     irradiation_window_60min_mean  \
2020-05-15 12:00:00                            NaN
2020-05-15 12:15:00                         0.5100
2020-05-15 12:30:00                         0.6500
2020-05-15 12:45:00                         0.6500
2020-05-15 13:00:00                         0.6775

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

                     irradiation_window_60min_std
2020-05-15 12:00:00                           NaN
2020-05-15 12:15:00                           NaN
2020-05-15 12:30:00                      0.197990
2020-05-15 12:45:00                      0.140000
2020-05-15 13:00:00                      0.126853

[5 rows x 22 columns]

用于窗口特征输入的变量存储在 WindowFeaturesvariables_ 属性中。

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

我们可以使用 get_feature_names_out() 方法获取转换后的数据框中的变量名称:

win_f.get_feature_names_out()
['ambient_temp',
 'module_temp',
 'irradiation',
 'color',
 'ambient_temp_window_30min_mean',
 'ambient_temp_window_30min_max',
 'ambient_temp_window_30min_std',
 'module_temp_window_30min_mean',
 'module_temp_window_30min_max',
 'module_temp_window_30min_std',
 'irradiation_window_30min_mean',
 'irradiation_window_30min_max',
 'irradiation_window_30min_std',
 'ambient_temp_window_60min_mean',
 'ambient_temp_window_60min_max',
 'ambient_temp_window_60min_std',
 'module_temp_window_60min_mean',
 'module_temp_window_60min_max',
 'module_temp_window_60min_std',
 'irradiation_window_60min_mean',
 'irradiation_window_60min_max',
 'irradiation_window_60min_std']

删除包含 nan 的行#

当我们创建窗口特征时,可能会为那些过去数据不足以创建窗口的数据点引入nan值。我们可以自动删除在训练集和目标变量中窗口特征包含nan值的行,如下所示:

win_f = WindowFeatures(
    window=["30min", "60min"],
    functions=["mean", ],
    freq="15min",
    drop_na=True,
)

win_f.fit(X)

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

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

我们看到,结果数据框包含的行数少于原始数据框:

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

填充包含 nan 的行#

如果我们不想删除窗口特征中带有 nan 的行,而是想填补这些值,我们可以使用 Feature-engine 的任何填补器来实现。在这里,我们将使用管道中的 ArbitraryNumberImputer 将 nan 替换为任意值 -99:

from feature_engine.imputation import ArbitraryNumberImputer
from feature_engine.pipeline import Pipeline

win_f = WindowFeatures(
    window=["30min", "60min"],
    functions=["mean", ],
    freq="15min",
)

pipe = Pipeline([
    ("windows", win_f),
    ("imputer", ArbitraryNumberImputer(arbitrary_number=-99))
])

X_tr = pipe.fit_transform(X, y)

print(X_tr.head())

我们看到生成的数据框,其中 nan 值被替换为 -99:

                     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_window_30min_mean  \
2020-05-15 12:00:00                          -99.00
2020-05-15 12:15:00                           31.31
2020-05-15 12:30:00                           31.41
2020-05-15 12:45:00                           31.83
2020-05-15 13:00:00                           32.27

                     module_temp_window_30min_mean  \
2020-05-15 12:00:00                        -99.000
2020-05-15 12:15:00                         49.180
2020-05-15 12:30:00                         49.510
2020-05-15 12:45:00                         51.095
2020-05-15 13:00:00                         51.490

                     irradiation_window_30min_mean  \
2020-05-15 12:00:00                        -99.000
2020-05-15 12:15:00                          0.510
2020-05-15 12:30:00                          0.650
2020-05-15 12:45:00                          0.720
2020-05-15 13:00:00                          0.705

                     ambient_temp_window_60min_mean  \
2020-05-15 12:00:00                      -99.000000
2020-05-15 12:15:00                       31.310000
2020-05-15 12:30:00                       31.410000
2020-05-15 12:45:00                       31.656667
2020-05-15 13:00:00                       31.840000

                     module_temp_window_60min_mean  \
2020-05-15 12:00:00                     -99.000000
2020-05-15 12:15:00                      49.180000
2020-05-15 12:30:00                      49.510000
2020-05-15 12:45:00                      50.456667
2020-05-15 13:00:00                      50.500000

                     irradiation_window_60min_mean
2020-05-15 12:00:00                       -99.0000
2020-05-15 12:15:00                         0.5100
2020-05-15 12:30:00                         0.6500
2020-05-15 12:45:00                         0.6500
2020-05-15 13:00:00                         0.6775

使用 pandas 系列#

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

以下是一个 pandas 系列:

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

我们可以使用 WindowFeatures 来创建,例如,通过在一个45分钟的窗口内找到一个pandas Series的平均值和最大值来创建两个新的窗口特征,如果我们使用 to_frame() 方法将其转换为pandas Dataframe的话:

win_f = WindowFeatures(
    window=["45min"],
    functions=["mean", "max"],
    freq="30min",
)

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

X_tr.head()
                     ambient_temp  ambient_temp_window_45min_mean  \
2020-05-15 12:00:00         31.31                             NaN
2020-05-15 12:15:00         31.51                             NaN
2020-05-15 12:30:00         32.15                       31.310000
2020-05-15 12:45:00         32.39                       31.410000
2020-05-15 13:00:00         32.62                       31.656667

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

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

win_f = WindowFeatures(
    window=["45min"],
    functions=["mean", "max"],
    freq="30min",
    drop_original=True,
)

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

X_tr.head()
                     ambient_temp_window_45min_mean  \
2020-05-15 12:00:00                             NaN
2020-05-15 12:15:00                             NaN
2020-05-15 12:30:00                       31.310000
2020-05-15 12:45:00                       31.410000
2020-05-15 13:00:00                       31.656667

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

获取新功能的名称#

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

win_f = WindowFeatures()

win_f.fit(X)

win_f.get_feature_names_out()
['ambient_temp',
 'module_temp',
 'irradiation',
 'color',
 'ambient_temp_window_3_mean',
 'module_temp_window_3_mean',
 'irradiation_window_3_mean']

目标变量的窗口 vs 预测变量的窗口#

通常,我们处理的是单变量时间序列,例如,零售公司的总销售收入。我们希望预测未来的销售值。销售变量是我们的目标变量,我们可以从过去的销售值窗口中提取特征。

我们也可以处理多元时间序列,其中我们有不同国家的销售数据,或者,多个时间序列,如空气中的污染物浓度,伴随着其他气体的浓度、温度和湿度。

在处理多变量时间序列时,例如多个国家的销售数据,我们会从每个国家的过去数据窗口中分别提取特征。

在处理多个时间序列时,如污染物浓度、气体浓度、温度和湿度,污染物浓度是我们的目标变量,而其他时间序列是伴随的预测变量。我们可以从过去的污染物浓度中创建窗口特征,即从目标变量的过去时间步中创建。此外,我们还可以从伴随时间序列的过去数据窗口中创建特征,如其他气体的浓度或温度或湿度。

从时间序列数据中提取特征,以创建一个包含预测变量和目标变量的表格,用于使用线性回归或随机森林等监督学习模型进行预测的过程,被称为“表格化”时间序列。

另见#

查看额外的转换器以创建扩展窗口特征(ExpandingWindowFeatures)或滞后特征,通过滞后时间序列数据的过去值(LagFeatures)。

其他用于窗口功能的开源包#

  • tsfresh 是一个用于时间序列特征提取和选择的Python库。

  • featuretools 是一个用于时间序列分析的工具。

教程和课程#

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

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

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

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

使用机器学习进行预测#











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

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