Эффективное построение набора данных скользящего временного ряда - PullRequest
1 голос
/ 23 сентября 2019

У меня есть 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)

Есть23 400 строк в исходном DataFrame и 137 функций, поэтому этот метод неэффективен и непригоден для использования.Так как это подается в Scikit-Learn, окончательные данные должны быть массивом (как показано ниже).Я вполне уверен, что было бы быстрее выполнять эти манипуляции в numpy вместо панд, однако во всех найденных мною примерах используется функция сдвига панд.

Как эффективно построить этот набор данных изоригинальный 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.]])

NB В конечном итоге я планирую отрезать первые 900 и последние 900 строк массива, так что любой результат, которыйне включает их будет работать.

1 Ответ

4 голосов
/ 23 сентября 2019

Мы можем использовать np.lib.stride_tricks.as_strided на основе scikit-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]

Дополнительная информация по использованию as_strided на основе view_as_windows.

Запуски Samnple -

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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...