Условие
В файле data_2.csv — данные о числе активных игроков за период 2016–2017:
DAY_ID— дата;DAU— Daily Active Users.
Постройте прогноз DAU на 3 месяца 2018 года (январь — март). Обоснуйте методику и подход.
Решение
Подход
DAU live-сервиса имеет несколько типичных компонент:
- Тренд — медленное снижение/рост.
- Недельная сезонность — выходные обычно выше будней.
- Годовая сезонность — лето просадка, зима подъём (для PC-онлайн-игр).
- События / релизы — резкие пики и провалы (новогодний BP, релиз патча).
- Внешние шоки — новости, конкуренты, аутаджи.
Для прогноза на 90 дней с горизонтом 2 года истории подходят:
- Prophet — лучший из коробки для бизнес-данных с выраженной сезонностью.
- SARIMA — классический, требует careful diagnostics.
- Декомпозиция STL + ARIMA на residuals — гибкий вариант.
- Ensemble (Prophet + SARIMA, медиана прогнозов) — снижает дисперсию.
Реализация
EDA
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import STL
df = pd.read_csv("data_2.csv", parse_dates=["DAY_ID"]).rename(
columns={"DAY_ID": "ds", "DAU": "y"})
df = df.sort_values("ds").reset_index(drop=True)
# Базовая визуализация
fig, ax = plt.subplots(figsize=(14, 4))
ax.plot(df["ds"], df["y"])
plt.title("DAU 2016-2017")
# Декомпозиция STL (period=7 для недельной)
res_week = STL(df.set_index("ds")["y"], period=7).fit()
res_year = STL(df.set_index("ds")["y"], period=365).fit()Из EDA узнаём:
- Размер недельной сезонности (часто ±10–15% от среднего).
- Размер годовой сезонности (часто ±20%).
- Наличие пиков (например, новогодний период).
- Тренд (растущий / падающий / стабильный).
Подход 1. Prophet с праздниками и оверрайдами
from prophet import Prophet
m = Prophet(
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False,
changepoint_prior_scale=0.05,
)
# Праздники РФ — частая причина пиков
holidays_ru = pd.DataFrame({
"holiday": "ru_newyear",
"ds": pd.to_datetime(["2017-01-01", "2017-01-02", "2018-01-01", "2018-01-02"]),
"lower_window": 0, "upper_window": 7
})
m.holidays = holidays_ru
m.fit(df)
future = m.make_future_dataframe(periods=90, freq="D")
fcst = m.predict(future)
# Картинка
fig = m.plot(fcst); fig = m.plot_components(fcst)Подход 2. SARIMA как baseline
from statsmodels.tsa.statespace.sarimax import SARIMAX
m = SARIMAX(df.set_index("ds")["y"],
order=(1, 1, 1), seasonal_order=(1, 1, 1, 7))
fit = m.fit(disp=False)
fcst_sarima = fit.get_forecast(steps=90)Годовая сезонность в SARIMA задаётся seasonal_order=(P, D, Q, 365) — но это тяжело учесть в pure SARIMA; обычно либо детрендируем «вручную» (вычитая STL-yearly), либо используем Prophet.
Валидация
Backtest на последних 90 днях 2017:
train = df[df["ds"] < "2017-10-01"]
test = df[df["ds"] >= "2017-10-01"]
m = Prophet(yearly_seasonality=True, weekly_seasonality=True)
m.fit(train)
fcst = m.predict(test[["ds"]])
import numpy as np
mape = np.mean(np.abs((test["y"].values - fcst["yhat"].values) / test["y"].values)) * 100
print(f"MAPE: {mape:.2f}%")Хорошее значение для DAU live-сервиса: MAPE 5–10% на 90-дневном горизонте.
Анализ / интерпретация
В отчёте:
- Прогноз с CI (yhat_lower, yhat_upper).
- Декомпозиция: тренд (стабилен/спадает), недельная сезонность (выходные +X%), годовая сезонность (январь — обычно пик).
- Допущения: «нет крупных релизов / событий в Q1 2018». Если ожидается релиз — модель не учитывает его без явного override (
add_regressorв Prophet). - Сценарии: pessimistic / base / optimistic с разными changepoint_prior_scale.
Для бизнеса: «На январь 2018 года ожидаем DAU около X тыс., с пиком Y тыс. в первую неделю января (новогодние праздники). Февраль и март — стабильно ≈ Z тыс. с просадкой в будни на 10–15%».
Подводные камни
- Использовать только train без backtest. Без валидации MAPE/MAE прогноз — «доверьте мне». Бэктест — обязателен.
- Игнорировать праздники. Новогодний пик может быть +50% — без его явного учёта прогноз будет систематически промахиваться.
- Слишком гибкая модель. Высокий
changepoint_prior_scale→ overfit на исторических локальных пиках, плохой долгосрочный прогноз. - Прогноз на изменения, которых не было в истории. Если запланирован релиз нового мода — модель не знает. Накладывайте поверх руками.
- Авторегрессия для DAU — спорно. «DAU вчера → DAU сегодня» — есть, но это часто мешает учитывать сезонность. Prophet/SARIMA лучше pure AR.
- Не учитывать обрыв ряда. Если в данных есть пропуски (сервер не работал) — заполнение 0 ломает модель. Используйте интерполяцию или удалите.
- «Тренд» — обманчивый сигнал. Падение DAU на хвосте может быть локальным (просадка, ивент закончился), а Prophet продлит как тренд. Контролируйте
n_changepoints.
Альтернативы
- Ensemble: медиана Prophet + SARIMA + naive seasonal — стабильнее одиночной модели.
- DeepAR (если есть много продуктов / гео и можно учиться на похожих рядах).
- Hierarchical forecasting: прогнозы на разных уровнях агрегации (страна / платформа) с reconciliation.
Эталонный ответ
Подход: Prophet с включённой годовой и недельной сезонностью + кастомные праздники (Новый год). Валидация — backtest на последних 90 днях 2017. Метрика — MAPE. Главное в ответе — обосновать выбор метода (что в данных есть тренд + 2 уровня сезонности + праздники) и не забыть про валидацию.