Условие
Дан минутный лог запросов requests(ts, user_id, status). Нужно получить:
- Число запросов и долю успешных по часам.
- Скользящее среднее по 24 часам.
- Перевести часовой ряд в дневной с агрегацией.
Решение
Подход
resample работает только если индекс — DatetimeIndex (или передан on= с datetime-колонкой). Алиасы: 'H' (час), 'D' (день), 'W-MON' (неделя с понедельника), 'M' (конец месяца), 'MS' (начало).
Реализация
import pandas as pd
df = df.set_index('ts').sort_index()
# Час
hourly = df.resample('H').agg(
n_requests=('user_id', 'size'),
n_success=('status', lambda s: (s == 200).sum()),
)
hourly['success_rate'] = hourly['n_success'] / hourly['n_requests']
# Скользящее среднее по 24 часам (24 точки)
hourly['ma_24h'] = hourly['n_requests'].rolling(window=24, min_periods=1).mean()
# Перевод в дневной
daily = hourly['n_requests'].resample('D').sum()Обработка дыр
# Если в каком-то часу 0 запросов — resample не создаст строку?
# Создаст: resample заполняет полную сетку. Но если у вас НЕТ ts в этом часе,
# resample('H').size() даст 0, а не NaN. С агрегацией mean — NaN.
hourly = hourly.fillna({'n_requests': 0, 'n_success': 0})
hourly['success_rate'] = hourly['success_rate'].fillna(0)Подводные камни
- Таймзоны:
resample('D')на UTC-индексе даст UTC-сутки. Для «московских суток» сначалаtz_convert('Europe/Moscow'), потомresample. 'M'— конец месяца,'MS'— начало. Путаница ломает join по месяцу.rolling(24)безmin_periodsоставит NaN на первых 23 точках — для plot лучшеmin_periods=1.resample('W')агрегирует по неделям, кончающимся в воскресенье — для понедельника'W-MON'.- На очень редком ряду
resampleсоздаст много пустых периодов — для разреженных событий используйтеgroupby(pd.Grouper(freq=...))с фильтром.
Эталонный ответ
df.set_index('ts').resample('H').agg(...) + .rolling(24).mean() для гладких графиков; следить за таймзоной и алиасами M/MS/W-MON.