Условие
Дана таблица заказов Bolt Food (по 28 февраля 2020). По доступным данным сделать прогноз: сколько заказов будет в марте 2020 в каждой из показанных стран?
Подсказка от компании: использовать Python или BI (не Excel/Google Sheets).
Решение
Подход
«Прогноз» = модель временного ряда + аккуратное обращение со сценариями. План:
- Построить дневной ряд
ordersпо каждой стране. - Разделить на trend + seasonality (см. парную задачу про сезонность).
- Выбрать модель: naive baseline → Holt-Winters → Prophet → ML.
- Сделать back-test (валидация на отрезке внутри данных).
- Дать прогноз на март.
- Главное: обсудить, что февраль 2020 — начало COVID-19, и любая чисто статистическая экстраполяция «как было» на март 2020 — неверна. Дать сценарии.
Шаг 1. Подготовка дневного ряда
import pandas as pd, numpy as np
from statsmodels.tsa.holtwinters import ExponentialSmoothing
df = pd.read_excel("bolt_food.xlsx", sheet_name="Data", parse_dates=["Created Date"])
df["delivered"] = (df["Order State"] == "delivered").astype(int)
daily = (df.groupby(["Country", df["Created Date"].dt.normalize()])
.agg(orders=("delivered", "sum"))
.reset_index().rename(columns={"Created Date": "date"}))Шаг 2. Naive baseline
Без них нельзя — это якорь, относительно которого мерять качество.
- Naive last 7 days: прогноз дня = тот же день недели в прошлой неделе.
- Average DoW: прогноз = среднее по DoW за весь доступный период.
def baseline_dow(ts: pd.Series, horizon_dates):
by_dow = ts.groupby(ts.index.dayofweek).mean()
return pd.Series([by_dow[d.dayofweek] for d in horizon_dates], index=horizon_dates)Шаг 3. Holt-Winters
Для weekly seasonality (m = 7):
def hw_forecast(ts: pd.Series, horizon: int):
ts = ts.asfreq("D").fillna(0)
if len(ts) < 21: # нужно ≥ 3 периода
return None
model = ExponentialSmoothing(
ts, trend="add", seasonal="add", seasonal_periods=7,
initialization_method="estimated"
).fit(optimized=True)
return model.forecast(horizon)
forecasts = {}
for country, sub in daily.groupby("Country"):
ts = sub.set_index("date")["orders"]
fc = hw_forecast(ts, horizon=31)
forecasts[country] = fcadd + add — для рядов без явной мультипликативной структуры. Если амплитуда сезонности растёт с уровнем, использовать seasonal="mul".
Шаг 4. Prophet (опционально, если данных достаточно)
from prophet import Prophet
def prophet_forecast(ts: pd.Series, horizon: int):
df_ = ts.reset_index().rename(columns={"date": "ds", "orders": "y"})
m = Prophet(weekly_seasonality=True, yearly_seasonality=False, daily_seasonality=False)
m.fit(df_)
future = m.make_future_dataframe(periods=horizon, freq="D")
fc = m.predict(future)
return fc.set_index("ds")["yhat"].iloc[-horizon:]Prophet удобен, если хотим добавить holidays, regressors (например, lockdown dummy).
Шаг 5. Back-test
Качество модели = MAPE / RMSE на отложенном куске. Например, обучаем на январе, валидируем на первых трёх неделях февраля:
def backtest(ts, horizon=14):
train, test = ts.iloc[:-horizon], ts.iloc[-horizon:]
fc = hw_forecast(train, horizon=horizon)
mape = (np.abs(test.values - fc.values) / np.maximum(test.values, 1)).mean()
return mape
for country, sub in daily.groupby("Country"):
ts = sub.set_index("date")["orders"]
print(country, "MAPE =", round(backtest(ts), 3))Если MAPE < 0.15 — модель пригодна. Если 0.30+ — слишком шумные данные, fallback на baseline.
Шаг 6. Главное: COVID-19
Февраль 2020:
- Италия — первые случаи 31.01, рост в феврале, лockdown с 9 марта (национально). До 28 февраля — растущая тревожность, но ограничения только локальные.
- Португалия — первые случаи 02.03; до 28.02 — обычный режим.
- Финляндия / Эстония — единичные случаи к концу февраля.
- Гана — никаких ограничений до середины марта.
Что это значит для прогноза:
- Чисто временной модели в марте 2020 верить нельзя. Она экстраполирует «обычный» март, а реальность будет резко другой.
- Эффект на food-delivery двойственный:
- Lockdown → рестораны закрыты для dine-in → люди заказывают доставку → рост спроса.
- Закрытие части ресторанов и проблемы с курьерами → падение supply → может упасть delivered.
- Без знания дат локдауна и реакции компании любая цифра — гадание.
Шаг 7. Сценарный прогноз
Правильный senior-ответ — три сценария на каждую страну:
| Сценарий | Допущение | Прогноз |
|---|---|---|
| Base | Без COVID-эффекта (модель Holt-Winters) | заказов |
| Boom | Lockdown с середины марта, спрос +30% после | для первой половины + для второй |
| Bust | Локдаун + закрытие ресторанов, supply −50% | + |
С каждым сценарием — диапазон, не точное число. Бизнесу нужны диапазоны и допущения, чтобы планировать capacity.
Шаг 8. Доверительные интервалы
Holt-Winters сам считает CI:
fc_obj = model.get_forecast(horizon) # для statsmodels SARIMAX/ETSResults
mean = fc_obj.predicted_mean
ci = fc_obj.conf_int(alpha=0.05)Для Prophet — yhat_lower, yhat_upper. Эти интервалы — только статистическая неопределённость, не учитывают COVID.
Подводные камни
- Слепое доверие модели. Holt-Winters в марте 2020 даст «обычный март» — это будет неверно для большинства стран. Senior должен это явно проговорить.
- Слишком короткий период обучения. Если данных всего 1–2 месяца, модели сезонности (HW, SARIMA, Prophet) будут переобучены или не сойдутся. Используйте простые baseline (DoW average).
- Маленькие страны. В Гане 10–20 заказов/день — статистический шум. Прогноз с MAPE 50%+. Дайте интервал, не точку.
# of ordersvsgmv. Разные метрики ведут себя по-разному (cancellation rate смещает orders, не gmv). Уточните, что именно прогнозируете.- Праздники. Март 2020: 17.03 St. Patrick's в IE, 8.03 в Восточной Европе. Они входят в модель неявно — лучше явно учесть как regressor.
- Tilt в выгрузке. Если данные за разный период по странам, модель начнёт «не туда». Проверьте период по каждой стране отдельно.
- Forecast horizon = 31 день. На горизонте 30+ дней любой временной модели MAPE растёт быстро. Чем дальше — тем шире CI.
- Outliers и пропуски в
delivered: за один сбой ETL может «провалиться» весь день. До прогноза — фиксы или интерполяция.
Эталонный ответ (структура презентации)
- EDA по странам: дневные ряды, DoW-индекс, тренды.
- Модель и валидация: Holt-Winters c
m=7, back-test MAPE. - Базовый прогноз (без COVID): таблица «прогноз заказов в марте 2020 по странам, с CI».
- COVID-disclaimer: явно сказать, что чисто статистическая модель в марте 2020 не годится.
- Сценарии: Base / Boom / Bust с допущениями.
- Рекомендации бизнесу: к чему готовиться, какие данные начать собирать оперативно (ежедневные supply-метрики, отмены по причине COVID), как обновлять модель по мере поступления.
Главное на собесе: показать, что знаете возможности и пределы моделей и понимаете, что в марте 2020 чистый forecast ≠ ответственный ответ.