Вход
Date event
2023-04-11 13:42:16 play
2023-04-11 14:02:26 play
2023-04-11 14:36:09 play
2023-04-11 14:37:46 start
2023-04-11 14:41:34 start
2023-04-11 14:46:27 start
2023-04-11 14:47:03 start
Ожидается это в фрейме данных pandas. Сгруппировать по порядку событий по дате и разнице между первой и последней разницей времени.
выход
date event diff
2023-04-11 play 00:53:52
2023-04-11 start 00:09:17
Давайте сделаем groupby
и agg
с np.ptp т.е. (макс. - мин.)
df['Date'] = pd.to_datetime(df['Date'])
df.groupby(['event', df['Date'].dt.date])['Date'].agg(np.ptp).reset_index(name='diff')
Результат
event Date diff
0 play 2023-04-11 0 days 00:53:53
1 start 2023-04-11 0 days 00:09:17
Используйте собственный groupby.agg:
df['Date'] = pd.to_datetime(df['Date'])
out = (
df.groupby([df['Date'].dt.normalize(), 'event'])
.agg(diff=('Date', lambda g: g.iloc[-1]-g.iloc[0]))
.reset_index()
)
Или повторно используйте группировщик:
g = df.groupby([df['Date'].dt.normalize(), 'event'])['Date']
out = g.last().sub(g.first()).reset_index(name='diff')
Выход:
Date event diff
0 2023-04-11 play 0 days 00:53:53
1 2023-04-11 start 0 days 00:09:17
Если данные изначально не отсортированы, используйте min
/max
в качестве агрегации:
out = (
df.groupby([df['Date'].dt.normalize(), 'event'])
.agg(diff=('Date', lambda g: g.max()-g.min()))
.reset_index()
)
Или:
g = df.groupby([df['Date'].dt.normalize(), 'event'])['Date']
out = g.max().sub(g.min()).reset_index(name='diff')
Это семантически идентично использованию np.ptp
, но по какой-то причине (и, к сожалению), намного эффективнее. Здесь время на 700 тыс. строк:
# max - min
99.7 ms ± 2.33 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# np.ptp
355 ms ± 43.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
@filpa да (похоже, здесь так и есть). Если нет, либо сначала sort_values
, либо используйте min
/max
вместо iloc
. Если данные уже отсортированы, это может быть более эффективно;)
@filpa Я провел несколько тестов, и производительность между min
/max
и first
/last
на самом деле похожа, однако оба они намного быстрее, чем np.ptp
(я хотел бы знать, почему, я представляю детали его реализации).
@ rose1110 были ли проблемы с решением?
df1.assign(date=df1.Date.dt.date).groupby('event',as_index=False)\
.agg(date=("date",'first'),diff=("Date",lambda ss:str(ss.iat[-1]-ss.iat[0])[6:]))
Результат
event Date diff
0 play 2023-04-11 0 days 00:53:53
1 start 2023-04-11 0 days 00:09:17
Это предполагает, что значения упорядочены от самого раннего к последнему, верно?