Как посчитать DiD в SQL

Закрепи формулу did в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать did в Telegram

Зачем DiD

Difference-in-differences (DiD) — самый популярный метод causal inference, когда A/B-эксперимент невозможен. Идея: сравнить изменение метрики у «обработанной» группы (получили feature, изменение цены, маркетинг) с изменением у контрольной — за один и тот же период. Эффект = разница в дельтах.

Таблица 2×2

Группа До (pre) После (post) Δ
Treated A B B − A
Control C D D − C

DiD = (B − A) − (D − C).

Это и эффект, если treated и control имеют параллельный тренд до intervention.

DiD в SQL

WITH base AS (
    SELECT
        user_id,
        is_treated,
        SUM(amount) FILTER (WHERE order_date BETWEEN '2026-03-01' AND '2026-03-31') AS revenue_pre,
        SUM(amount) FILTER (WHERE order_date BETWEEN '2026-04-15' AND '2026-05-14') AS revenue_post
    FROM users u
    LEFT JOIN orders o USING (user_id)
    GROUP BY user_id, is_treated
)
SELECT
    is_treated,
    AVG(revenue_pre) AS mean_pre,
    AVG(revenue_post) AS mean_post,
    AVG(revenue_post) - AVG(revenue_pre) AS delta
FROM base
GROUP BY is_treated;

Получили 2×2. Эффект:

WITH did_cells AS (
    -- из предыдущего CTE
    SELECT is_treated, AVG(revenue_pre) AS mean_pre, AVG(revenue_post) AS mean_post FROM base
    GROUP BY is_treated
)
SELECT
    (MAX(CASE WHEN is_treated THEN mean_post END) - MAX(CASE WHEN is_treated THEN mean_pre END))
    - (MAX(CASE WHEN NOT is_treated THEN mean_post END) - MAX(CASE WHEN NOT is_treated THEN mean_pre END))
    AS did_estimate
FROM did_cells;

Параллельные тренды

DiD работает только если treated и control двигались параллельно до intervention. Проверка — построить serial trends:

SELECT
    DATE_TRUNC('week', order_date)::DATE AS week,
    is_treated,
    AVG(amount) AS avg_revenue
FROM users u
JOIN orders o USING (user_id)
WHERE order_date BETWEEN '2025-12-01' AND '2026-03-31'  -- pre-period
GROUP BY DATE_TRUNC('week', order_date), is_treated
ORDER BY week, is_treated;

Графически в pre-period линии должны быть параллельны. Расхождение — нарушение DiD assumption.

Закрепи формулу did в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать did в Telegram

Через регрессию

DiD как OLS:

y = α + β1 × treated + β2 × post + β3 × treated × post + ε

β3 — DiD estimate. В SQL делаем «руками»:

SELECT
    AVG(CASE WHEN NOT is_treated AND NOT is_post THEN amount END) AS intercept,
    AVG(CASE WHEN is_treated AND NOT is_post THEN amount END)
    - AVG(CASE WHEN NOT is_treated AND NOT is_post THEN amount END) AS beta_treated,
    AVG(CASE WHEN NOT is_treated AND is_post THEN amount END)
    - AVG(CASE WHEN NOT is_treated AND NOT is_post THEN amount END) AS beta_post,
    (AVG(CASE WHEN is_treated AND is_post THEN amount END)
     - AVG(CASE WHEN is_treated AND NOT is_post THEN amount END))
    - (AVG(CASE WHEN NOT is_treated AND is_post THEN amount END)
     - AVG(CASE WHEN NOT is_treated AND NOT is_post THEN amount END)) AS beta_did
FROM observations;

Для CI и стандартных ошибок — лучше OLS в Python (statsmodels.formula.api.ols).

Частые ошибки

Ошибка 1. Нарушенные параллельные тренды. Если до intervention тренды расходились, DiD приписывает разницу intervention. Графический pre-period check — обязательно.

Ошибка 2. Selection bias в treated. Если treated — это «top customers, кому решили дать feature» — они и так выросли бы. DiD не помогает без random assignment.

Ошибка 3. Малая control group. Слишком маленький control дисперсия раздувает CI. Минимум 100+ юзеров.

Ошибка 4. Внешний шок в post-period. Если в post интервал произошёл рынок-wide event (COVID, праздник), DiD улавливает его в treated × post.

Ошибка 5. Считать на больших covariates без adjustments. Если treated и control различаются по age/plan/region — нужны fixed effects или controls в регрессии.

Связанные темы

FAQ

Когда DiD, когда A/B?

A/B — если можете рандомизировать. DiD — когда нельзя (политика, маркетинг по всем).

Параллельные тренды — что если нарушены?

Считаете synthetic control или event study. DiD будет biased.

DiD на 2 точках?

Минимум. Лучше много pre/post точек для устойчивости.

Можно ли DiD с непрерывным treatment?

Да, через continuous interaction term. Усложняется.

CI для DiD?

Через bootstrap или OLS standard errors. В чистом SQL — bootstrap.