Панды вставляют строку каждый заданный интервал времени

#python #pandas

#python #pandas

Вопрос:

Имея следующий DF:

     id           init_time          event_time  value
0    0 2020-10-21 00:21:25 2020-10-21 00:21:30      1
1    0 2020-10-21 00:21:25 2020-10-21 00:21:35      3
2    0 2020-10-21 00:21:25 2020-10-21 00:23:25      7
3    1 2020-10-21 15:21:25 2020-10-21 15:21:30      3
4    1 2020-10-21 15:21:25 2020-10-21 15:22:25      6
5    1 2020-10-21 15:21:25 2020-10-21 16:21:25      9
6    1 2020-10-21 15:21:25 2020-10-21 18:21:25     10
7    1 2020-10-21 15:21:25 2020-10-21 18:30:25     10
8    2 2020-10-21 08:11:20 2020-10-21 08:21:25      1
9    2 2020-10-21 08:11:20 2020-10-21 09:11:20      2
10   2 2020-10-21 08:11:20 2020-10-21 09:21:25      3
11   2 2020-10-21 08:11:20 2020-10-21 10:12:12      4
12   2 2020-10-21 08:11:20 2020-10-21 10:30:00      5
13   2 2020-10-21 08:11:20 2020-10-21 11:50:48      6
14   2 2020-10-21 08:11:20 2020-10-21 11:59:15      7
 

Я хотел бы сгруппировать id , затем на основе init_time вставлять строку каждые X интервалов и повторять Y раз. Обратите внимание, что init_time это одинаково для всех строк и event_time является временем создания строки.


Например, если X is 1h и Y is 5 раз, результат DF будет:

     id           init_time          event_time  value
0    0 2020-10-21 00:21:25 2020-10-21 00:21:25     -- ⎫
1    0 2020-10-21 00:21:25 2020-10-21 00:21:30      12    0 2020-10-21 00:21:25 2020-10-21 00:21:35      3 ⎬ 1H interval (00:21:25 - 01:21:25)
3    0 2020-10-21 00:21:25 2020-10-21 00:23:25      74    0 2020-10-21 00:21:25 2020-10-21 01:21:25     -- ⎭
5    0 2020-10-21 00:21:25 2020-10-21 02:21:25     --  
6    0 2020-10-21 00:21:25 2020-10-21 03:21:25     --
7    0 2020-10-21 00:21:25 2020-10-21 04:21:25     --

8    1 2020-10-21 15:21:25 2020-10-21 15:21:25     -- ⎫
9    1 2020-10-21 15:21:25 2020-10-21 15:21:30      310   1 2020-10-21 15:21:25 2020-10-21 15:22:25      6 ⎬ 1H interval (15:21:25 - 16:21:25)
11   1 2020-10-21 15:21:25 2020-10-21 16:21:25     -- ⎭
12   1 2020-10-21 15:21:25 2020-10-21 16:21:25      9
13   1 2020-10-21 15:21:25 2020-10-21 17:21:25     --
14   1 2020-10-21 15:21:25 2020-10-21 18:21:25     --
15   1 2020-10-21 15:21:25 2020-10-21 18:21:25     10
16   1 2020-10-21 15:21:25 2020-10-21 18:30:25     10
17   1 2020-10-21 15:21:25 2020-10-21 19:21:25     --

18   2 2020-10-21 08:11:20 2020-10-21 08:11:20     -- ⎫
19   2 2020-10-21 08:11:20 2020-10-21 08:21:25      1 ⎬ 1H interval (08:11:20 - 09:11:20)
20   2 2020-10-21 08:11:20 2020-10-21 09:11:20     -- ⎭
21   2 2020-10-21 08:11:20 2020-10-21 09:11:20      2
22   2 2020-10-21 08:11:20 2020-10-21 09:21:25      3
23   2 2020-10-21 08:11:20 2020-10-21 10:11:20     --
24   2 2020-10-21 08:11:20 2020-10-21 10:12:12      4
25   2 2020-10-21 08:11:20 2020-10-21 10:30:00      5
26   2 2020-10-21 08:11:20 2020-10-21 11:11:20     --
27   2 2020-10-21 08:11:20 2020-10-21 11:50:48      6
28   2 2020-10-21 08:11:20 2020-10-21 11:59:15      7
29   2 2020-10-21 08:11:20 2020-10-21 12:11:20     --
 

Любая помощь будет оценена по достоинству 🙂

Обновление: вот что я сделал до сих пор:

 df2 = pd.DataFrame()
groups = df.groupby('id')['init_time'].first()
for g in groups:
  tmp = pd.DataFrame(index=pd.date_range(g, periods=5, freq="H"))
  tmp['value'] = -1
  df2 = df2.append(tmp,ignore_index=False)
df2 = df2.reset_index(drop=False)
df2['event_time'] = df2.pop('index')
 

Код создает недостающие строки:

     value          event_time
0      -1 2020-10-21 00:21:25
1      -1 2020-10-21 01:21:25
2      -1 2020-10-21 02:21:25
3      -1 2020-10-21 03:21:25
4      -1 2020-10-21 04:21:25
5      -1 2020-10-21 15:21:25
6      -1 2020-10-21 16:21:25
7      -1 2020-10-21 17:21:25
8      -1 2020-10-21 18:21:25
9      -1 2020-10-21 19:21:25
10     -1 2020-10-21 08:11:20
11     -1 2020-10-21 09:11:20
12     -1 2020-10-21 10:11:20
13     -1 2020-10-21 11:11:20
14     -1 2020-10-21 12:11:20
 

Теперь мне нужно выяснить, как объединить DFS, или, может быть, найти лучшую альтернативу

Update2: я хочу оптимизировать решение, предложенное @Serge Ballesta, чтобы увеличить время выполнения.

Комментарии:

1. Вы хотите сделать это в режиме реального времени или после сбора данных?

2. После сбора данных

Ответ №1:

Я не мог найти умный способ. Поэтому я только предлагаю:

  1. получить строку данных для каждого id init_time столбца (предполагается, что она уникальна для идентификатора)
  2. добавьте новый event_time столбец, построенный из a date_range , и объедините все
  3. объедините ее с исходным фреймом данных, отсортируйте и создайте новый индекс

Код может быть (при условии, что фрейм данных есть df , а xxx_time столбцы имеют datetime64[ns] dtypes):

 # extract init_time per id
df1 = df[['id', 'init_time']].drop_duplicates()

# build the new event_time data
df2 = pd.concat(pd.DataFrame({'id': [s[1]['id']] * 5,
                              'init_time': [s[1]['init_time']] * 5,
                              'event_time': pd.date_range(s[1]['init_time'],
                                                          periods=5,
                                                          freq='1H')})
                for s in df1.iterrows())

# concat with the initial dataframe
final = pd.concat((df, df2)).sort_values(['id', 'event_time']).reset_index(
    drop=True)
 

С вашими примерами данных это дает:

     id           init_time          event_time  value
0    0 2020-10-21 00:21:25 2020-10-21 00:21:25    NaN
1    0 2020-10-21 00:21:25 2020-10-21 00:21:30    1.0
2    0 2020-10-21 00:21:25 2020-10-21 00:21:35    3.0
3    0 2020-10-21 00:21:25 2020-10-21 00:23:25    7.0
4    0 2020-10-21 00:21:25 2020-10-21 01:21:25    NaN
5    0 2020-10-21 00:21:25 2020-10-21 02:21:25    NaN
6    0 2020-10-21 00:21:25 2020-10-21 03:21:25    NaN
7    0 2020-10-21 00:21:25 2020-10-21 04:21:25    NaN
8    1 2020-10-21 15:21:25 2020-10-21 15:21:25    NaN
9    1 2020-10-21 15:21:25 2020-10-21 15:21:30    3.0
10   1 2020-10-21 15:21:25 2020-10-21 15:22:25    6.0
11   1 2020-10-21 15:21:25 2020-10-21 16:21:25    9.0
12   1 2020-10-21 15:21:25 2020-10-21 16:21:25    NaN
13   1 2020-10-21 15:21:25 2020-10-21 17:21:25    NaN
14   1 2020-10-21 15:21:25 2020-10-21 18:21:25   10.0
15   1 2020-10-21 15:21:25 2020-10-21 18:21:25    NaN
16   1 2020-10-21 15:21:25 2020-10-21 18:30:25   10.0
17   1 2020-10-21 15:21:25 2020-10-21 19:21:25    NaN
18   2 2020-10-21 08:11:20 2020-10-21 08:11:20    NaN
19   2 2020-10-21 08:11:20 2020-10-21 08:21:25    1.0
20   2 2020-10-21 08:11:20 2020-10-21 09:11:20    2.0
21   2 2020-10-21 08:11:20 2020-10-21 09:11:20    NaN
22   2 2020-10-21 08:11:20 2020-10-21 09:21:25    3.0
23   2 2020-10-21 08:11:20 2020-10-21 10:11:20    NaN
24   2 2020-10-21 08:11:20 2020-10-21 10:12:12    4.0
25   2 2020-10-21 08:11:20 2020-10-21 10:30:00    5.0
26   2 2020-10-21 08:11:20 2020-10-21 11:11:20    NaN
27   2 2020-10-21 08:11:20 2020-10-21 11:50:48    6.0
28   2 2020-10-21 08:11:20 2020-10-21 11:59:15    7.0
29   2 2020-10-21 08:11:20 2020-10-21 12:11:20    NaN
 

Комментарии:

1. Спасибо за ответ, ваш код работает, однако время выполнения очень велико для моего реального набора данных. Как мы можем это улучшить?

2. @ShlomiSchwartz: Неудивительно, что причина, по которой я начал свой ответ, заключалась в том, что я не мог найти умный способ . iterrows известно, что это приводит к простым в реализации решениям, но с низкой производительностью при увеличении размера. groupby и apply также можно попробовать, но они немного сложнее в настройке, и выигрыш должен быть сопоставлен. С этим образцом данных выигрыша нет…

3. У меня нет времени писать ответ прямо сейчас, но я думаю, что если вместо разработки временных интервалов вдоль строк вы превратите их в periods столбцы, а затем melt в фрейм данных, вы можете использовать операции с массивами и избежать iterrows. Например. после создания вы df1 добавляете 5 новые столбцы, в которыхвы добавляете 1 час init_time , затем превращаете такие столбцы в новые строки, используя что-то вроде melt , тогда вам просто нужно немного почистить, но должно быть быстрее.

4. @gionni: я был удивлен этим, но с образцами данных, melt похоже, медленнее, чем iterrows (не тестировалось с большим количеством строк …)