Векторизованный подход к сравнению последовательных столбцов в фрейме данных и последующему обновлению столбца в этом фрейме данных

#python #pandas #dataframe #lambda

Вопрос:

У меня есть большой фрейм данных с именованными объектами, которые можно сгруппировать по идентификатору события. Каждая группа состоит из одного или нескольких событий. Механизм обнаружения событий часто запускает новую группу до завершения текущей серии, что приводит к большому количеству коротких событий, в которых действительно должно быть меньшее количество более длинных событий.

Я могу определить группы, которые принадлежат друг другу по временному интервалу между событиями — если последнее событие в одной группе происходит в течение N секунд после первого события в следующей группе, то группы должны быть объединены.

В псевдокоде я хочу:

 for all rows in dataframe:
    if row['time_interval'] is less than N
    and if row['ID'] is not equal to next_row['ID']
    then where row['ID'] is the first row's value set it to the second row's value
 

Мои данные выглядят так:

TimeUTC Имя ID time_interval_seconds
2021-06-01 08:58:47 00:00 0M63HB200SY101 9c3807ce-bf21-4cd8-b4ac-f2da440340dc
2021-06-01 09:00:11 00:00 0M63HB200SY101 16e9ea6c-2722-4881-bd35-5867e83be19b 4
2021-06-01 09:00:21 00:00 0M63HB200SY101 16e9ea6c-2722-4881-bd35-5867e83be19b 10
2021-06-01 09:00:24 00:00 0M63HB200SY101 16e9ea6c-2722-4881-bd35-5867e83be19b 3
2021-06-01 09:04:25 00:00 0M63HB200SY101 204fb08c-7271-4399-b111-81488f5f26ec 152
2021-06-01 09:04:35 00:00 0M63HB200SY101 204fb08c-7271-4399-b111-81488f5f26ec 10
2021-06-01 09:05:23 00:00 0M63HB200SY101 204fb08c-7271-4399-b111-81488f5f26ec 48
2021-06-01 09:06:30 00:00 0M63HB200SY101 4bfedd19-b081-467a-8441-bebaef05520c 6
2021-06-01 09:07:04 00:00 0M63HB200SY101 4bfedd19-b081-467a-8441-bebaef05520c 34

Так как значение time_interval во второй строке равно

Даже если идентификатор изменится между строками 4 и 5, мы не будем обновлять идентификатор, так как значение time_interval равно > N.

Однако все идентификаторы во втором блоке будут изменены на идентификаторы из третьего блока.

Такое ощущение, что я могу обнаружить изменение с помощью этой лямбда-функции:

     if r2.time_interval_seconds < 5:
        if r1['ID'] != r2['ID']:
            return(r2['ID'])

   df['newID'] = df[df.apply(lambda x: row_compare(x, x's iloc 1)
 

Но а) я не могу понять, как передать обе строки (.shift()?) и б) получить доступ к исходному кадру данных для его обновления.

Если бы для данной строки я мог передать исходный кадр данных в лямбда-функцию, то я мог бы присоединиться к двум группам внутри функции.

У меня есть миллионы этих строк, и перебирать их на самом деле не вариант, хотя это было бы прямолинейно.

Ответ №1:

Способ сделать это без применения, но все же с векторизованным вычислением:

Я предполагаю, что df сортируется по TimeUTC . Вы создаете новый столбец newID , содержащий только значение для начала каждой группы. Группа начинается, когда time_interval_seconds >= N

 df['newID'] = np.where(
                  time_interval_seconds >= N,
                  df['ID'], np.nan
              )
 

Вы инициализируете первое значение столбца :

 df['newID'].iloc[0] = df['ID'].iloc[0]
 

А затем вы пересылаете его, чтобы заполнить колонку

 df['newId'] = df['newId'].ffill()
 

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

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

2. Я вижу, как это работает, и думаю, что это должно сработать. Я пойду, покатаюсь на нем. Спасибо.