Если добавить одну секунду в Etime
то можно найти строки, которые будут соединены группирование по ['A', 'B']
, а затем для каждой группы сравнения сдвинуты Etime
с с следующей Stime
:
df['Etime'] += pd.Timedelta(seconds=1)
df = df.sort_values(by=['A', 'B', 'Stime'])
df['keep'] = df.groupby(['A','B'])['Etime'].shift(1) != df['Stime']
# A B C Etime Stime keep
# 0 1220627 a 10.0 2016-05-29 18:10:00 2016-05-29 18:00:00 True
# 1 1220627 a 12.0 2016-05-29 18:27:00 2016-05-29 18:15:00 True
# 3 1220683 a 3.0 2016-05-29 18:39:00 2016-05-29 18:36:00 True
# 2 1220683 b 3.0 2016-05-29 18:39:00 2016-05-29 18:36:00 True
# 4 1220732 a 59.0 2016-05-29 18:59:00 2016-05-29 18:00:00 True
# 5 1220760 A 16.0 2016-05-29 18:40:00 2016-05-29 18:24:00 True
# 7 1220760 A 19.0 2016-05-29 18:59:00 2016-05-29 18:40:00 False
# 12 1220760 a 0.0 2016-05-29 18:10:00 2016-05-29 18:00:00 True
# 6 1220760 a 16.0 2016-05-29 18:40:00 2016-05-29 18:24:00 True
# 9 1220760 a 19.0 2016-05-29 18:59:00 2016-05-29 18:40:00 False
# 11 1220760 a 11.0 2016-05-29 19:10:00 2016-05-29 18:59:00 False
# 8 1220760 b 19.0 2016-05-29 18:59:00 2016-05-29 18:40:00 True
# 10 1220775 a 3.0 2016-05-29 18:06:00 2016-05-29 18:03:00 True
Мы хотим сохранить строки где keep
имеет значение True и удаляет строки, где keep
- False, , за исключением того, что мы также хотим обновить Etime
s, если необходимо.
Было бы хорошо, если бы мы могли назначить «групповой номер» для каждой строки, чтобы мы могли группироваться по ['A', 'B', 'group_number']
- и на самом деле мы можем.Все, что нам нужно сделать, это применить cumsum
к keep
колонок:
df['group_number'] = df.groupby(['A','B'])['keep'].cumsum()
# A B C Etime Stime keep group_number
# 0 1220627 a 10.0 2016-05-29 18:10:00 2016-05-29 18:00:00 True 1.0
# 1 1220627 a 12.0 2016-05-29 18:27:00 2016-05-29 18:15:00 True 2.0
# 3 1220683 a 3.0 2016-05-29 18:39:00 2016-05-29 18:36:00 True 1.0
# 2 1220683 b 3.0 2016-05-29 18:39:00 2016-05-29 18:36:00 True 1.0
# 4 1220732 a 59.0 2016-05-29 18:59:00 2016-05-29 18:00:00 True 1.0
# 5 1220760 A 16.0 2016-05-29 18:40:00 2016-05-29 18:24:00 True 1.0
# 7 1220760 A 19.0 2016-05-29 18:59:00 2016-05-29 18:40:00 False 1.0
# 12 1220760 a 0.0 2016-05-29 18:10:00 2016-05-29 18:00:00 True 1.0
# 6 1220760 a 16.0 2016-05-29 18:40:00 2016-05-29 18:24:00 True 2.0
# 9 1220760 a 19.0 2016-05-29 18:59:00 2016-05-29 18:40:00 False 2.0
# 11 1220760 a 11.0 2016-05-29 19:10:00 2016-05-29 18:59:00 False 2.0
# 8 1220760 b 19.0 2016-05-29 18:59:00 2016-05-29 18:40:00 True 1.0
# 10 1220775 a 3.0 2016-05-29 18:06:00 2016-05-29 18:03:00 True 1.0
Теперь желаемый результат может быть найден путем группирования по ['A', 'B', 'group_number']
, и найти минимальную Stime
и максимальную Etime
для каждой группы:
result = df.groupby(['A','B', 'group_number']).agg({'Stime':'min', 'Etime':'max'})
Stime Etime
A B group_number
1220627 a 1.0 2016-05-29 18:00:00 2016-05-29 18:10:00
2.0 2016-05-29 18:15:00 2016-05-29 18:27:00
1220683 a 1.0 2016-05-29 18:36:00 2016-05-29 18:39:00
b 1.0 2016-05-29 18:36:00 2016-05-29 18:39:00
1220732 a 1.0 2016-05-29 18:00:00 2016-05-29 18:59:00
1220760 A 1.0 2016-05-29 18:24:00 2016-05-29 18:59:00
a 1.0 2016-05-29 18:00:00 2016-05-29 18:10:00
2.0 2016-05-29 18:24:00 2016-05-29 19:10:00
b 1.0 2016-05-29 18:40:00 2016-05-29 18:59:00
1220775 a 1.0 2016-05-29 18:03:00 2016-05-29 18:06:00
Собираем все вместе,
import numpy as np
import pandas as pd
df = pd.DataFrame(
{'A': [1220627, 1220627, 1220683, 1220683, 1220732, 1220760, 1220760,
1220760, 1220760, 1220760, 1220775, 1220760, 1220760],
'B': ['a', 'a', 'b', 'a', 'a', 'A', 'a', 'A', 'b', 'a', 'a', 'a', 'a'],
'C': [10.0, 12.0, 3.0, 3.0, 59.0, 16.0, 16.0, 19.0, 19.0, 19.0, 3.0, 11.0, 0],
'Stime': ['18:00:00', '18:15:00', '18:36:00', '18:36:00', '18:00:00',
'18:24:00', '18:24:00', '18:40:00', '18:40:00', '18:40:00',
'18:03:00', '18:59:00', '18:00:00'],
'Etime': ['18:09:59', '18:26:59', '18:38:59', '18:38:59', '18:58:59',
'18:39:59', '18:39:59', '18:58:59', '18:58:59', '18:58:59',
'18:05:59', '19:09:59', '18:09:59'],})
for col in ['Stime', 'Etime']:
df[col] = pd.to_datetime(df[col])
df['Etime'] += pd.Timedelta(seconds=1)
df = df.sort_values(by=['A', 'B', 'Stime'])
df['keep'] = df.groupby(['A','B'])['Etime'].shift(1) != df['Stime']
df['group_number'] = df.groupby(['A','B'])['keep'].cumsum()
result = df.groupby(['A','B', 'group_number']).agg({'Stime':'min', 'Etime':'max'})
result = result.reset_index()
result['C'] = (result['Etime']-result['Stime']).dt.total_seconds()/60.0
result = result[['A', 'B', 'C', 'Stime', 'Etime']]
print(result)
выходы
A B C Stime Etime
0 1220627 a 10.0 2016-05-29 18:00:00 2016-05-29 18:10:00
1 1220627 a 12.0 2016-05-29 18:15:00 2016-05-29 18:27:00
2 1220683 a 3.0 2016-05-29 18:36:00 2016-05-29 18:39:00
3 1220683 b 3.0 2016-05-29 18:36:00 2016-05-29 18:39:00
4 1220732 a 59.0 2016-05-29 18:00:00 2016-05-29 18:59:00
5 1220760 A 35.0 2016-05-29 18:24:00 2016-05-29 18:59:00
6 1220760 a 10.0 2016-05-29 18:00:00 2016-05-29 18:10:00
7 1220760 a 46.0 2016-05-29 18:24:00 2016-05-29 19:10:00
8 1220760 b 19.0 2016-05-29 18:40:00 2016-05-29 18:59:00
9 1220775 a 3.0 2016-05-29 18:03:00 2016-05-29 18:06:00
Одним из преимуществ использования полуоткрытых интервалов вида [start, end)
вместо того, чтобы полностью замкнутые интервалы [start, end]
в том, что, когда два интервала примыкают, в end
из одного равноstart
следующего.
Другим преимуществом является то, что количество минут в полуоткрытом интервале равно end-start
. При полностью закрытом интервале формула становится end-start+1
.
Встроенный в Python range
и список разрезанных синтаксиса используют полуоткрытые интервалы for these same reasons. Поэтому я порекомендовал бы использовать полуоткрытые интервалы [Stime, Etime)
в вашем DataFrame .
делает случай вопроса письмо в колонке B? это «А» так же, как «а»? – piRSquared
нет, его не то же самое. Дело имеет значение. –