滚动时间序列数据集的高效构建

时间:2019-09-23 19:13:43

标签: python pandas numpy dataframe

我有一个DataFrame,它以第二个间隔表示特征值的时间序列

>>> df
   FeatA   FeatB   FeatC
0      1       6      11
1      2       7      12
2      3       8      13
3      4       9      14
4      5      10      15

我想用它来为Scikit-Learn模型构建训练数据集。我想在每行中以以下格式添加前15分钟(900行)的特征值

>>> df
   FeatA  FeatB  FeatC  FeatA_T1  FeatB_T1  FeatC_T1  FeatA_T2  FeatB_T2 ...
0      1      6     11       NaN       NaN       NaN       NaN       NaN
1      2      7     12         1         6        11       NaN       NaN
2      3      8     13         2         7        12         1         6
3      4      9     14         3         8        13         2         7
4      5     10     15         4         9        14         3         8

当前我使用的代码本质上是

for i in range(1, 900):
    for feature in ["FeatA", "FeatB", "FeatC"]:
        df[f"{feature}_T{i}"] = df[feature].shift(i)

原始DataFrame中有23400行,并且有137个功能,因此该方法效率低下且无法使用。由于这将被馈送到Scikit-Learn,因此最终数据必须是一个numpy数组(如下所示)。我可以肯定的是,用numpy代替pandas进行这些操作会更快,但是我发现的所有示例都使用了pandas shift函数。

如何从原始DataFrame高效地构建此数据集?是numpy数组函数吗?

预期结果

array([[ 1.,  6., 11., nan, nan, nan, nan, nan, nan],
       [ 2.,  7., 12.,  1.,  6., 11., nan, nan, nan],
       [ 3.,  8., 13.,  2.,  7., 12.,  1.,  6., 11.],
       [ 4.,  9., 14.,  3.,  8., 13.,  2.,  7., 12.],
       [ 5., 10., 15.,  4.,  9., 14.,  3.,  8., 13.]])

最终,我计划分割数组的前900行和最后900行,这样不包含它们的任何结果都将起作用。

1 个答案:

答案 0 :(得分:4)

我们可以利用基于np.lib.stride_tricks.as_stridedscikit-image's view_as_windows来将滑动窗口视图获取到输入的NaNs填充版本中,成为一个视图将在内存和性能上有效。

实现看起来像这样-

from skimage.util.shape import view_as_windows

def sliding_windows_grouped(a, W, fillval = np.nan):
    # W : Number of rows to be grouped for each row in output array
    m,n = a.shape
    ext = np.full((W-1)*n, fillval)
    a_ext = np.concatenate((a[::-1].ravel(),ext))
    return view_as_windows(a_ext,n*W,step=n)[::-1]

More info on use of as_strided based view_as_windows

Samanple运行-

In [63]: a
Out[63]: 
array([[55, 58],
       [75, 78],
       [78, 20],
       [94, 32],
       [47, 98]])

In [64]: sliding_windows_grouped(a, W=2)
Out[64]: 
array([[55., 58., nan, nan],
       [75., 78., 55., 58.],
       [78., 20., 75., 78.],
       [94., 32., 78., 20.],
       [47., 98., 94., 32.]])

In [65]: sliding_windows_grouped(a, W=3)
Out[65]: 
array([[55., 58., nan, nan, nan, nan],
       [75., 78., 55., 58., nan, nan],
       [78., 20., 75., 78., 55., 58.],
       [94., 32., 78., 20., 75., 78.],
       [47., 98., 94., 32., 78., 20.]])

In [66]: sliding_windows_grouped(a, W=4)
Out[66]: 
array([[55., 58., nan, nan, nan, nan, nan, nan],
       [75., 78., 55., 58., nan, nan, nan, nan],
       [78., 20., 75., 78., 55., 58., nan, nan],
       [94., 32., 78., 20., 75., 78., 55., 58.],
       [47., 98., 94., 32., 78., 20., 75., 78.]])

边际改进

我们可以使用np.lib.stride_tricks.as_strided跳过先前提出的解决方案的最后一步,就像这样-

def sliding_windows_grouped_v2(a, W, fillval = np.nan):
    # W : Number of rows to be grouped for each row in output array
    m,n = a.shape
    ext = np.full((W-1)*n, fillval)
    a_ext = np.concatenate((a[::-1].ravel(),ext))
    strided = np.lib.stride_tricks.as_strided
    s = a_ext.strides[0]
    return strided(a_ext[(m-1)*n:],strides=(-n*s,s), shape=(m,W*n))