#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)