Начните с определения 2 функций:
def cnt(grp):
hr = grp.Hour
return grp.assign(count=hr.size, period_start=hr.iloc[0], period_end=hr.iloc[-1])
def fn(grp):
gr = grp.groupby((grp.Hour - grp.Hour.shift()).gt(pd.Timedelta('1H')).cumsum())
return gr.apply(cnt)
Затем сгруппируйте и примените его:
df.groupby('Site').apply(fn).reset_index(level=[0, 1], drop=True).sort_index()
Вы должны начать чтение кода с конца.
Первый шаг - сгруппировать по Site (первый уровень группировки) и применить fn к каждой группе. В настоящее время пропустите остальную часть этой инструкции.
Затем функция fn выполняет группировку второго уровня. Идея состоит в том, чтобы разделить исходную группу (первый уровень) на группы строк для последовательных часов.
К каждой группе (второго уровня) применяется функция cnt . Его результатом является исходная группа с добавленными столбцами count , period_start и period_end .
И теперь есть время взглянуть на (пропущено) ) часть первой инструкции. Партия groupby (...). Apply (...) дает следующий результат (для краткости я включил только результат для Site == A и B .
Hour Site count period_start period_end
Site Hour
A 0 0 2020-08-01 00:00:00 A 3 2020-08-01 00:00:00 2020-08-01 02:00:00
4 2020-08-01 01:00:00 A 3 2020-08-01 00:00:00 2020-08-01 02:00:00
8 2020-08-01 02:00:00 A 3 2020-08-01 00:00:00 2020-08-01 02:00:00
1 12 2020-08-01 04:00:00 A 2 2020-08-01 04:00:00 2020-08-01 05:00:00
14 2020-08-01 05:00:00 A 2 2020-08-01 04:00:00 2020-08-01 05:00:00
2 15 2020-08-01 08:00:00 A 1 2020-08-01 08:00:00 2020-08-01 08:00:00
B 0 1 2020-08-01 00:00:00 B 2 2020-08-01 00:00:00 2020-08-01 01:00:00
5 2020-08-01 01:00:00 B 2 2020-08-01 00:00:00 2020-08-01 01:00:00
Чтобы получить окончательный результат, необходимо:
- reset_index (...) - удалить первые 2 уровня индекса.
- sort_index () - отсортировать строки по индексу.
Результат такой, как вы ожидали.