A/B тестирование

Гипотезы, p-value, размер выборки, выбор критерия — всё что спрашивают на собесах в продуктовые команды.

Нулевая и альтернативная гипотезы

~7 мин

H0 и H1 — как правильно формулировать гипотезу до запуска теста, а не после.

Нулевая гипотеза (H0): изменение НЕ даёт эффекта.
Альтернативная (H1): изменение ДАЁТ эффект.

Пример:
  H0: Новая кнопка «Купить» не меняет конверсию
  H1: Новая кнопка повышает конверсию

Ошибки гипотез:
  Тип I (False Positive / α):  отвергаем H0, хотя она верна
                                → «нашли эффект которого нет»
  Тип II (False Negative / β): не отвергаем H0, хотя H1 верна
                                → «пропустили реальный эффект»

Стандартные уровни:
  α = 0.05  (5% вероятность ошибки типа I)
  β = 0.20  (80% статистическая мощность = 1 - β)

Правило: гипотезу формулируют ДО запуска теста.
Менять метрику после просмотра данных — p-hacking!

p-value — что это и как объяснить на собесе

~8 мин

p-value — вероятность получить такие данные, если H0 верна. Как правильно интерпретировать и частые заблуждения.

p-value = вероятность получить ТАКОЙ ЖЕ ИЛИ БОЛЕЕ ЭКСТРЕМАЛЬНЫЙ результат,
если нулевая гипотеза (H0) верна.

p < 0.05 → результат статистически значимый
           → отвергаем H0
           → НЕ означает, что H1 верна на 95%!

Частые заблуждения:
  ✗ «p = 0.03 означает 97% вероятность что эффект есть»
  ✓ «При H0 мы получили бы такой результат с вероятностью 3%»

  ✗ «p > 0.05 означает нет эффекта»
  ✓ «Недостаточно данных чтобы отвергнуть H0»

  ✗ «Малый p-value = большой эффект»
  ✓ На большой выборке даже незначимый эффект даёт p < 0.05

Поэтому всегда смотрим ВМЕСТЕ:
  • p-value (значимость)
  • effect size / lift (размер эффекта)
  • confidence interval (диапазон)

Размер выборки — как рассчитать

~9 мин

Минимальный детектируемый эффект, мощность теста, α — формула расчёта и онлайн-калькуляторы.

Параметры для расчёта:
  α = 0.05           (уровень значимости)
  β = 0.20 → 80%    (мощность теста)
  p1 = baseline_cr   (текущая конверсия, например 0.05 = 5%)
  MDE = 0.10         (минимальный детектируемый эффект = +10% от baseline)
  p2 = p1 * (1+MDE)  (целевая конверсия = 0.055)

Python-расчёт:
from statsmodels.stats.power import TTestIndPower, zt_ind_solve_power
from statsmodels.stats.proportion import proportion_effectsize

es = proportion_effectsize(0.05, 0.055)  # Cohen's h
n = zt_ind_solve_power(effect_size=es, alpha=0.05, power=0.80)
print(f'Нужно ≥ {int(n)} пользователей на группу')

Практические правила:
  • Чем меньше MDE — тем больше нужна выборка
  • Базовая конверсия 5%: для +10% нужно ~15 000/группу
  • Базовая конверсия 20%: для +10% нужно ~3 400/группу
  • Новинка: используй онлайн-калькулятор Evan's A/B Tools

Какой тест выбрать: t-test, χ², Mann-Whitney

~10 мин

Критерии выбора статистического теста в зависимости от типа данных и распределения.

t-test (Стьюдент):
  • Непрерывная метрика (средний чек, время на сайте)
  • Нормальное распределение ИЛИ n > 30 (ЦПТ)
  from scipy.stats import ttest_ind
  stat, p = ttest_ind(control, variant)

χ² (хи-квадрат):
  • Категориальная метрика (конверсия: купил/не купил)
  • Сравниваем частоты / пропорции
  from scipy.stats import chi2_contingency
  table = [[conv_c, no_conv_c], [conv_v, no_conv_v]]
  chi2, p, dof, expected = chi2_contingency(table)

Mann-Whitney U-test:
  • Непрерывная метрика с НЕНОРМАЛЬНЫМ распределением
  • Нет выбросов-зависимости (долго, выручка с хвостом)
  from scipy.stats import mannwhitneyu

import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'A/B-тесты для аналитика',
  description: 'Гипотезы, p-value, размер выборки, частые ошибки в A/B-экспериментах.',
  openGraph: { title: 'A/B-тесты для аналитика', description: 'Гипотезы, p-value, размер выборки, частые ошибки в A/B-экспериментах.' },
};

  stat, p = mannwhitneyu(control, variant, alternative='two-sided')

Шпаргалка:
  Конверсия (доля)          → χ² тест
  Средний чек (нормальное)  → t-test
  Средний чек (с хвостами)  → Mann-Whitney
  Время удержания           → Log-rank test

Топ ошибок в A/B тестировании

~8 мин

Peeking problem, множественное тестирование, novelty effect — что убивает достоверность тестов.

1. Peeking / Ранняя остановка
   Нельзя смотреть на результат и останавливать тест
   как только p < 0.05 — inflate Type I error.
   Решение: Sequential testing / bayesian approach.

2. Множественное тестирование (Multiple Comparisons)
   Тестируешь 20 гипотез → одна значима по случайности (α=0.05).
   Решение: Bonferroni correction → α_adj = 0.05 / 20 = 0.0025

3. Novelty Effect (эффект новизны)
   Пользователи нажимают на новое просто потому что оно новое.
   Решение: смотреть retention через 1-2 недели, не первый день.

4. Неправильная рандомизация
   Одни пользователи попадают в обе группы, или
   рандомизация по сессии а не по user_id.
   Решение: всегда рандомизировать по entity (user/device) ID.

5. Разные исходные группы (SRM — Sample Ratio Mismatch)
   Контроль: 10 000 пользователей, вариант: 9 200 — не 50/50.
   Проверка χ²: ожидаем 50/50, получаем — другое → баг в рандомизации.

6. Измерение не той метрики
   Оптимизируешь CTR кнопки «Купить» — растёт.
   Но выручка падает, т.к. кликают случайно.
   Решение: первичная метрика + guardrail metrics.