Как посчитать naive forecast в SQL
Содержание:
Зачем naive forecast
Naive — простейший прогноз: «завтра как сегодня» или «через неделю как неделю назад». Используется как baseline: если ML-модель не обыгрывает naive — она бесполезна. Звучит банально, но 30% прод-моделей реально хуже seasonal naive.
Last value naive
Y_hat_{T+h} = Y_T для всех h. В SQL — просто берём последнее значение:
WITH last_value AS (
SELECT value, DATE
FROM daily_metrics
WHERE metric_name = 'dau'
ORDER BY DATE DESC
LIMIT 1
),
forecast AS (
SELECT
CURRENT_DATE + h AS forecast_date,
lv.value AS prediction
FROM last_value lv
CROSS JOIN generate_series(1, 14) h
)
SELECT * FROM forecast;Подходит для рядов без тренда и сезонности — типа стабильной error rate.
Seasonal naive
Y_hat_{T+h} = Y_{T+h−m}, где m — длина сезона. Для дневной метрики с недельной сезонностью:
WITH past_week AS (
SELECT
DATE,
value,
ROW_NUMBER() OVER (ORDER BY DATE DESC) AS days_ago
FROM daily_metrics
WHERE metric_name = 'dau'
AND DATE >= CURRENT_DATE - INTERVAL '7 days'
),
forecast AS (
SELECT
CURRENT_DATE + h AS forecast_date,
(SELECT value FROM past_week WHERE days_ago = 8 - h) AS prediction
FROM generate_series(1, 7) h
)
SELECT * FROM forecast;Для месячной сезонности — m = 12, берём значение 12 месяцев назад.
Drift method
Y_hat_{T+h} = Y_T + h × (Y_T − Y_1) / (T − 1). Linearly extrapolates от первой точки до последней:
WITH series AS (
SELECT DATE, value
FROM daily_metrics
WHERE metric_name = 'dau'
AND DATE >= CURRENT_DATE - INTERVAL '90 days'
),
endpoints AS (
SELECT
MIN(DATE) AS first_date,
(SELECT value FROM series WHERE DATE = (SELECT MIN(DATE) FROM series)) AS first_value,
MAX(DATE) AS last_date,
(SELECT value FROM series WHERE DATE = (SELECT MAX(DATE) FROM series)) AS last_value,
(MAX(DATE) - MIN(DATE))::INT AS span_days
FROM series
)
SELECT
CURRENT_DATE + h AS forecast_date,
last_value + h * (last_value - first_value)::NUMERIC / NULLIF(span_days, 0) AS prediction
FROM endpoints
CROSS JOIN generate_series(1, 14) h;Если ряд рос — drift продолжает рост. Не моделирует сезонность.
Naive как benchmark
Сравнить production-модель с seasonal naive:
WITH backtest AS (
SELECT
DATE,
actual,
model_prediction,
seasonal_naive_prediction
FROM forecasts_backtest
WHERE DATE >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT
AVG(ABS(actual - model_prediction)) AS mae_model,
AVG(ABS(actual - seasonal_naive_prediction)) AS mae_naive,
AVG(ABS(actual - model_prediction))
/ NULLIF(AVG(ABS(actual - seasonal_naive_prediction)), 0) AS mase_ratio
FROM backtest;mase_ratio < 1 — модель лучше naive. > 1 — хуже, имеет смысл откатить.
Частые ошибки
Ошибка 1. Считать naive plain как «всегда хуже ML». На weekly DAU seasonal naive часто обыгрывает наивные ML-baselines (RF, простые регрессии).
Ошибка 2. Last value на сезонных данных. Прогноз для выходного по понедельнику — ошибка в 30%.
Ошибка 3. Drift на коротких рядах. Drift через 2 точки даёт прямую — переэкстраполирует на длинный horizon.
Ошибка 4. Не учитывать holidays. Если 31 декабря — аномалия, seasonal naive отнесёт аномалию в 31-12 следующего года. Нужно сезонное очищение от holidays.
Ошибка 5. Сравнивать модель и naive без CI. MAE модели 99, naive — 100 — статистически не различимы. Diebold-Mariano test.
Связанные темы
- Как посчитать exponential smoothing в SQL
- Как посчитать Holt-Winters в SQL
- Как посчитать MAPE в SQL
- Time series forecasting для аналитика
FAQ
Какой naive выбрать?
Last value для рядов без сезонности. Seasonal naive для дневных/недельных. Drift для трендов.
Когда naive обыгрывает ML?
Часто. Особенно на коротких рядах (< 200 точек) и при сильной сезонности.
Что считается «хорошим» MASE?
< 1 — модель лучше naive. 0.8–0.9 — хороший продакшен.
Naive в production?
Да, как fallback. Если ML-модель упала — naive отвечает.
Drift для коротких прогнозов?
Норм. Для длинных (> 30 точек) — слишком линейно.