Как посчитать regression discontinuity в SQL
Содержание:
Зачем RDD
Regression Discontinuity Design (RDD) применяется когда treatment назначается по пороговому значению переменной. Юзеры с score > 80 получают premium feature, остальные — нет. RDD оценивает скачок outcome ровно на cutoff: сравнивает почти-treated (score 79) и почти-control (score 81). Эти группы похожи во всём, кроме самого порога.
Sharp vs fuzzy
- Sharp RDD: treatment назначается строго по cutoff. Все score > 80 → treated.
- Fuzzy RDD: на cutoff вероятность treatment скачком растёт, но не до 1. Например, 30% → 70%.
Fuzzy решается через IV-эстиматор: cutoff_indicator как instrument для treatment.
RDD в SQL
Простой sharp RDD через сравнение средних в окне ±h вокруг cutoff:
WITH window_data AS (
SELECT
score,
outcome,
CASE WHEN score >= 80 THEN 1 ELSE 0 END AS treated,
score - 80 AS centered_score
FROM observations
WHERE score BETWEEN 75 AND 85 -- bandwidth h = 5
)
SELECT
treated,
COUNT(*) AS n,
AVG(outcome) AS mean_outcome
FROM window_data
GROUP BY treated;Разница mean_outcome(treated) − mean_outcome(control) — оценка эффекта (LATE на cutoff).
Местная линейная регрессия — точнее:
WITH window_data AS (
SELECT
outcome,
score - 80 AS x,
CASE WHEN score >= 80 THEN 1 ELSE 0 END AS d,
(score - 80) * (CASE WHEN score >= 80 THEN 1 ELSE 0 END) AS x_times_d
FROM observations
WHERE score BETWEEN 75 AND 85
)
SELECT
-- y = a + b*x + c*d + e*(x*d). c — RDD effect.
AVG(outcome) FILTER (WHERE d = 1) - AVG(outcome) FILTER (WHERE d = 0) AS simple_diff,
COUNT(*) AS n
FROM window_data;Для real RDD используют local linear regression — в чистом SQL громоздко, лучше Python rdrobust.
Bandwidth
Маленький bandwidth — точнее, но меньше N. Большой — biased если outcome не линейный. Стандарт — оптимальный bandwidth (Imbens-Kalyanaraman):
-- Грубо: bandwidth = 3 × σ(score | score near cutoff)
WITH near_cutoff AS (
SELECT score FROM observations WHERE ABS(score - 80) < 10
)
SELECT 3 * STDDEV_SAMP(score) AS suggested_h FROM near_cutoff;Чувствительность к выбору h проверяется отдельно: запускают RDD на h, h/2, 2h.
Manipulation check
Если юзеры могут manipulate score (например, попасть в premium искусственно), RDD biased. Density-test на cutoff:
SELECT
FLOOR(score) AS score_bucket,
COUNT(*) AS n
FROM observations
WHERE score BETWEEN 75 AND 85
GROUP BY FLOOR(score)
ORDER BY score_bucket;«Bump» на 80 — sign of manipulation. McCrary test делает это формально (вне SQL).
Частые ошибки
Ошибка 1. Bandwidth слишком большой. Если bandwidth = вся выборка, RDD = OLS с control variables. Bias возрастает.
Ошибка 2. Манипуляция score. Юзеры подгоняют score под cutoff — selection bias возвращается.
Ошибка 3. Discontinuity в running variable. Если distribution score внезапно скачет на cutoff (но не из-за manipulation, а из measurement), RDD не работает.
Ошибка 4. Игнорировать LATE. RDD даёт effect для юзеров с score ≈ cutoff, не для всей populace.
Ошибка 5. Линейный fit там, где нужен polynomial. Если outcome нелинейный, простая разница средних в окне даёт bias.
Связанные темы
- Как посчитать DiD в SQL
- Как посчитать instrumental variable в SQL
- Regression discontinuity простыми словами
- Как посчитать propensity score matching в SQL
FAQ
RDD vs DiD?
DiD — temporal, RDD — cross-sectional (по score). Разные источники variation.
Sharp vs fuzzy — что чаще?
Sharp — когда правило строгое (grade, age cutoff). Fuzzy — soft cutoffs (вероятность treatment).
Какой bandwidth?
Оптимальный — Imbens-Kalyanaraman или Calonico-Cattaneo-Titiunik. Стандартизировано в rdrobust.
Можно ли RDD на бинарном outcome?
Да, через logit или просто linear probability model в окне.
Сколько данных?
В окне ±h хотя бы 100 юзеров с каждой стороны. На малых N RDD шумит.