Как посчитать Uplift в SQL
Содержание:
Зачем Uplift
В A/B-тесте Treatment группа показала CR 5%, Control — 4%. Uplift = +1 п.п. (absolute) или +25% (relative). Какую цифру показывать команде? Зависит от контекста.
Uplift — основной способ выразить разницу в A/B. В статье — SQL и нюансы.
Absolute vs Relative Uplift
Absolute Uplift = CR_treatment - CR_control
Relative Uplift = (CR_treatment - CR_control) / CR_control × 100%Пример: CR control 2%, treatment 3%.
- Absolute: +1 п.п.
- Relative: +50%
Relative выглядит больше, но обманчив на низких базах. Используйте обе.
Базовый расчёт
Данные: ab_assignments(user_id, group_name, exp_id), events(user_id, event_type).
WITH stats AS (
SELECT
a.group_name,
COUNT(DISTINCT a.user_id) AS users,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN a.user_id END) AS conversions
FROM ab_assignments a
LEFT JOIN events e ON e.user_id = a.user_id
WHERE a.exp_id = 'check_out_v2'
GROUP BY a.group_name
)
SELECT
group_name,
users,
conversions,
conversions::NUMERIC / NULLIF(users, 0) * 100 AS cr_pct
FROM stats;Затем для uplift:
WITH cr AS (
SELECT
group_name,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN a.user_id END)::NUMERIC
/ NULLIF(COUNT(DISTINCT a.user_id), 0) AS cr
FROM ab_assignments a
LEFT JOIN events e ON e.user_id = a.user_id
WHERE a.exp_id = 'check_out_v2'
GROUP BY a.group_name
)
SELECT
(SELECT cr FROM cr WHERE group_name = 'treatment') - (SELECT cr FROM cr WHERE group_name = 'control') AS absolute_uplift,
((SELECT cr FROM cr WHERE group_name = 'treatment') / NULLIF((SELECT cr FROM cr WHERE group_name = 'control'), 0) - 1) * 100 AS relative_uplift_pct;Uplift по сегментам
WITH cr_seg AS (
SELECT
a.group_name,
u.platform,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN a.user_id END)::NUMERIC
/ NULLIF(COUNT(DISTINCT a.user_id), 0) AS cr
FROM ab_assignments a
JOIN users u ON u.user_id = a.user_id
LEFT JOIN events e ON e.user_id = a.user_id
WHERE a.exp_id = 'check_out_v2'
GROUP BY a.group_name, u.platform
)
SELECT
platform,
MAX(CASE WHEN group_name = 'control' THEN cr END) AS cr_control,
MAX(CASE WHEN group_name = 'treatment' THEN cr END) AS cr_treatment,
MAX(CASE WHEN group_name = 'treatment' THEN cr END)
- MAX(CASE WHEN group_name = 'control' THEN cr END) AS abs_uplift
FROM cr_seg
GROUP BY platform;Confidence interval
WITH stats AS (
SELECT
group_name,
COUNT(DISTINCT a.user_id) AS n,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN a.user_id END) AS k
FROM ab_assignments a
LEFT JOIN events e ON e.user_id = a.user_id
WHERE a.exp_id = 'check_out_v2'
GROUP BY a.group_name
)
SELECT
group_name,
n,
k,
k::NUMERIC / n AS p,
1.96 * SQRT(k::NUMERIC / n * (1 - k::NUMERIC / n) / n) AS ci_radius
FROM stats;95% CI для CR: p ± ci_radius. Если CI control и treatment не пересекаются — uplift статистически значим (грубый proxy для z-test).
Частые ошибки
Ошибка 1. Считать relative на низкой базе. CR 0,1% → 0,15% = +50% relative. Звучит огромно, но абсолютно — +0,05 п.п.
Ошибка 2. Считать без statistical significance. Uplift 2% при n=100 — может быть шумом. Нужен p-value / CI.
Ошибка 3. Игнорировать SRM. Перед uplift проверьте sample ratio — если control 48% / treatment 52% при 50/50 целевом, эксперимент сломан.
Ошибка 4. Кумулятивный vs final uplift. В A/B uplift первой недели может отличаться от финального. Не вытаскивайте preliminary results.
Ошибка 5. Несколько метрик. Считаете uplift по CR + AOV + revenue. Multiple comparisons → false positives. Используйте Bonferroni / sequential testing.
Ошибка 6. Negative uplift. Treatment работает хуже control — это валидный результат. Не игнорируйте.
Связанные темы
FAQ
Absolute или relative?
Absolute — если база известна. Relative — если хотите подчеркнуть рост (но на низких базах обманчиво).
Какой uplift считается большим?
В CR-метриках +5-10% relative — нормальный. +20%+ — большой, и часто шум.
Negative uplift — что делать?
Не катить treatment. Понять, почему хуже control.
Uplift зависит от MDE?
Да. Запустите A/B на меньшем эффекте, чем MDE, и он будет в шуме.
Можно ли uplift в SQL для long-term?
Стандартный — нет. Долгосрочные эффекты считаются через holdout.