Difference-in-Differences простыми словами
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
Marketplace запустил фичу в одном городе, в другом нет — как измерить эффект? A/B-тест невозможен: нельзя юзеру одновременно «видеть и не видеть». На помощь приходит DiD — классика quasi-experimental methods.
На собесах в B2C / marketplace / delivery компаниях DiD — обязательный навык для middle+ аналитика.
Короткое объяснение
Difference-in-Differences сравнивает изменения в treatment и control группах до и после воздействия.
Effect = (Treatment_after - Treatment_before) - (Control_after - Control_before)Первая разница — что случилось у treatment. Вторая — что случилось у control (baseline trend). Разница разниц — чистый эффект.
Пример
Москва получила фичу, Санкт-Петербург — нет.
| До | После | |
|---|---|---|
| Москва | 100 orders | 130 orders |
| СПб | 90 orders | 110 orders |
- Москва: +30
- СПб: +20 (общий тренд сезонный)
Effect = 30 - 20 = +10 orders — чистый эффект фичи.
Без DiD мы бы сказали «фича дала +30», что неверно (+20 был бы и без фичи).
Parallel trends assumption
Ключевое assumption: без воздействия тренды в двух группах были бы параллельны.
Т.е. разница между Москвой и СПб оставалась бы такой же.
Если это не так — DiD не работает.
Как проверить
Посмотрите исторические данные. Если тренды паралельны в pre-period — assumption likely holds.
В формуле
Y = α + β₁ × Treatment + β₂ × Post + β₃ × (Treatment × Post) + εГде:
- Treatment = 1 если в treatment group
- Post = 1 если после воздействия
- β₃ — это и есть эффект (DiD estimate)
Пример в SQL
SELECT
city, period,
SUM(orders) AS total
FROM data
WHERE city IN ('Moscow', 'SPb')
GROUP BY 1, 2;
-- Потом в Python / Excel:
effect = (moscow_after - moscow_before) - (spb_after - spb_before)В Python
import statsmodels.api as sm
data['treatment'] = (data['city'] == 'Moscow').astype(int)
data['post'] = (data['period'] == 'after').astype(int)
data['interaction'] = data['treatment'] * data['post']
X = sm.add_constant(data[['treatment', 'post', 'interaction']])
model = sm.OLS(data['orders'], X).fit()
print(model.summary()) # коэффициент interaction = DiD estimateКогда использовать
- A/B невозможен (географическое воздействие, legal reasons)
- Несколько групп (более 2)
- Временные панельные данные
- Natural experiments (minimum wage, закон, эпидемия)
Проблемы и pitfalls
Нарушение parallel trends
Если Москва рос быстрее СПб и без фичи — DiD завышает эффект.
Решение: placebo-тест. Применить DiD на pre-period — если виден «эффект» там, где его нет — assumption нарушено.
Spillover effects
Если Москва «заражает» СПб (пользователи мигрируют) — control не чистый.
Composition changes
Если в treatment group изменился состав — эффект смешан с compositional effect.
Anticipation
Если treatment group «знает заранее» о будущем воздействии и меняет поведение → bias.
Расширения
Synthetic control
Вместо одного control города — weighted combination нескольких. Более precise.
Event study
Смотреть эффект по периодам: какие дни после? Показывает dynamics.
Triple-differences (DDD)
Три размерности: treatment × post × подгруппа.
На собесе
«Что такое DiD?» Метод измерения эффекта без randomized assignment, через сравнение изменений в группах.
«Ключевое assumption?» Parallel trends — без воздействия тренды были бы параллельны.
«Как проверить?» Placebo tests, визуализация pre-period trends.
«Когда использовать?» Когда нельзя A/B (geo, legal, etc.).
Частые ошибки
Не проверять parallel trends
Самая частая и критичная ошибка.
Игнорировать spillover
Treatment может влиять на control indirectly.
Pre-period слишком короткий
Нужно достаточно long pre-period, чтобы оценить trends.
Связанные темы
FAQ
Все равно предвзятый?
Если assumption выполняется — unbiased. Иначе смещённый.
Работает ли с несколькими группами?
Да, через panel data methods.
DiD vs A/B?
A/B — gold standard (randomized). DiD — alternative когда A/B нельзя.
Тренируйте статистику — откройте тренажёр с 1500+ вопросами для собесов.