Условие
Таблица experiment(user_id, variant text, has_purchase boolean). Посчитайте:
- конверсию в покупку в группах A и B,
- абсолютный uplift в п.п.,
- относительный lift в процентах,
- общее число пользователей в каждой группе.
Решение
Прямой запрос
WITH agg AS (
SELECT
variant,
COUNT(*) AS users,
SUM(CASE WHEN has_purchase THEN 1 ELSE 0 END) AS buyers,
AVG(CASE WHEN has_purchase THEN 1.0 ELSE 0.0 END) AS cr
FROM experiment
GROUP BY variant
)
SELECT
MAX(CASE WHEN variant = 'A' THEN users END) AS users_a,
MAX(CASE WHEN variant = 'B' THEN users END) AS users_b,
MAX(CASE WHEN variant = 'A' THEN buyers END) AS buyers_a,
MAX(CASE WHEN variant = 'B' THEN buyers END) AS buyers_b,
ROUND(100 * MAX(CASE WHEN variant = 'A' THEN cr END)::numeric, 3) AS cr_a_pct,
ROUND(100 * MAX(CASE WHEN variant = 'B' THEN cr END)::numeric, 3) AS cr_b_pct,
ROUND(100 * (MAX(CASE WHEN variant = 'B' THEN cr END)
- MAX(CASE WHEN variant = 'A' THEN cr END))::numeric, 3) AS uplift_pp,
ROUND(100 *
(MAX(CASE WHEN variant = 'B' THEN cr END)
- MAX(CASE WHEN variant = 'A' THEN cr END))
/ NULLIF(MAX(CASE WHEN variant = 'A' THEN cr END), 0)::numeric, 2) AS lift_pct
FROM agg;Z-критерий для двух пропорций — прямо в SQL
WITH agg AS (...)
SELECT
(cr_b - cr_a)
/ SQRT(p*(1-p)*(1.0/n_a + 1.0/n_b)) AS z_stat
FROM (
SELECT
MAX(cr) FILTER (WHERE variant='A') AS cr_a,
MAX(cr) FILTER (WHERE variant='B') AS cr_b,
SUM(users) FILTER (WHERE variant='A') AS n_a,
SUM(users) FILTER (WHERE variant='B') AS n_b,
(SUM(buyers))::numeric / SUM(users) AS p
FROM agg
) t;p > 1.96 → отвергаем H₀ на уровне 5% (двусторонне).
Подводные камни
- Lift vs uplift. Разные люди путают: uplift — абсолютная разность пропорций (п.п.), lift — относительная (раз/процентов). Уточняйте.
- Деление целых.
2/10 = 0в Postgres. Везде кастуйте::numericили умножайте на1.0. - Один пользователь в двух группах. Если попал и в A, и в B — должен быть исключён или приписан к одной группе при назначении.
- Бакетирование (sticky-assignment): проверьте, что один user_id всегда видит один variant.
COUNT(*)vsCOUNT(DISTINCT user_id). Если в таблице по строке на сессию — беритеCOUNT(DISTINCT user_id).- Стат. значимость. Lift без p-value и доверительного интервала — не результат, а наблюдение.
Эталонный ответ
AVG(CASE WHEN has_purchase THEN 1.0 ELSE 0.0 END) — конверсия. Uplift = cr_b - cr_a (п.п.), lift = (cr_b - cr_a)/cr_a (%). Без z-теста или CI результаты не сообщают.