Как работать с time series в pandas
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это нужно
Любой продуктовый аналитик работает с временными рядами: ежедневные DAU, monthly revenue, weekly cohort retention. Базовые операции — resample в разные частоты, moving average для сглаживания, shift для сравнения с прошлым периодом — нужно делать быстро и правильно.
На собеседовании для middle-аналитика вопросы по time series в pandas обязательны: «как посчитать 7-day moving average», «как сравнить с предыдущим периодом», «как выделить сезонность». Без этих знаний дашборды вы не построите.
В статье:
- datetime index и почему он важен
- resample — изменение частоты
- rolling — скользящие окна
- shift — сравнение с прошлым
- Декомпозиция тренда и сезонности
1. Установить datetime index
import pandas as pd
df['date'] = pd.to_datetime(df['date'])
df = df.set_index('date').sort_index()С datetime index все time series операции работают.
2. Resample — изменить частоту
Daily → weekly
weekly = df['revenue'].resample('W').sum()Частоты: D, W, M, Q, Y, H, 15min.
Upsample (daily → hourly)
hourly = df.resample('H').ffill() # forward-fill пропуски3. Rolling window (moving average)
# 7-day moving average
df['revenue_ma7'] = df['revenue'].rolling(7).mean()
# 30-day
df['revenue_ma30'] = df['revenue'].rolling(30).mean()
# sum
df['revenue_sum7'] = df['revenue'].rolling(7).sum()По времени (нужен datetime index):
df['ma_7d'] = df['revenue'].rolling('7D').mean() # ровно 7 дней4. Shift — сравнение с прошлым
# значение день назад
df['revenue_prev_day'] = df['revenue'].shift(1)
# DoD change
df['dod_change'] = df['revenue'] - df['revenue'].shift(1)
df['dod_pct'] = df['revenue'].pct_change()
# неделю назад
df['revenue_prev_week'] = df['revenue'].shift(7)
df['wow_pct'] = df['revenue'].pct_change(7)
# месяц назад
df['revenue_prev_month'] = df['revenue'].shift(30)
df['mom_pct'] = df['revenue'].pct_change(30)
# год назад (YoY)
df['yoy_pct'] = df['revenue'].pct_change(365)5. Cumulative sum
# running total
df['revenue_cumsum'] = df['revenue'].cumsum()
# YTD (Year-to-date)
df['revenue_ytd'] = df.groupby(df.index.year)['revenue'].cumsum()6. Date range и reindex
Заполнить пропущенные даты:
full_range = pd.date_range(df.index.min(), df.index.max(), freq='D')
df = df.reindex(full_range)
df['revenue'] = df['revenue'].fillna(0) # дни без продаж = 07. Фильтр по дате
# последние 30 дней
df.last('30D')
# первые 7 дней
df.first('7D')
# по диапазону
df.loc['2026-01':'2026-04']8. Выделить сезонность
from statsmodels.tsa.seasonal import seasonal_decompose
result = seasonal_decompose(df['revenue'], period=7) # weekly seasonality
result.plot()Выдаст 4 графика: original, trend, seasonal, residual.
9. Forecasting (простейшее)
# naive (вчера = сегодня)
df['forecast'] = df['revenue'].shift(1)
# moving average
df['forecast'] = df['revenue'].rolling(7).mean().shift(1)
# exponential smoothing
df['forecast'] = df['revenue'].ewm(alpha=0.3).mean().shift(1)10. Обработка пропусков
# forward fill
df['revenue'].ffill()
# backward fill
df['revenue'].bfill()
# interpolation
df['revenue'].interpolate()
# линейная
df['revenue'].interpolate(method='time')11. Агрегация по частям времени
df['year'] = df.index.year
df['month'] = df.index.month
df['day_of_week'] = df.index.day_name()
df['hour'] = df.index.hour
# revenue по дням недели
df.groupby('day_of_week')['revenue'].mean()12. Correlation between time series
# корреляция между двумя series
df['revenue'].rolling(30).corr(df['ad_spend'])Частые ошибки
1. Не sort_index
Rolling / shift работают по порядку. Без sort — мусор.
2. Shift в groupby
# неправильно
df['prev'] = df.groupby('user_id')['revenue'].shift(1)
# — работает, но если не отсортировать по дате → неверный prevПравильно:
df = df.sort_values(['user_id', 'date'])
df['prev'] = df.groupby('user_id')['revenue'].shift(1)3. Resample на нечисловой колонке
Нужна числовая для агрегации. Для текста — first() / last() / mode().
4. Таймзоны
Без явного tz-localize операции могут сдвигаться на ±3 часа.
Связанные темы
- Как обработать даты в pandas
- datetime pandas шпаргалка
- Cross-sectional vs longitudinal
- ARIMA прогнозирование
FAQ
resample vs groupby?
Resample требует datetime index — удобнее. groupby универсальнее.
shift vs LAG в SQL?
Эквивалент. shift(1) в pandas = LAG(1) в SQL.
Как rolling с custom window?
df.rolling('7D', on='date').mean() с explicit колонкой.
Моделирование seasonality?
Для advance — SARIMA, Prophet, tbats.
Тренируйте pandas — откройте тренажёр с 1500+ вопросами для собесов.