A/B-тестирование и чек-лист аналитика

A/B-тест — это проверка гипотез в чистом виде и самый частый статистический инструмент в продуктовой аналитике. Соберём весь курс воедино.

A/B-тест — контролируемый эксперимент: пользователей случайно делят на группу A (контроль) и группу B (изменение) и сравнивают метрику, чтобы понять, реален ли эффект.

Зачем нужен A/B-тест

Поменяли кнопку — конверсия выросла с 10% до 12%. Это заслуга кнопки или просто случайный шум? Без эксперимента не ответить. A/B-тест случайно распределяет пользователей по группам (это и даёт право говорить о причинности из урока 5.2), а статистика отвечает, значима ли разница.

Считаем разницу конверсий

Допустим, в группе A из 1000 человек купили 100 (10%), в группе B из 1000 — 125 (12.5%). Разница 2.5 процентного пункта. Проверим H₀ «разницы нет, обе группы из одной совокупности».

a_users, a_conv = 1000, 100      # контроль: 10.0%
b_users, b_conv = 1000, 125      # вариант:  12.5%

rate_a = a_conv / a_users
rate_b = b_conv / b_users
print(f"Конверсия A: {rate_a:.1%}")
print(f"Конверсия B: {rate_b:.1%}")
print(f"Абсолютная разница: {(rate_b - rate_a):.1%}")
print(f"Относительный прирост: {(rate_b - rate_a) / rate_a:.1%}")

Вывод:

Конверсия A: 10.0%
Конверсия B: 12.5%
Абсолютная разница: 2.5%
Относительный прирост: 25.0%

p-value через симуляцию перестановок

Оценим, могла ли такая разница (или больше) возникнуть случайно, если на деле группы одинаковы. Идея перестановочного теста: смешаем всех пользователей в один «котёл», много раз случайно переразобьём на две группы того же размера и посмотрим, как часто случайная разница не меньше наблюдаемой.

import random
random.seed(2024)

# Общий котёл: 1000+1000 пользователей, всего 225 покупок (100+125)
pool = [1] * (100 + 125) + [0] * (2000 - 225)
observed_diff = 125 / 1000 - 100 / 1000      # 0.025

trials = 20000
count = 0
for _ in range(trials):
    random.shuffle(pool)
    group_a = pool[:1000]
    group_b = pool[1000:]
    diff = sum(group_b) / 1000 - sum(group_a) / 1000
    if abs(diff) >= observed_diff:
        count += 1

p_value = count / trials
print("Наблюдаемая разница:", observed_diff)
print("p-value (перестановки):", round(p_value, 4))
print("Значимо при alpha=0.05?", p_value < 0.05)

Вывод:

Наблюдаемая разница: 0.024999999999999994
p-value (перестановки): 0.0887
Значимо при alpha=0.05? False

Сюрприз: p-value ≈ 0.08 > 0.05 — разница не значима! Прирост в 25% выглядел убедительно, но при таких размерах групп подобное расхождение вполне может быть случайным. Чтобы уверенно поймать эффект, нужна бóльшая выборка (вспомните урок про мощность).

Типичные ошибки A/B-тестов

  • Подглядывание (peeking). Остановить тест, как только p опустилось ниже 0.05, — почти гарантированная ложная тревога. Размер выборки и срок фиксируют заранее.
  • Слишком маленькая выборка. Низкая мощность — реальный эффект не виден.
  • Много метрик сразу. Проверяя 20 метрик при α = 0.05, в среднем одна «выстрелит» случайно. Нужна поправка на множественность.
  • Путаница значимости и важности. Значимый, но крошечный прирост может не окупать внедрение.

Чек-лист аналитика данных

  1. Посмотрел на данные глазами: типы, пропуски, выбросы, форму распределения?
  2. Выбрал правильную меру центра (медиана при скосе) и привёл меру разброса?
  3. Проверил репрезентативность выборки — нет ли систематического перекоса?
  4. Для связей: не путаю ли корреляцию с причинностью? Есть ли скрытые переменные?
  5. Для выводов: указал неопределённость (доверительный интервал), а не только точечную оценку?
  6. Для эксперимента: хватает ли мощности? Зафиксировал ли размер выборки заранее?
  7. Различаю статистическую значимость и практическую важность?

Что дальше

Вы прошли фундамент: описательная статистика, вероятность, распределения, связи и статистический вывод. Дальше углубляться стоит в t-критерий и χ²-тесты, регрессию со многими переменными, байесовскую статистику и, конечно, инструменты — numpy, pandas, scipy.stats, которые делают всё это на больших данных в одну строку. Но теперь вы понимаете, что именно эти строки считают и где они могут обмануть.

Итог

  • A/B-тест — рандомизированный эксперимент, дающий право на вывод о причинности.
  • Значимость разницы конверсий проверяют тем же аппаратом гипотез и p-value.
  • Заметный на вид прирост может быть незначим при малых группах — нужна мощность.
  • Главные ловушки: подглядывание, малая выборка, множество метрик, подмена важности значимостью.
Проверьте себя
1. Почему A/B-тест позволяет говорить о причинности, а простое наблюдение корреляции — нет?
AПотому что в A/B-тесте больше данных
BПотому что пользователей случайно распределяют по группам, устраняя влияние скрытых переменных
CПотому что A/B-тест всегда даёт p < 0.05
DПотому что в нём не нужна статистика
2. Прирост конверсии выглядит как +25%, но p-value = 0.08 при α = 0.05. Какой вывод верен?
AЭффект точно есть, можно внедрять
BРазница статистически не значима; возможно, не хватает размера выборки
CТест проведён с ошибкой
DНужно снизить конверсию A
3. Что такое «подглядывание» (peeking) в A/B-тесте и чем оно опасно?
AПросмотр данных конкурентов; это незаконно
BОстановка теста сразу, как только p опустилось ниже 0.05; резко повышает риск ложной тревоги
CАнализ только группы B; искажает среднее
DЧтение чужого кода; замедляет работу
4. Почему проверка сразу 20 метрик при α = 0.05 проблематична?
AЭто слишком долго считать
BВ среднем примерно одна метрика «выстрелит» значимо чисто случайно — нужна поправка на множественность
CМетрики нельзя сравнивать между собой
DPython не поддерживает столько метрик
Поддержать проект