Корреляция и причинность: что делать, когда эксперимент невозможен
Фраза «корреляция не означает причинности» — самая известная и самая бесполезная в прикладной аналитике. Её знают все. Её произносят на каждом разборе. И сразу после неё в презентации появляется слайд «пользователи, включившие уведомления, покупают в 3,4 раза чаще — надо включить уведомления всем».
Проблема не в незнании принципа, а в том, что он сформулирован как запрет, а не как программа работы: говорит, чего нельзя, и молчит о том, что тогда делать. А делать что-то надо — решение о раскатке фичи, о звонке клиенту, о переносе кнопки принимается всё равно, с вашей оценкой или без неё. Аналитик, который на любой вопрос отвечает «это всего лишь корреляция», перестаёт участвовать в решениях примерно через два месяца.
Эта глава — про то, что делать. Она устроена как лестница: сверху рандомизированный эксперимент из главы про A/B-тесты, а ниже — набор конструкций, вытаскивающих причинный вывод из данных, собранных без вашего участия. У каждой конструкции есть цена: допущение, которое нельзя проверить по данным и которое надо выложить на стол вместе с числом. Внизу лестницы — ситуации, где причинность недоступна в принципе, и там честный ответ звучит «мы не можем ответить на этот вопрос этими данными», а не «связь слабая, но присутствует».
Сквозная мысль всего трека здесь становится буквальной. Способ сбора данных определяет, какие выводы они выдерживают. Одни и те же две колонки в одной и той же таблице — «включил уведомления» и «купил» — позволяют сказать «эти пользователи покупают чаще», если строки появились сами собой, и «уведомления повышают покупки на столько-то», если распределение по колонке было вашим случайным броском монеты. Данные идентичны, число одинаковое, вывод разный. Разница не в данных, а в том, как они возникли.
Что вообще значит «X влияет на Y»
Прежде чем что-то оценивать, нужно определить, что именно оценивается: разговорное «влияет» слишком туманно, чтобы привязать к нему формулу. Рабочее определение — контрфактическое. X влияет на Y, если при вмешательстве, меняющем X и ничего больше, Y стал бы другим. Ключевые слова: «вмешательство» (не наблюдение) и «ничего больше» (не заодно и половина мира).
Стандартная формализация — потенциальные исходы Дональда Рубина. Для каждого пользователя $i$ существуют две величины: $Y_i(1)$ — что было бы, получи он воздействие, и $Y_i(0)$ — что было бы, не получи. Индивидуальный эффект $\tau_i = Y_i(1) - Y_i(0)$. И тут же — фундаментальная проблема причинного вывода: для каждого пользователя мы наблюдаем ровно одну из двух величин. Тот, кто включил уведомления, дал нам $Y_i(1)$; его $Y_i(0)$ не существует нигде, ни в каком логе, никогда. Причинный эффект в принципе не наблюдаем на уровне единицы. Оценить можно только средние:
$$\mathrm{ATE} = \mathbb{E}\big(Y(1) - Y(0)\big) \qquad \mathrm{ATT} = \mathbb{E}\big(Y(1) - Y(0) \mid D = 1\big)$$
Первое — эффект на всех, второе — на тех, кто воздействие получил ($D = 1$ — признак воздействия). Это разные числа, и путать их дорого: уведомления могут отлично работать на тех, кто включил их по своей воле, и не работать вовсе на остальных.
Откуда берётся смещение отбора
Главная алгебра главы: что именно мы считаем, когда сравниваем две группы «как есть»?
$$\underbrace{\mathbb{E}(Y \mid D=1) - \mathbb{E}(Y \mid D=0)}{\text{наблюдаемая разница}} = \underbrace{\mathbb{E}\big(Y(1) - Y(0) \mid D=1\big)}{\mathrm{ATT}\ -\ \text{то, что нужно}} + \underbrace{\mathbb{E}\big(Y(0) \mid D=1\big) - \mathbb{E}\big(Y(0) \mid D=0\big)}_{\text{смещение отбора}}$$
Вывод в две строки: наблюдаемое среднее в группе воздействия равно $\mathbb{E}(Y(1) \mid D=1)$, в контрольной — $\mathbb{E}(Y(0) \mid D=0)$; вычтем и прибавим $\mathbb{E}(Y(0) \mid D=1)$.
Второе слагаемое читается по-русски так: насколько отличались бы группы, даже если бы воздействия не было вообще. Люди, включившие уведомления, — активные, лояльные, уже покупавшие. Их $Y(0)$, то есть покупки без всяких уведомлений, и так выше. Наблюдаемая разница в 3,4 раза складывается из эффекта уведомлений и из этой разницы, и по одной колонке «включил / не включил» разделить их нечем.
Рандомизация обнуляет второе слагаемое. Если $D$ назначен броском монеты, он независим от потенциальных исходов, значит $\mathbb{E}(Y(0) \mid D=1) = \mathbb{E}(Y(0) \mid D=0)$, смещение равно нулю, и наблюдаемая разница — это ATT (он же ATE). Вся ценность эксперимента в этом одном равенстве; всё остальное в главе — попытки приблизиться к нему без монеты.
Четыре механизма связи без влияния
Связь между X и Y возникает по четырём причинам, и только одна из них — влияние X на Y.
внутри выборки" .-> y3 end subgraph D["4. Цепочка — медиатор"] direction TB x4["Письмо"] --> z4["Заполнил профиль"] --> y4["Купил"] end
Общая причина. И уведомления, и покупки вызваны третьей вещью — вовлечённостью. Лечится контролем этой третьей вещи, если вы её измерили.
Обратное направление. Не уведомления привели к покупкам, а те, кто и так покупает, пошли и включили уведомления. По срезу «на сегодня» неразличимо вообще: нужны временные метки события включения и события покупки. Проверка минимальная и обязательная — сравните дату воздействия с датой исхода. Половина «причинных» находок в продуктовой аналитике умирает на этом шаге, потому что в витрине лежит агрегат за период и порядок событий потерян; как он теряется, разбирают главы про источники данных и качество данных.
Отбор. Самый коварный: связь создаётся не в мире, а в вашем WHERE. Цепочка. X действительно влияет на Y, но через посредника — и если посредника «учесть», эффект исчезнет. Оба разберём отдельными разделами.
Пятый механизм — простое совпадение: проверьте двести разрезов, и десяток даст p < 0,05 при полном отсутствии эффектов (проверка гипотез). Коллекция таких связей — Spurious Correlations Тайлера Вигена, где потребление маргарина коррелирует с разводами в штате Мэн на 0,99. Логическую сторону — почему «после этого» не значит «вследствие этого» — разбирает глава про причинно-вероятностные ошибки.
Третья переменная: контролировать или не контролировать
Здесь живёт самая дорогая ошибка практикующего аналитика. Она звучит как «давай на всякий случай добавим побольше контролей — хуже не будет». Будет. У третьей переменной $Z$ есть четыре принципиально разные роли, и правильное действие в каждой своё.
| Роль $Z$ | Структура | Что делать | Что будет, если наоборот |
|---|---|---|---|
| Конфаундер — общая причина | $Z \to X$, $Z \to Y$ | Контролировать обязательно | Эффект смещён, знак может быть противоположным |
| Медиатор — звено цепи | $X \to Z \to Y$ | Не контролировать, если нужен полный эффект | Эффект «исчезает»: вы вычли собственный механизм действия |
| Коллайдер — общее следствие | $X \to Z \leftarrow Y$ | Не контролировать никогда | Контроль создаёт связь там, где её нет |
| Предиктор исхода | $Z \to Y$, но не $\to X$ | Контролировать полезно | Ничего страшного, просто шире интервал |
Формальное правило, покрывающее все случаи, — критерий заднего входа (backdoor criterion) Джудеа Пёрла: набор $Z$ годится для оценки эффекта $X$ на $Y$, если он блокирует все пути от $X$ к $Y$, идущие «против стрелки» из $X$, и не содержит потомков $X$. При выполнении критерия работает формула корректировки:
$$\Pr\big(Y = y \mid \mathrm{do}(X = x)\big) = \sum_{z} \Pr(Y = y \mid X = x, Z = z) \cdot \Pr(Z = z)$$
Слева — вероятность при вмешательстве, справа — только наблюдаемые условные вероятности. В этом весь фокус: при выполнении критерия причинную величину считает обычный SQL, при невыполнении — не считает никакой. Подробнее про язык графов — глава причинные диаграммы и книга Judea Pearl, Dana Mackenzie, The Book of Why.
Медиатор: как «доказать», что рабочая штука не работает
Продуктовое письмо после регистрации: «заполните профиль». Гипотеза — письмо повышает конверсию в первую покупку. Аналитик считает эффект письма, контролируя заполненность профиля («заполненный профиль сильно влияет на конверсию, надо учесть»). Результат: эффект ≈ 0. Вывод в отчёте: «письмо не работает, отключаем».
Что произошло: письмо работает через заполнение профиля. Сравнивая людей с одинаковой заполненностью, вы сравниваете тех, кто заполнил бы профиль и без письма. Внутри этой группы письмо действительно ничего не меняет — оно уже сделало свою работу, просто вы её вычли. Полный эффект = прямой плюс через медиатор, и на вопрос «отключать ли письмо» отвечает именно полный. Правило простое и жёсткое: никогда не контролируйте то, что происходит после воздействия. Дата события — ваш главный инструмент контроля контролей.
Коллайдер: связь, которой не было до вашего запроса
Отбор кандидатов. Пусть навык алгоритмов и навык коммуникации в потоке откликов независимы (доля «сильных» — половина по каждому), а нанимаем, если хотя бы один навык сильный. На 1000 откликов было по 250 в каждой клетке; среди 750 нанятых распределение становится таким:
| Среди нанятых | Коммуникация слабая | Коммуникация сильная |
|---|---|---|
| Алгоритмы слабые | 0 (все отсеяны) | 250 |
| Алгоритмы сильные | 250 | 250 |
Коэффициент сопряжённости $\varphi$ в потоке откликов равен ровно нулю; среди нанятых:
$$\varphi = \frac{ad - bc}{\sqrt{(a+b)(c+d)(a+c)(b+d)}} = \frac{0 \cdot 250 - 250 \cdot 250}{\sqrt{250 \cdot 500 \cdot 250 \cdot 500}} = -0{,}5$$
Внутри компании честно наблюдается: «сильные алгоритмисты хуже общаются». Это факт о процессе найма, а не о людях. Заметьте: никаких ошибок в данных, никакой предвзятости в замерах — только WHERE hired = true.
Продуктовые версии того же самого встречаются ежедневно. «Среди платящих цена не влияет на удовлетворённость» — платят те, кому подошла либо цена, либо функциональность, отбор по «или» и есть коллайдер. «Среди доживших до 90-го дня онбординг не помогает» — дожили либо благодаря онбордингу, либо потому что им и так подходило. «Среди тикетов, дошедших до второй линии, сложность не связана со временем ответа» — доходят либо сложные, либо те, где первая линия затянула.
Универсальный признак опасности: вы условились на что-то, что случилось после и X, и Y. Короче: любой WHERE по величине, на которую влияет исход, — потенциальный коллайдер. Родственная тема — систематика выживших источников из главы про источники данных.
Парадокс Симпсона: когда контроль нужен и меняет знак
Раскатали новый экран оплаты — не случайно, а сначала на десктопе, где команда была увереннее. Через месяц смотрим:
-- Наивное сравнение: то, что попадёт в дашборд по умолчанию
SELECT
has_new_checkout,
COUNT(*) AS users,
COUNT(*) FILTER (WHERE converted) AS conversions,
ROUND(100.0 * COUNT(*) FILTER (WHERE converted) / COUNT(*), 2) AS cr_pct
FROM user_snapshot
GROUP BY has_new_checkout
ORDER BY has_new_checkout;
| Новый экран | Пользователей | Конверсий | CR |
|---|---|---|---|
| нет | 10 000 | 650 | 6,50 % |
| да | 10 000 | 1 750 | 17,50 % |
Рост в 2,7 раза. Теперь добавим в SELECT и GROUP BY один столбец — platform:
| Платформа | Новый экран | Пользователей | Конверсий | CR |
|---|---|---|---|---|
| mobile | нет | 9 000 | 450 | 5,00 % |
| mobile | да | 1 000 | 40 | 4,00 % |
| desktop | нет | 1 000 | 200 | 20,00 % |
| desktop | да | 9 000 | 1 710 | 19,00 % |
В каждом сегменте новый экран хуже старого. В сумме — лучше в 2,7 раза. Это парадокс Симпсона, и мистики в нём нет: новый экран стоит преимущественно там, где конверсия и так вчетверо выше. Агрегат измеряет не экран, а платформу.
Правильная оценка — прямая стандартизация: посчитать конверсию каждого варианта так, как если бы состав по платформам был одинаковым. Это ровно формула корректировки заднего входа, записанная на SQL:
WITH cell AS (
SELECT platform, has_new_checkout,
COUNT(*) AS users,
COUNT(*) FILTER (WHERE converted) AS conversions
FROM user_snapshot
GROUP BY platform, has_new_checkout
),
w AS ( -- вес страты = её доля во всей популяции, то есть Pr(Z = z)
SELECT platform,
SUM(users)::numeric / SUM(SUM(users)) OVER () AS weight
FROM cell
GROUP BY platform
)
SELECT c.has_new_checkout,
ROUND(100 * SUM(c.conversions::numeric / c.users * w.weight), 2) AS cr_standardized_pct
FROM cell AS c
JOIN w USING (platform)
GROUP BY c.has_new_checkout
ORDER BY c.has_new_checkout;
Результат: 12,50 % без нового экрана против 11,50 % с ним. Проверьте руками: $0{,}5 \cdot 5% + 0{,}5 \cdot 20% = 12{,}5%$ и $0{,}5 \cdot 4% + 0{,}5 \cdot 19% = 11{,}5%$. Оценка эффекта — минус 1 процентный пункт, знак противоположен наивному. Про оконные функции и агрегаты, которыми это считается, — глава SQL для анализа.
И сразу оговорка, без которой раздел вреден. Разбиение по платформе помогло, потому что платформа — конфаундер: она влияет и на раскатку, и на конверсию, и предшествует обеим. Разбиение по чему попало вредит: разобьёте по медиатору — потеряете эффект, по коллайдеру — получите эффект из воздуха. Симпсон не учит «всегда разбивай»; он учит «сначала пойми, откуда взялось распределение по группам». Механизм, по которому локальные улучшения складываются в противоположный общий результат, разобран в главе про локальную оптимизацию.
Стандартизация на Python — на случай, если считаете не в базе:
from collections import defaultdict
Row = tuple[str, int, int] # (страта, воздействие 0/1, исход 0/1)
def standardized_rate(rows: list[Row], treat: int, weights: dict[str, float]) -> float:
"""Прямая стандартизация: средний исход по стратам с внешними весами."""
num: dict[str, int] = defaultdict(int)
den: dict[str, int] = defaultdict(int)
for stratum, d, y in rows:
if d == treat:
num[stratum] += y
den[stratum] += 1
if any(den[s] == 0 for s in weights): # страта без наблюдений в этой ветке —
raise ValueError("позитивность нарушена") # контрфактического наблюдения нет
return sum(weights[s] * num[s] / den[s] for s in weights)
Сложность: $O(n + k)$ по времени и $O(k)$ по памяти, где $k$ — число страт. Интервал берётся бутстрапом поверх этой функции — $O(b \cdot n)$ при $b$ ресэмплах; на миллионах строк сырые строки ресэмплить не надо, достаточно разыгрывать агрегаты по стратам биномиально. Без интервала стандартизованная разность в −1 п.п. — такое же голое число, как и наивные +11 п.п. (неопределённость).
Лестница доказательности
Прежде чем разбирать методы, полезно увидеть их сразу все — по силе вывода и по доступности.
Самые популярные методы — корреляция в срезе и сравнение «до и после» — стоят в правом нижнем углу: дёшево и почти бесполезно. Именно их результаты чаще всего попадают в презентации.
Как выбрать метод под конкретную задачу:
пользователей?"} Q1 -->|да| AB["A/B-тест"] Q1 -->|нет| Q2{"Можно рандомизировать
город, склад, время суток?"} Q2 -->|да| GEO["Гео-эксперимент
или switchback"] Q2 -->|нет| Q3{"Можно рандомизировать
приглашение, а не саму фичу?"} Q3 -->|да| IVD["Encouragement design:
ITT и LATE"] Q3 -->|нет| Q4{"Есть порог в правиле,
который пользователь не крутит?"} Q4 -->|да| RDD["Разрывный дизайн"] Q4 -->|нет| Q5{"Есть похожая группа
или период без вмешательства?"} Q5 -->|да| DID["Разность разностей
или синтетический контроль"] Q5 -->|нет| Q6{"Все конфаундеры
измерены и записаны?"} Q6 -->|"честно — нет"| NONE["Причинность недоступна:
описываем связь,
границы и чувствительность"] Q6 -->|"допустим, да"| ADJ["Стратификация, регрессия, IPW
плюс анализ чувствительности"]
Прежде чем спускаться по лестнице, убедитесь, что верхняя ступенька действительно недоступна. Настоящих причин ровно пять, и «у нас нет фреймворка для экспериментов» в их число не входит: сетевые эффекты (воздействие на A меняет исход у B, нарушено допущение SUTVA — лечится сменой единицы рандомизации на город или кластер графа); слишком крупная единица (цена на всю страну, ребрендинг: пользователей миллион, независимых единиц одна); этика и обязательства (нельзя случайной половине показывать худшую цену); длинный горизонт (эффект на годовое удержание не померить за две недели — см. метрики и закон Гудхарта); событие уже произошло (конкурент поднял цены, вышло регулирование). Отдельный случай — редкий исход: при конверсии 0,05 % эксперимент технически возможен, но длится год, и вместо квазиэксперимента нужна суррогатная метрика с честной оговоркой, что суррогат проверялся отдельно.
Разность разностей
Самый рабочий инструмент, когда есть похожая нетронутая группа. Запустили новый тариф доставки в одном городе; есть второй, сопоставимый по размеру, где ничего не меняли.
| До (заказов/сутки) | После | Изменение | |
|---|---|---|---|
| Тестовый город | 100 | 130 | +30 |
| Контрольный город | 80 | 96 | +16 |
Наивное «до и после» даёт +30, то есть +30 %. Но контрольный город, где ничего не запускали, вырос на +16 — это сезон, реклама, погода, весь фон разом. Вмешательству принадлежит разность разностей:
$$\hat{\tau}{\mathrm{DiD}} = \big(\bar{Y}^{T}{\text{после}} - \bar{Y}^{T}{\text{до}}\big) - \big(\bar{Y}^{C}{\text{после}} - \bar{Y}^{C}_{\text{до}}\big) = (130 - 100) - (96 - 80) = +14$$
Контрфактическая оценка для тестового города: $100 + 16 = 116$ заказов; наблюдали 130; эффект +14 заказов в сутки.
WITH daily AS (
SELECT city, order_date, COUNT(*) AS orders
FROM orders
WHERE order_date >= DATE '2026-05-04'
AND order_date < DATE '2026-06-29'
AND city IN ('Тестовый', 'Контрольный')
GROUP BY city, order_date
),
cell AS (
SELECT city,
(order_date >= DATE '2026-06-01') AS is_post,
AVG(orders) AS avg_orders
FROM daily
GROUP BY city, (order_date >= DATE '2026-06-01')
)
SELECT ROUND((
(MAX(avg_orders) FILTER (WHERE city = 'Тестовый' AND is_post)
- MAX(avg_orders) FILTER (WHERE city = 'Тестовый' AND NOT is_post))
- (MAX(avg_orders) FILTER (WHERE city = 'Контрольный' AND is_post)
- MAX(avg_orders) FILTER (WHERE city = 'Контрольный' AND NOT is_post))
)::numeric, 2) AS did_orders_per_day
FROM cell;
Запрос даёт точечную оценку и ни слова о неопределённости. Для интервала нужна регрессионная форма $Y_{it} = \alpha + \beta T_i + \gamma P_t + \tau (T_i \cdot P_t) + \varepsilon_{it}$, где $T_i$ — принадлежность к тестовой группе, $P_t$ — период «после»; коэффициент при взаимодействии $\tau$ и есть DiD-оценка, а его стандартная ошибка — то, что нужно для интервала (см. линейную и логистическую регрессию).
Критично: ошибки надо кластеризовать по единице рандомизации (по городу), а не по дням. Наблюдения внутри города коррелированы, и обычная стандартная ошибка занижена в разы — тот же дефект «единица анализа ≠ единица рандомизации», что и в главе про проверку гипотез. Классический разбор — Bertrand, Duflo, Mullainathan, «How Much Should We Trust Differences-in-Differences Estimates?» (Quarterly Journal of Economics, 2004): без кластеризации доля ложных срабатываний доходит до 45 % вместо 5 %. Следствие: два города — это выборка размера два, интервал будет широчайшим, и это честно.
Допущение параллельных трендов и как его щупать
Вся оценка держится на одном утверждении: без вмешательства тестовый город повторил бы динамику контрольного. Проверить его нельзя — контрфактического мира нет. Проверить можно только следствия:
- Предтренды. Постройте те же линии за 8–12 периодов до запуска. Расходятся до вмешательства — допущение уже под вопросом.
- Плацебо-тест. Прогоните тот же запрос, сдвинув «дату запуска» на месяц назад, в период, когда ничего не происходило. «Эффект» появился и там — метод ловит расхождение групп, а не вмешательство.
- Негативный контроль и второй контроль. Метрика, на которую вмешательство влиять не может (доставка изменилась — а конверсия в регистрацию?), и третий город вместо второго. Сдвинулась негативная метрика — сдвинулось что-то общее; разные контроли дают разный ответ — вы измеряете выбор контроля.
Ни одна проверка не доказывает допущение. Все вместе снижают шанс, что вы публикуете артефакт.
Аддитивно или мультипликативно — ответы разные
Тонкость, которую пропускают почти все: параллельность трендов может выполняться в абсолютных величинах или в относительных, но обычно не в обеих сразу.
- Аддитивно: контроль вырос на +16, контрфактический уровень $100 + 16 = 116$, эффект +14 заказов.
- Мультипликативно: контроль вырос в $96/80 = 1{,}2$ раза, контрфактический уровень $100 \cdot 1{,}2 = 120$, эффект +10 заказов, то есть $130/120 - 1 = +8{,}3%$.
Одни и те же четыре числа, две легитимные версии DiD, разница в оценке — 40 %. Выбор шкалы — часть допущения, а не техническая деталь, и объявлять его надо до расчёта. Практическое правило: если метрика — счётчик или деньги и группы разного масштаба, логарифмируйте (DiD считается по $\ln Y$ и читается как проценты). Почему для правых хвостов логарифмическая шкала естественна — глава про распределения.
Синтетический контроль
Что делать, если ни один город не похож на тестовый? Собрать похожий из нескольких. Синтетический контроль (Абади) строит взвешенную комбинацию доступных контрольных единиц так, чтобы она максимально совпадала с тестовой до вмешательства, и дальше сравнивает с ней. Тот же приём в форме временного ряда — CausalImpact от Google: байесовская структурная модель учится на динамике до вмешательства и на не затронутых рядах-предикторах, прогнозирует контрфактический ряд и считает накопленное отклонение с интервалом. Работает, когда есть достаточно длинный «до» (минимум несколько сезонных циклов), есть предикторы, на которые вмешательство точно не повлияло (иначе прогноз впитает эффект), и эффект велик на фоне обычной волатильности ряда. Про сами модели — глава временные ряды.
Разрывный дизайн: пороги, которые уже есть в вашем продукте
Самое недооценённое: в любом продукте полно правил вида «если величина превысила порог, делаем X». Бесплатная доставка от 3000 рублей. Звонок клиентам с риск-скором выше 0,7. Премиум-статус от 12 заказов. Каждый такой порог — встроенный естественный эксперимент.
Логика: пользователи со скором 0,699 и 0,701 практически неразличимы, а обращаются с ними по-разному. Значит, в узкой окрестности порога назначение воздействия почти случайно:
$$\hat{\tau}{\mathrm{RDD}} = \lim{x \downarrow c} \mathbb{E}(Y \mid X = x) - \lim_{x \uparrow c} \mathbb{E}(Y \mid X = x)$$
-- Порог 0,70: выше — звонок менеджера. Смотрим узкую полосу вокруг порога.
SELECT
(s.risk_score >= 0.70) AS got_call,
COUNT(*) AS users,
ROUND(AVG(s.risk_score)::numeric, 4) AS avg_score,
ROUND(100 * AVG(r.renewed::int)::numeric, 2) AS renewed_pct
FROM churn_scores AS s
JOIN renewals AS r USING (user_id)
WHERE s.scored_on = DATE '2026-06-01'
AND s.risk_score BETWEEN 0.65 AND 0.75
GROUP BY (s.risk_score >= 0.70)
ORDER BY got_call;
Что обязательно проверить:
- Сбалансированность на границе. Средний скор слева и справа должен отличаться чуть-чуть (0,675 против 0,725), а ковариаты — не отличаться вовсе. Отличаются — порог совпал с чем-то ещё.
- Отсутствие манипуляции. Постройте гистограмму самой пороговой величины с мелким шагом. «Горб» у порога (тест Маккрери) означает, что кто-то подкручивает: пользователи добирают корзину до 3000 рублей, менеджеры округляют скор. Такой RDD мёртв. Пороги на внутренних скорах моделей, которых пользователь не видит, — самые чистые.
- Ширина полосы. Узкая — мало данных, широкая — в оценку заползает общий тренд. Показывайте оценку для нескольких ширин: скачет — вы смотрите на тренд, а не на разрыв.
Главное ограничение: RDD даёт эффект только у порога. «Звонок помогает тем, у кого скор около 0,7» не переносится на скор 0,95. Это ответ на локальный вопрос — но локальный вопрос обычно и есть тот, который вы решаете («сдвинуть ли порог до 0,65?»).
Инструментальные переменные и encouragement design
Фичу нельзя выдать принудительно — пользователь сам решает, включать ли двухфакторную аутентификацию. Но можно рандомизировать приглашение: рассылаем пуш «включите 2FA» случайной половине базы.
| Пользователей | Включили 2FA | Конверсия в продление | |
|---|---|---|---|
| Получили пуш ($Z=1$) | 50 000 | 40 % | 8,0 % |
| Не получили ($Z=0$) | 50 000 | 10 % | 7,4 % |
ITT (intention-to-treat) — эффект самого приглашения: $8{,}0 - 7{,}4 = 0{,}6$ п.п. Это честная, полностью экспериментальная величина, и часто именно она отвечает на вопрос бизнеса «стоит ли рассылать пуш». LATE — эффект самой фичи на тех, кто включил её из-за пуша; оценка Уолда:
$$\hat{\tau}_{\mathrm{LATE}} = \frac{\mathbb{E}(Y \mid Z=1) - \mathbb{E}(Y \mid Z=0)}{\mathbb{E}(D \mid Z=1) - \mathbb{E}(D \mid Z=0)} = \frac{0{,}006}{0{,}40 - 0{,}10} = 0{,}02$$
То есть +2 процентных пункта для «податливых» — тех, кто включает 2FA только после пуша. Не для всех и не для энтузиастов, включивших бы и так, — только для этой прослойки.
| Допущение | Что означает | Как ломается на практике |
|---|---|---|
| Релевантность | Инструмент реально двигает воздействие | Пуш сдвинул на 2 п.п. — знаменатель крошечный, оценка разлетается |
| Исключение | Инструмент влияет на исход только через воздействие | Пуш сам напомнил о продукте. Ломается почти всегда |
| Независимость | Инструмент назначен случайно | Отправляли только тем, у кого включены пуши, — это уже отбор |
| Монотонность | Нет тех, кто от пуша включает реже | Раздражение от навязчивого пуша — реальный механизм |
Самое слабое место — исключение. Любое коммуникационное «приглашение» само по себе действие: письмо возвращает пользователя в продукт независимо от того, включил он фичу. Поэтому в продуктовой аналитике честнее отчитываться по ITT, а LATE показывать как «верхнюю границу того, что могла бы дать сама фича». За теорию LATE Ангрист и Имбенс получили Нобелевскую премию 2021 года; практическое изложение — Angrist, Pischke, Mostly Harmless Econometrics.
Контроль конфаундеров: матчинг, регрессия, взвешивание
Нижняя часть лестницы: рандомизации нет, естественного эксперимента нет, есть только таблица с ковариатами. Работаем с тем, что есть, — и предельно ясно проговариваем цену.
Склонность к воздействию (propensity score) — вероятность попасть в группу воздействия при данных ковариатах, $e(X) = \Pr(D = 1 \mid X)$. Ключевое свойство: если условная независимость выполняется по $X$, она выполняется и по одному числу $e(X)$, то есть многомерное сравнение сводится к одномерному. Дальше три способа применения: матчинг (каждому обработанному подбирается контроль с близким скором), стратификация (5–10 корзин по скору, эффект внутри каждой, усреднение с весами) и взвешивание (IPW), где наблюдение входит с весом, обратным вероятности его собственного статуса:
$$\hat{\tau}{\mathrm{IPW}} = \frac{1}{n}\sum{i=1}^{n} \left( \frac{D_i Y_i}{e(X_i)} - \frac{(1 - D_i) Y_i}{1 - e(X_i)} \right)$$
Псевдокод матчинга и его стоимость:
вход: обработанные T, контрольные C, скор e(·), допуск caliper
1. отсортировать C по e -> O(|C| log |C|)
2. для каждого t из T:
бинарным поиском найти ближайший c -> O(log |C|)
если |e(t) - e(c)| > caliper: отбросить t (нет перекрытия!)
иначе: записать пару (t, c)
3. эффект = среднее по парам (y_t - y_c)
Итог: $O\big((|T| + |C|) \log |C|\big)$ по времени и $O(|C|)$ по памяти — против $O(|T| \cdot |C|)$ у наивного перебора. Здесь ломаются три вещи.
Позитивность (перекрытие). Если при некоторых $X$ воздействие получают все или никто, контрфактического наблюдения для этих $X$ нет физически. В IPW это проявляется как вес $1/e$ при $e = 0{,}002$: одно наблюдение получает вес 500 и в одиночку определяет ответ. Обязательная диагностика — гистограммы скора в обеих группах должны перекрываться; хвосты обрезать и честно писать, что оценка теперь про подпопуляцию.
Только измеренные конфаундеры. Это не техническое ограничение, а математическое: скор строится из колонок, которые есть в таблице, а мотивация, срочность потребности и разговор с другом — не колонки. Матчинг не приближает к рандомизации, он лишь аккуратно применяет допущение, которое вы всё равно приняли. Классическая демонстрация — Robert LaLonde (American Economic Review, 1986): наблюдательные оценки эффекта программы трудоустройства расходились с экспериментальными в разы и меняли знак.
Сам матчинг по скору небезопасен. Gary King, Richard Nielsen, «Why Propensity Scores Should Not Be Used for Matching» (Political Analysis, 2019): PSM способен ухудшать баланс и добавлять произвол в оценку. Практический вывод — предпочитать матчинг по самим ковариатам или стратификацию, а скор использовать для диагностики перекрытия. Инструменты, где всё реализовано: DoWhy (граф → идентификация → оценка → тесты на устойчивость) и EconML. Ценность DoWhy не в оценщиках, а в том, что он заставляет записать граф и прогнать опровергающие проверки.
Когда причинность недоступна вовсе
Иногда правильный технический ответ — «этот вопрос на этих данных не решается». Это не поражение: это результат, экономящий месяцы работы над оценкой, которой нельзя пользоваться. Признаки, что вы в этой зоне: воздействие получили те, кто сам захотел, а причина желания не измерена; нет ни одной группы и ни одного периода без воздействия; воздействие совпало по времени с ещё тремя изменениями (релиз плюс маркетинг плюс сезон); ключевая колонка появилась в логах позже воздействия; «контрольная группа» получена фильтром по величине, зависящей от исхода.
1. Ограничить утверждение до того, что данные выдерживают. Не «фича повышает удержание на 12 %», а «пользователи фичи удерживаются на 12 % лучше; отбор в группу не случаен, эффект самой фичи может быть от нуля до 12 %».
2. Триангуляция и негативные контроли. Соберите несколько оценок с разными по природе смещениями: наблюдательная корректировка, DiD по регионам с разной датой раскатки, качественные интервью (см. сбор требований и пользовательские исследования). Совпали — уверенность растёт; разошлись — вы узнали важное. Плюс исход, на который воздействие не может влиять физически: «новый экран оплаты повысил конверсию — а заодно и долю пользователей из Германии?» Значит, поменялся трафик, а не экран.
4. Анализ чувствительности. Вместо «может, есть конфаундер» — «насколько сильным он должен быть, чтобы объяснить наблюдаемое». E-value (VanderWeele, Ding, Annals of Internal Medicine, 2017) для отношения рисков $\mathrm{RR} \ge 1$ равно $E = \mathrm{RR} + \sqrt{\mathrm{RR} \cdot (\mathrm{RR} - 1)}$:
| Наблюдаемое RR | E-value | Как читать |
|---|---|---|
| 1,2 | 1,69 | Хватит умеренного конфаундера — вывод хрупкий |
| 1,5 | 2,37 | Нужен заметный, но правдоподобный |
| 2,0 | 3,41 | Нужен очень сильный неучтённый фактор |
| 3,0 | 5,45 | Такой конфаундер трудно не заметить |
Читается так: чтобы объяснить RR = 2 одним лишь смещением, неучтённый фактор должен быть связан и с воздействием, и с исходом с отношением рисков не менее 3,41 каждое — сверх всего, что вы уже учли. Для $\mathrm{RR} < 1$ сначала берут $1/\mathrm{RR}$. Калькулятор и разбор — evalue-calculator.com. Это не превращает корреляцию в причинность, но переводит спор из «мне кажется» в «назовите фактор такой силы».
5. Разделить прогноз и вмешательство. Огромный источник путаницы. Для предсказания корреляции достаточно: признак «зашёл в раздел отмены подписки» превосходно предсказывает отток. Для вмешательства он бесполезен — закрыв раздел, вы не удержите никого. Модель ранжирует, а не объясняет; путать это — значит строить продуктовые решения на признаках-симптомах (обзор машинного обучения).
Девять признаков Брэдфорда Хилла (сила связи, воспроизводимость, специфичность, временна́я последовательность, доза-эффект, правдоподобный механизм, согласованность со знанием, экспериментальные данные, аналогия) — не чек-лист для галочки, а список вопросов, которые стоит задать себе до того, как слово «влияет» попадёт в письмо руководителю.
Причинность закладывается заранее, а не восстанавливается потом
Главный практический вывод главы адресован инженеру. Почти всё, что делает причинный вывод возможным, стоит копейки — если сделано до запуска, а не после.
- Постоянный холдаут. 1 % пользователей, не получающих новые фичи. Дорого политически, бесценно аналитически: через год это единственная группа, по которой видно, сколько дал весь год работы.
- Логировать назначение, а не только результат. Событие «пользователю назначен вариант B, seed такой-то, в момент такой-то» — это и есть ваш $D$. Восстанавливать его потом по косвенным признакам — гарантированное смещение.
- Ступенчатая раскатка вместо мгновенной. Раскатка по регионам с разными датами превращается в staggered DiD; мгновенная глобальная не оставляет ничего.
- Хранить пороги и календарь изменений как данные. Порог 0,7 менялся трижды за год — без истории порогов RDD не построить. Календарь релизов, кампаний и внешних событий превращает «метрика скакнула 14 мая» в «14 мая было три изменения, разделить нельзя» (см. моделирование данных).
- Не удалять сырые события. Агрегат за сутки убивает порядок событий, а с ним — половину возможностей проверить направление связи.
Жизненный цикл причинного утверждения полезно вести явно, как статус в трекере:
Смысл диаграммы: утверждение «X влияет на Y» имеет статус, и статус меняется. Замороженное — не выброшенное, это очередь на проверку, которая разберётся, как только появится подходящая раскатка.
Разница между аналитиком, которому верят, и аналитиком, которого перепроверяют, — в одном абзаце отчёта на четыре строки: число с интервалом («эффект +14 заказов в сутки, 95 %-й интервал от +3 до +25»), откуда взялось сравнение («DiD, контроль — Тверь, 4 недели до и 4 после»), допущение, на котором всё держится («без запуска динамика городов совпала бы; предтренды параллельны, плацебо-тест на май эффекта не дал») и что бы вывод опровергло («если в Твери в июне была своя акция, оценка завышена; проверяем по календарю маркетинга»). Четвёртая строка — самая ценная и самая редкая: она превращает отчёт из позиции в проверяемое утверждение. Про то, как из вопроса вырастает решение, — глава сначала вопрос; про то, как показывать неопределённость на графике, не превращая её в украшение, — визуализация.
Типичные ошибки
| Ошибка | Почему возникает | Что делать |
|---|---|---|
| «Пользователи фичи покупают чаще → фича работает» | Фичу включают самые активные | Разложить на ATT и смещение отбора; искать порог, холдаут, приглашение |
| Контроль на медиатор | «Учли всё, что влияет на исход» | Не контролировать ничего, что произошло после воздействия |
| Фильтр по величине, зависящей от исхода | Кажется разумным сузить выборку | Проверить: не является ли фильтр общим следствием X и Y |
| Сравнение «до и после» без контроля | Данных всегда хватает | Найти нетронутую группу или период; иначе сказать «эффект неотделим от фона» |
| DiD без предтрендов, плацебо и кластеризации | Метод дал число, число ушло в отчёт | Проверки обязательны; кластеризовать по единице рандомизации, помнить, что два города — это n = 2 |
| Матчинг подан как «почти эксперимент» | Красивая процедура внушает доверие | Написать список неизмеренных конфаундеров, посчитать E-value |
| Экстраполяция RDD за пределы порога или LATE как эффект «на всех» | Одно число выглядит универсальным | Эффект локален по построению; отчитываться по ITT и называть, кто такие податливые |
| Причинный вывод из важности признака модели | Важность выглядит как влияние | Важность — про предсказание; для вмешательства нужен дизайн |
| «Это всего лишь корреляция» вместо оценки | Кажется профессиональной осторожностью | Дать оценку с границами и назвать, что её опровергнет |
Мини-итог
- Причинный эффект по определению контрфактичен и на уровне единицы не наблюдаем никогда. Оценивать можно только средние — и только через дизайн, а не через формулу.
- Наблюдаемая разница = эффект + смещение отбора. Рандомизация обнуляет второе слагаемое; все методы главы — попытки обнулить его без рандомизации, и каждая стоит непроверяемого допущения.
- Контроль третьей переменной помогает только для конфаундера. Для медиатора он уничтожает эффект, для коллайдера — создаёт несуществующий. «Добавим побольше контролей» — не осторожность, а источник ошибок.
- Любой
WHEREпо величине, зависящей от исхода, способен породить связь из ничего. Корреляция −0,5 получается из двух независимых признаков и одного правила найма. - Парадокс Симпсона решается стандартизацией, и она пишется обычным SQL — но только после того, как вы поняли, почему группы неодинаковы.
- DiD стоит на параллельных трендах, которые нельзя доказать. Предтренды, плацебо-тест, негативный контроль и второй контроль — минимальный набор; шкала (уровни или логарифмы) объявляется заранее и меняет ответ на десятки процентов.
- Пороги в вашем продукте — готовые естественные эксперименты, если пользователь не может их подкрутить. Эффект при этом локален, и это нормально: решения о порогах тоже локальны.
- ITT честнее LATE, а матчинг не заменяет рандомизацию. Рандомизировать приглашение можно почти всегда, но допущение об исключении в продукте почти всегда нарушено; матчинг же лишь аккуратно применяет допущение «все конфаундеры измерены», которое обычно ложно.
- «Этими данными на этот вопрос ответить нельзя» — валидный результат. Вместе с границами, чувствительностью и списком того, что изменило бы вывод, он полезнее уверенного числа из ниоткуда.
Дополнительное чтение: Scott Cunningham, Causal Inference: The Mixtape — бесплатный учебник с кодом на R, Stata и Python, лучший вход в тему для практика; Nick Huntington-Klein, The Effect — та же территория с упором на интуицию и графы, тоже бесплатно; Miguel Hernán, James Robins, Causal Inference: What If — строгий и бесплатный стандарт эпидемиологии; Judea Pearl, Dana Mackenzie, The Book of Why — популярное изложение языка графов; Ronald Kohavi, Diane Tang, Ya Xu, Trustworthy Online Controlled Experiments — про то, как в индустрии всё-таки добираются до верхней ступеньки лестницы.
Что дальше
Когорты и удержание: как читать кривые и не обмануться — почему кривая удержания почти всегда «выравнивается», даже когда никто не выравнивается; чем когорта отличается от среза; как эффект состава превращает падение продукта в рост метрики; и почему сравнение когорт разных месяцев — тот же самый вопрос про конфаундеры, только с осью времени вместо платформы.