Добавить строку, когда условия из нескольких столбцов совпадают в Python?

#python #pandas

#python #pandas

Вопрос:

Я пытаюсь добавить строку в фрейм данных. Условие заключается в том, что когда пользователь возвращается (через 300 секунд) в приложение снова, мне нужно добавить строку. Ниже приведен мой код. Это работает нормально, но требует много времени выполнения, так как реальный фрейм данных содержит 10 миллионов строк.

 for i in range(1,len(df)):
    if df['user_id'][i]==df['user_id'][i-1] and (df['start_time'][i]-df['start_time'][i-1]).seconds>300:
        df.loc[len(df)]=[df['user_id'][i],df['start_time'][i],'psuedo_App_start_2']
  

Ввод:

 user_id   start_time        event
100       03/04/19 6:11     psuedo_App_start
100       03/04/19 6:11     notification_receive
100       03/04/19 8:56     notification_dismiss
10        03/04/19 22:05    psuedo_App_start
10        03/04/19 22:05    subcategory_click
10        03/04/19 22:06    subcategory_click
  

вывод должен выглядеть так:

 user_id   start_time        event
100       03/04/19 6:11     psuedo_App_start
100       03/04/19 6:11     notification_receive
100       03/04/19 8:56     psuedo_App_start_2
100       03/04/19 8:56     notification_dismiss
10        03/04/19 22:05    psuedo_App_start
10        03/04/19 22:05    subcategory_click
10        03/04/19 22:06    subcategory_click
  

Как видно из выходных данных, добавлена строка для user_id = 100, поскольку он вернулся в 8.56, т. е. через 300 секунд.

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

1. Есть ли у вас контроль над тем, как эти события вставляются в фрейм данных?

2. нет, автоматически генерируются другие события

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

4. Не могу проверить это прямо сейчас, но вы могли бы groupby['user_id','start_time'] , затем используйте df.timedelta, чтобы проверить, больше ли start_time для каждого идентификатора 300, и вставьте новую строку, если условие выполнено (с последним start_time и user_id извлеченным из df)

Ответ №1:

Сначала отфильтруйте по 2 условиям — сравните user_id по DataFrameGroupBy.shift отредактированным значениям для групп, а также разницу для групп по DataFrameGroupBy.diff , затем переназначьте evet столбец по DataFrame.assign , последнему concat вместе и сортируйте по DataFrame.sort_values :

 #MM/DD/YY HH:MM
#df['start_time'] = pd.to_datetime(df['start_time'])
#DD/MM/YY HH:MM
#df['start_time'] = pd.to_datetime(df['start_time'], dayfirst=True)

m1 = df['user_id'].eq(df.groupby('user_id')['user_id'].shift())
m2 = df.groupby('user_id')['start_time'].diff().dt.total_seconds() > 300

df1 = df[m1 amp; m2].assign(event='psuedo_App_start_2')

df1 = (pd.concat([df, df1], ignore_index=True)
         .sort_values(['user_id','start_time'], ascending=[False, True]))
print (df1)
   user_id          start_time                 event
0      100 2019-03-04 06:11:00      psuedo_App_start
1      100 2019-03-04 06:11:00  notification_receive
2      100 2019-03-04 08:56:00  notification_dismiss
6      100 2019-03-04 08:56:00    psuedo_App_start_2
3       10 2019-03-04 22:05:00      psuedo_App_start
4       10 2019-03-04 22:05:00     subcategory_click
5       10 2019-03-04 22:06:00     subcategory_click
  

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

1. можете ли вы помочь мне понять, что хранится в m1 и m2?

2. @nk23 Существуют логические маски, сначала сравниваемые с помощью eq для ==, затем сравниваемые с помощью >

Ответ №2:

Обычно в таких случаях вам нужно преобразовать явные циклы в векторизованные операции. Попробуйте что-то вроде этого:

 i = (df.user_id.values[1:] == df.user_id.values[:-1]) amp; ((df.start_time.values[1:] - df.start_time.values[:-1])/np.timedelta64(1, 's') > 300)
newRows = tt[np.append(False, i)].copy()
newRows.event = 'psuedo_App_start_2'
df.append(newRows)