подсчет вхождений указанной метки c в скользящем окне - PullRequest
1 голос
/ 06 апреля 2020

У меня есть следующий DataFrame.

         t_msec    ID
0           1.1  0200
1           1.4  020a
2           8.9  01f4
3          11.1  0200
4          13.2  02e2
...         ...   ...
85454  189915.3  02e4
85455  189915.6  02e6
85456  189921.8  0200
85457  189922.3  01f4
85458  189924.0  020a

Я хочу запустить скользящее окно, которое подсчитывает вхождения указанной метки c на 1 секунду вперед.

only_id_df = df[df.ID == id]
counts = Counter()
for index, row in only_id_df.iterrows():
    mask = (only_id_df.t_msec< row.t_msec + 1000) & (only_id_df.t_msec> row.t_msec)
    counts.update([len(only_id_df[mask])])

Однако это очень медленно, и я знаю, что должен быть способ выполнить это без ручной итерации каждой строки и фильтрации (что, как я знаю, неэффективно).

Каков правильный способ достижения этой цели? Как я могу ускорить этот расчет?

1 Ответ

0 голосов
/ 06 апреля 2020

Для моего теста я использовал следующий DataFrame:

   t_msec    ID
0      60  0200
1      70  020a
2     445  01f4
3     555  0200
4     660  02e2
5    1005  0200
6    1510  02e2
7    2105  0200
8    2260  02e2

Так что если мы ищем, например, ID == '0200' , в пределах 1 с от начальная строка ( t_mse c == 60 ) вперед, затем есть строки 3 , на t_mse c == [60, 555, 1005] .

Чтобы вычислить весь результат, мы должны выполнить несколько трюков:

  1. Для выполнения скользящих вычислений на основе временного ряда , мы должны вычислить временный столбец, включающий pd.to_datetime и установить его в качестве индекса.

  2. Но скользящие вычисления выполняются в обратном направлении из текущая строка, то есть Pandas ищет строки между, например, 1 с до текущего индекса и сейчас (текущий индекс) и выполняет определенные вычисления для строк в этом окне, в то время как мы хотим, чтобы окно смотрело forward . Таким образом, здесь необходим трюк:

    • вычислить индекс как df.t_mse c .max () - df.t_mse c [мс] ,
    • обработайте его в обратном порядке,
    • снова измените порядок результата.
  3. Причина последнего трюка заключается в том, что:

    • мы хотим взглянуть на столбец ID ( строка ),
    • , но скользящие вычисления можно выполнять только для цифр c столбцы.
  4. К счастью, столбец ID содержит только строки hex , которые можно преобразовать в INT . Поэтому мы конвертируем его и сохраняем в новом (временном) столбце.

Первым шагом является выполнение вычислений «настройки»:

lbl = int('0200', 16)  # Label to look for (hex -> dec)
# ID converted to dec
df['ID_dec'] = df.ID.apply(lambda x: int(x, 16))
# Set index
df = df.set_index(pd.to_datetime(df.t_msec.max() - df.t_msec, unit='ms'))

, чтобы df содержит теперь:

                         t_msec    ID  ID_dec
t_msec                                       
1970-01-01 00:00:02.200      60  0200     512
1970-01-01 00:00:02.190      70  020a     522
1970-01-01 00:00:01.815     445  01f4     500
1970-01-01 00:00:01.705     555  0200     512
1970-01-01 00:00:01.600     660  02e2     738
1970-01-01 00:00:01.255    1005  0200     512
1970-01-01 00:00:00.750    1510  02e2     738
1970-01-01 00:00:00.155    2105  0200     512
1970-01-01 00:00:00.000    2260  02e2     738

Второй (основной) шаг - вычисление столбца Nr :

df['Nr'] = df.ID_dec[::-1].rolling(window=pd.offsets.Second(1), closed='both')\
    .apply(lambda grp: grp[grp == lbl].size, raw=False).astype(int)[::-1]

Обратите внимание на [ :: - 1] реверсирование исходного столбца и самого результата.

Now df содержит:

                         t_msec    ID  ID_dec  Nr
t_msec                                           
1970-01-01 00:00:02.200      60  0200     512   3
1970-01-01 00:00:02.190      70  020a     522   2
1970-01-01 00:00:01.815     445  01f4     500   2
1970-01-01 00:00:01.705     555  0200     512   2
1970-01-01 00:00:01.600     660  02e2     738   1
1970-01-01 00:00:01.255    1005  0200     512   1
1970-01-01 00:00:00.750    1510  02e2     738   1
1970-01-01 00:00:00.155    2105  0200     512   1
1970-01-01 00:00:00.000    2260  02e2     738   0

И последний шаг - временное удаление столбцы и восстановить исходный индекс:

df = df.drop(columns='ID_dec').reset_index(drop=True)

Окончательный результат:

   t_msec    ID  Nr
0      60  0200   3
1      70  020a   2
2     445  01f4   2
3     555  0200   2
4     660  02e2   1
5    1005  0200   1
6    1510  02e2   1
7    2105  0200   1
8    2260  02e2   0

Время выполнения должно быть значительно короче. Напишите комментарий о времени выполнения вашего кода и моего (+ номер строки).

...