Фрея · риск-менеджер необанка «Монетка»
С 1 апреля половина клиентов получает 5% кешбэка в кафе. Продуктовая команда уже кричит «траты выросли на 11%!». Я не верю никому, пока не проверю сам тест. Он вообще честно устроен?
Мы продолжаем с базой bank из прошлого урока и Фреей, которая не принимает цифры на веру. В «Монетке» идёт эксперимент: с 1 апреля 2026 клиенты группы test получают 5% кешбэка в кафе и ресторанах (MCC 5812 и 5814) сверх обычного 1%, группа control только обычный 1%. Кто в какой группе, записано в cashback_groups. Окупился ли кешбэк, Фрея решит в финале курса. Сегодня проверяем, можно ли вообще доверять тесту, и считаем первую метрику.
Две половины поля
Агроном хочет проверить удобрение. Он не сравнивает этот год с прошлым: погода другая. Он делит одно поле на две половины, удобряет одну и сравнивает урожай в тот же год. А чтобы половины были похожи, делит не «северную и южную», а грядки вперемешку, по жребию.
A/B-тест устроен так же. Дизайн теста отвечает на пять вопросов до старта:
- Гипотеза: что изменится. «Кешбэк 5% в кафе приведёт клиентов в кафе чаще».
- Метрика: чем меряем. Одна главная, решённая заранее.
- Рандомизация: клиентов делят жребием, а не по городу или дате регистрации.
- Размер и срок: сколько клиентов и дней нужно, чтобы заметить ожидаемый эффект.
- Проверки: что посмотрим до выводов, чтобы поймать сломанный тест.
Проверка 1: каждый в одной группе
Клиент должен видеть ровно один вариант. Посмотрим, так ли это:
SELECT client_id, count(*)
FROM cashback_groups
GROUP BY client_id
HAVING count(*) > 1;
30 клиентов записаны в обе группы: сбой распределения выдал им второй вариант через несколько часов после первого. Какой кешбэк они видели, неизвестно, поэтому их исключают из анализа целиком, из обеих групп сразу.
WITH dual AS (
SELECT client_id FROM cashback_groups GROUP BY client_id HAVING count(*) > 1
)
SELECT test_group, count(*) AS clients
FROM cashback_groups
WHERE client_id NOT IN (SELECT client_id FROM dual)
GROUP BY test_group;
После чистки 976 клиентов в test и 994 в control.
Проверка 2: сплит 50 на 50
Задумывали поровну. 976 из 1970 это 49,54%. Случайный перекос или сломанное распределение? Перекос размеров групп называют SRM, и его проверяют тем же z, что и доли, о нём ниже. Здесь z = −0,41: в пределах случайности, сплит в порядке.
Проверка 3: группы похожи до теста
Если жребий честный, до 1 апреля группы должны вести себя одинаково. Возьмём долю клиентов, которые хоть раз платили в кафе в январе-марте: 84,84% в test и 86,32% в control. Разница в полтора пункта, и она в пользу контроля. Запомним: тестовая группа стартовала не с форой.
Метрика: кто дошёл до кафе
Главная метрика нашего разбора: доля клиентов группы, которые за время теста хоть раз заплатили в кафе. Для каждого клиента ставим флаг через EXISTS: так клиент без единой операции останется в знаменателе с false, а не выпадет из JOIN.
WITH dual AS (
SELECT client_id FROM cashback_groups GROUP BY client_id HAVING count(*) > 1
), groups AS (
SELECT client_id, test_group FROM cashback_groups
WHERE client_id NOT IN (SELECT client_id FROM dual)
), flags AS (
SELECT g.test_group,
EXISTS (
SELECT 1 FROM transactions t JOIN cards c ON c.id = t.card_id
WHERE c.client_id = g.client_id AND t.status = 'approved'
AND t.mcc IN (5812, 5814) AND t.created_at >= '2026-04-01'
) AS converted
FROM groups g
)
SELECT test_group, count(*) AS clients,
count(*) FILTER (WHERE converted) AS converted,
round(avg(converted::int), 4) AS conversion
FROM flags
GROUP BY test_group;
test_group | clients | converted | conversion
------------+---------+-----------+------------
control | 994 | 870 | 0.8753
test | 976 | 881 | 0.9027
Разница 2,74 процентного пункта в пользу теста. Осталось понять, не шум ли это.
урок из курса
Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.