Условие
В A/B-тесте мы хотим сравнить CTR = clicks / impressions. Если просто посчитать var(CTR) = sum_clicks / sum_impressions и применить обычную формулу для пропорций, получим неверный CI.
Почему? И что использовать вместо?
Решение
Почему обычная формула не работает
Формула SE(p) = sqrt(p(1-p)/n) работает для биномиальной случайной величины — где n это число независимых бернулли-испытаний. В A/B на user-уровне:
- один user может иметь много impressions,
- impressions внутри одного user коррелируют (один и тот же контекст),
- единицы рандомизации — юзеры, не показы.
sum_clicks / sum_impressions — это ratio двух случайных величин, не одна биномиальная. Его дисперсия по простой формуле занижается → CI узкий → ложные positive.
Решение — delta method
Если X = sum_clicks per user, Y = sum_impressions per user:
CTR ≈ E[X] / E[Y] = X̄ / Ȳ
Дисперсия отношения через delta method (первый порядок Taylor):
Var(X̄/Ȳ) ≈ (1/Ȳ²) * Var(X̄) − 2 * (X̄/Ȳ³) * Cov(X̄, Ȳ) + (X̄²/Ȳ⁴) * Var(Ȳ)
Или эквивалентно через формулу для отношения:
Var(X̄/Ȳ) ≈ (X̄/Ȳ)² * (Var(X̄)/X̄² − 2*Cov(X̄,Ȳ)/(X̄*Ȳ) + Var(Ȳ)/Ȳ²) / n
На SQL уровне
WITH user_stats AS (
SELECT user_id, variant,
SUM(clicks) AS x,
SUM(impressions) AS y
FROM events
GROUP BY user_id, variant
),
agg AS (
SELECT variant,
AVG(x) AS x_bar, AVG(y) AS y_bar,
VAR_SAMP(x) AS vx, VAR_SAMP(y) AS vy,
COVAR_SAMP(x, y) AS cxy,
COUNT(*) AS n
FROM user_stats GROUP BY variant
)
SELECT variant,
x_bar / y_bar AS ctr,
SQRT(
(vx / (y_bar * y_bar)
- 2 * x_bar * cxy / POWER(y_bar, 3)
+ x_bar * x_bar * vy / POWER(y_bar, 4)
) / n
) AS se_ctr
FROM agg;Альтернатива — bootstrap
Если delta method кажется сложным или распределение скошено — bootstrap по юзерам:
- Семплируем юзеров с возвратом N раз.
- Считаем CTR на каждом семпле.
- Распределение CTR из бутстрапа → CI как 2.5–97.5 перцентили.
В A/B это сейчас стандарт у Meta / Booking — bootstrap дешевле, корректнее, не требует delta-математики.
Подводные камни
- «n» путаница. В формуле n — число юзеров, не impressions. Сильно влияет на величину SE.
- Cov(X, Y). Часто > 0 (юзер с многими impressions имеет и много clicks). Игнорируя её — переоцениваем variance.
- Длинный хвост. Если 1% пользователей дают 50% impressions — bootstrap намного устойчивее, чем delta-method-Wald.
- Tarpaulin shifting. Если меняется состав юзеров между группами (mix shift), даже корректная вариативность не спасёт от bias оценки.
Эталонный ответ
CTR в A/B — это ratio с корреляцией внутри юзера. Обычная биномиальная SE неверна (занижена). Использовать delta method для SE или bootstrap по юзерам для CI. Это caveat одного из самых частых сценариев в продуктовых A/B-тестах.