Datetime в Pandas: шпаргалка для собеседования

Зачем аналитику datetime в pandas

Временные ряды — главный формат данных в аналитике. DAU, MoM, cohort retention, скользящие средние — всё работает через datetime. На собесе проверяют знание dt-аксессора и resample.

Преобразование в datetime

import pandas as pd

# Из строки
df['date'] = pd.to_datetime(df['date'])
df['date'] = pd.to_datetime(df['date'], format='%d.%m.%Y')

# Из unix timestamp
df['ts'] = pd.to_datetime(df['ts_seconds'], unit='s')

# С обработкой ошибок
df['date'] = pd.to_datetime(df['date'], errors='coerce')  # ошибки → NaT

На собесе: errors='coerce' — важный параметр, превращает некорректные в NaT вместо исключения.

dt-аксессор

# Компоненты
df['date'].dt.year
df['date'].dt.month
df['date'].dt.day
df['date'].dt.hour
df['date'].dt.dayofweek  # 0=понедельник
df['date'].dt.day_name()  # 'Monday'
df['date'].dt.quarter
df['date'].dt.is_month_end
df['date'].dt.is_weekend  # pandas 2.0+

# Формат
df['date'].dt.strftime('%Y-%m-%d')
df['date'].dt.date          # просто дата (без времени)
df['date'].dt.normalize()   # обнулить время

Сравнение и фильтрация

# За последние 30 дней
df[df['date'] >= pd.Timestamp.now() - pd.Timedelta(days=30)]

# В диапазоне
df[(df['date'] >= '2026-01-01') & (df['date'] < '2026-02-01')]

# По месяцу/году
df[df['date'].dt.year == 2026]
df[df['date'].dt.month == 4]

# Текущая неделя
df[df['date'].dt.isocalendar().week == pd.Timestamp.now().isocalendar().week]

resample — ресэмплинг времени

# Нужен datetime как индекс
df = df.set_index('date')

# Агрегация по дням
df['amount'].resample('D').sum()

# По неделям (с понедельника)
df['amount'].resample('W-MON').sum()

# По месяцам
df['amount'].resample('M').sum()

# Даунсэмплинг с несколькими агрегатами
df.resample('W').agg({'amount': 'sum', 'user_id': 'nunique'})

# Апсэмплинг (из часов в минуты) + forward fill
df.resample('1min').ffill()

Попробовать силы на подобных вопросах проще всего в тренажёре Карьерник — прямо в Telegram, без регистрации через сайт.

rolling — скользящие окна

# Скользящее среднее за 7 дней
df['ma_7d'] = df['amount'].rolling(window=7).mean()

# Скользящая сумма
df['sum_30d'] = df['amount'].rolling(window=30).sum()

# С условием min_periods
df['ma_7d'] = df['amount'].rolling(window=7, min_periods=1).mean()

# По времени (нужен datetime index)
df['ma_24h'] = df.set_index('date')['amount'].rolling('24h').mean()

expanding — нарастающие агрегаты

# Нарастающий итог
df['cumsum'] = df['amount'].cumsum()

# Нарастающее среднее
df['cummean'] = df['amount'].expanding().mean()

Timezone

# Локализовать (добавить timezone)
df['date'] = df['date'].dt.tz_localize('UTC')

# Конвертировать
df['moscow'] = df['date'].dt.tz_convert('Europe/Moscow')

# Убрать timezone
df['date'].dt.tz_localize(None)

Ловушка: tz_localize нельзя применить дважды — сначала localize, потом convert.

Арифметика дат

# Добавить/вычесть
df['date_plus_7'] = df['date'] + pd.Timedelta(days=7)
df['date_minus_month'] = df['date'] - pd.DateOffset(months=1)

# Разница
(df['end'] - df['start']).dt.days
(df['end'] - df['start']).dt.total_seconds() / 3600  # в часах

# Возраст в днях
(pd.Timestamp.now() - df['registered_at']).dt.days

Важно: Timedelta — точный интервал (days=30 = ровно 30×24ч). DateOffset — календарный (months=1 = «через месяц», учитывает разную длину месяцев).

Генерация последовательностей

# Календарь
pd.date_range('2026-01-01', '2026-12-31', freq='D')
pd.date_range('2026-01-01', periods=12, freq='M')

# Рабочие дни
pd.bdate_range('2026-04-01', '2026-04-30')

# Каждую вторую неделю
pd.date_range('2026-01-01', periods=26, freq='2W')

Пройти 30–50 задач по теме за вечер можно в Telegram-тренажёре. Это то, что отличает «знаю» от «уверенно отвечу на собесе».

Когортный анализ

# Месяц регистрации
df['cohort'] = df.groupby('user_id')['date'].transform('min').dt.to_period('M')

# Период активности
df['period'] = df['date'].dt.to_period('M')

# Месяцев с регистрации
df['cohort_idx'] = (df['period'] - df['cohort']).apply(lambda x: x.n)

# Когортная таблица
cohort_table = df.groupby(['cohort', 'cohort_idx'])['user_id'].nunique().unstack()

10 задач с собесов

1. Выручка по месяцам

df.set_index('date')['amount'].resample('M').sum()

2. MoM growth

monthly = df.set_index('date')['amount'].resample('M').sum()
mom = monthly.pct_change() * 100

3. DAU за апрель

april = df[df['date'].dt.month == 4]
april.groupby(april['date'].dt.date)['user_id'].nunique()

4. Скользящее среднее DAU за 7 дней

dau = df.groupby(df['date'].dt.date)['user_id'].nunique()
dau.rolling(7).mean()

5. Доля заказов в выходные

df['is_weekend'] = df['date'].dt.dayofweek >= 5
df['is_weekend'].mean()

6. Возраст пользователя в днях на момент покупки

(df['order_date'] - df['registered_at']).dt.days

7. Конвертация в московское время

df['date_msk'] = df['date'].dt.tz_localize('UTC').dt.tz_convert('Europe/Moscow')

8. Заказы по дням недели

df.groupby(df['date'].dt.day_name())['amount'].sum()

9. Retention D7

df['signup'] = df.groupby('user_id')['date'].transform('min').dt.date
df['days_since'] = (df['date'].dt.date - df['signup']).dt.days

d7_users = df[df['days_since'] == 7]['user_id'].nunique()
cohort = df[df['days_since'] == 0]['user_id'].nunique()
retention_d7 = d7_users / cohort

10. Заполнить пропущенные дни нулями

df.set_index('date').resample('D')['amount'].sum().fillna(0)

Как тренироваться

Datetime в pandas — это не теория, а привычка. Dt-аксессор, resample, rolling — пишите каждый день, и через месяц они будут «в пальцах».

Тренажёр Карьерник содержит задачи на datetime: от dt-аксессора до timezone и когорт. С разборами типичных ловушек.

Совет: если колонка-дата в строковом формате — первое же действие pd.to_datetime. Не пытайтесь работать через str, это всегда заканчивается плохо.

Читайте также

FAQ

Timedelta vs DateOffset — что использовать?

Timedelta — точные интервалы (дни, часы, минуты). DateOffset — календарные (месяц, квартал, год). Для «ровно 30 дней» — Timedelta. Для «через месяц» — DateOffset.

Почему pandas тормозит на datetime-фильтрации?

Если колонка в string — каждое сравнение парсится заново. Превратите через to_datetime один раз в начале — и всё быстро.

to_datetime против astype('datetime64')

Функционально одинаково в современном pandas. to_datetime поддерживает format, errors, unit — безопаснее. astype меньше контроля.

Период (Period) vs Timestamp?

Timestamp — точка во времени (2026-04-15 14:30). Period — интервал (April 2026, 2026-Q1). Period удобен для когорт и resample.