КПКейсПрактика
Все материалы
Продуктовая аналитикагайдстарт

Даты и время в pandas: периоды, недели и когорты без ошибок

Как работать с датами в pandas: преобразовать строки, задать границы периода, группировать по неделям и не потерять события из-за часового пояса.

КПКейсПрактика1 августа 2026 г.11 мин
Два аналитика раскладывают события на временной шкале и проверяют границы периода в pandas.

Строка “2026-08-01 00:30” выглядит как дата, пока не выясняется, в какой таймзоне она записана и входит ли в отчёт за июль. В pandas ошибки времени обычно не ломают код: запрос просто считает не тот период. Поэтому даты нужно привести к типу, явно задать границы и проверить результат на нескольких строках.

Коротко

Для аналитического периода используй левую включённую и правую исключённую границу: `>= start` и `< next_start`. Строки превращай в datetime через `pd.to_datetime`, недели и месяцы группируй после приведения к единой таймзоне, а неполный текущий период не сравнивай с полным прошлым.

  • Сначала проверь dtype и диапазон дат.
  • Не смешивай naive datetime и timezone-aware datetime.
  • Для недель зафиксируй, с какого дня начинается неделя.
  • Возраст когорты считай относительно даты старта пользователя, а не календарной даты отчёта.

Преобразовать строки в datetime

Не полагайся на автоматическое угадывание формата, если источник нестабилен. `errors="coerce"` помогает найти нераспарсенные значения через `isna`, но не должен незаметно превращать ошибки в пропуски. После преобразования проверь минимум, максимум и число новых NaT.

pythonРазобрать дату и найти невалидные строки
orders['created_at'] = pd.to_datetime(
    orders['created_at'],
    errors='coerce',
    utc=True,
)

invalid_dates = orders['created_at'].isna().sum()
print('invalid dates:', invalid_dates)
print(orders['created_at'].min(), orders['created_at'].max())

Период: месяц, неделя и день

Период лучше задавать границей следующего периода. Так июль включает все события до 1 августа, включая последний день с любым временем. `dt.to_period` удобен для подписи и группировки, а `date_range` или явные границы лучше подходят для фильтров.

pythonПолный месяц и календарная неделя
start = pd.Timestamp('2026-07-01', tz='UTC')
next_start = pd.Timestamp('2026-08-01', tz='UTC')
july = orders.loc[
    (orders['created_at'] >= start)
    & (orders['created_at'] < next_start)
].copy()

july['week'] = july['created_at'].dt.to_period('W-MON').astype(str)
weekly = july.groupby('week', as_index=False).agg(
    orders=('order_id', 'nunique'),
)

Когорты и возраст пользователя

Для retention важен не только день события, но и расстояние от регистрации. Пользователь, который сделал действие через семь дней после signup, относится к D7 независимо от календарного месяца. В pandas для этого удобно привести обе даты к началу дня и посчитать разницу в днях.

pythonРассчитать возраст события относительно регистрации
events['event_date'] = events['occurred_at'].dt.floor('D')
users['signup_date'] = users['signup_at'].dt.floor('D')
events = events.merge(users[['user_id', 'signup_date']], on='user_id', how='left')
events['day_age'] = (events['event_date'] - events['signup_date']).dt.days

d7 = events.loc[events['day_age'].eq(7)]
d7_users = d7['user_id'].nunique()

Часовой пояс — часть определения метрики

Если продукт работает в нескольких странах, “день” может означать UTC, локальный день пользователя или бизнесовую таймзону компании. Для событий около полуночи эти варианты дадут разные DAU и выручку. Выбери правило до расчёта и запиши его в названии или описании отчёта.

  • Храни исходное время с timezone, если источник его передаёт.
  • Не локализуй naive datetime, пока не знаешь, в какой зоне он записан.
  • Проверь несколько событий около полуночи после конвертации.
  • Сравни период с SQL-версией расчёта на контрольной выборке.
Продолжить чтение
Вся библиотека