Какие статистические критерии вы используете для анализа A/B-тестов?

Ответ

Выбор критерия зависит от типа метрики и свойств данных. Вот мой практический подход:

Для бинарных метрик (конверсия, кликабельность):

  • Z-тест для пропорций: Основной инструмент при больших размерах выборок (n > 30, а лучше > 100 на группу), когда можно аппроксимировать биномиальное распределение нормальным.
    from statsmodels.stats.proportion import proportions_ztest
    # count - количество успехов, nobs - размер группы
    count = np.array([150, 180])  # Конверсии в группах A и B
    nobs = np.array([1000, 1000]) # Размеры групп
    z_stat, p_value = proportions_ztest(count, nobs, alternative='two-sided')
    print(f"Z-статистика: {z_stat:.3f}, p-value: {p_value:.4f}")

Для непрерывных метрик (средний чек, время в приложении):

  • T-тест Стьюдента: Использую, когда данные приблизительно нормально распределены и дисперсии в группах схожи (проверяю тестом Левена).
    from scipy.stats import ttest_ind
    # revenue_a, revenue_b - массивы с данными по чекам
    t_stat, p_value = ttest_ind(revenue_a, revenue_b, equal_var=True)
  • U-тест Манна-Уитни: Непараметрическая альтернатива, когда данные не нормальны или есть выбросы. Он сравнивает не средние, а распределения в целом.
    from scipy.stats import mannwhitneyu
    u_stat, p_value = mannwhitneyu(time_on_site_a, time_on_site_b, alternative='two-sided')

Ключевые принципы, которые я соблюдаю:

  1. Проверка предпосылок: Перед t-тестом смотрю на гистограммы и проверяю нормальность (Shapiro-Wilk) и гомогенность дисперсий (Levene's test).
  2. Коррекция множественных сравнений: Если тестируется несколько гипотез одновременно, применяю поправку Бонферрони или Бенджамини-Хохберга (FDR).
  3. Расчёт мощности теста: До запуска оцениваю необходимый размер выборки, чтобы с заданной вероятностью (обычно 80%) обнаружить эффект ожидаемого размера (MDE). Использую для этого statsmodels.stats.power.
  4. Анализ не только p-value: Всегда смотрю на доверительные интервалы разницы метрик, чтобы оценить не только статистическую значимость, но и практическую значимость эффекта.

Ответ 18+ 🔞

А, ну это же классика, ёпта! Сейчас объясню, как я обычно разбираюсь с этой статистической ерундой, чтобы не облажаться. Сидишь такой, смотришь на цифры, и подозрение ебать чувствуешь — а вдруг это просто случайный всплеск, а не реальный эффект? Вот тут и начинается магия.

Если метрика простая, как две копейки — «кликнул/не кликнул», «купил/не купил»: Тут всё просто, как три рубля. Берёшь Z-тест для пропорций. Работает он на ура, когда выборки большие (ну там, от сотни наблюдений в каждой группе, а лучше больше). По сути, он проверяет, не сошли ли мы с ума, думая, что разница в процентах — это не просто погрешность.

from statsmodels.stats.proportion import proportions_ztest
# count - количество успехов, nobs - размер группы
count = np.array([150, 180])  # Конверсии в группах A и B
nobs = np.array([1000, 1000]) # Размеры групп
z_stat, p_value = proportions_ztest(count, nobs, alternative='two-sided')
print(f"Z-статистика: {z_stat:.3f}, p-value: {p_value:.4f}")

Если p-value меньше 0.05 — поздравляю, можно начинать волнение ебать, скорее всего, разница реальная. Но это ещё не конец!

А вот если метрика посложнее — средний чек, время сессии, какая-нибудь хитроумная скор-бла-бла: Тут уже нужно смотреть, что у нас за данные. Идеальный мир — это T-тест Стьюдента. Но он, сука, такой нежный, требует, чтобы данные были хоть немного похожи на нормальное распределение и чтобы разбросы в группах были примерно одинаковые.

from scipy.stats import ttest_ind
# revenue_a, revenue_b - массивы с данными по чекам
t_stat, p_value = ttest_ind(revenue_a, revenue_b, equal_var=True)

Но жизнь — не идеальный мир. Часто данные выглядят так, будто их кот сука собака через себя пропустил: выбросы, дикие распределения, хвосты во все стороны. Вот тут на сцену выходит спаситель — U-тест Манна-Уитни. Ему похуй на нормальность, он сравнивает вообще всё распределение. Сильная штука.

from scipy.stats import mannwhitneyu
u_stat, p_value = mannwhitneyu(time_on_site_a, time_on_site_b, alternative='two-sided')

Но самое главное, чувак, — это не тупо запустить тест и порадоваться зелёной галочке. Вот мои железные правила, чтобы не выглядеть потом пиздопроебибной:

  1. Не верь глазам своим, проверь. Перед тем как тыкать в t-тест, посмотри на гистограммы. Данные нормальные? Дисперсии похожи? Есть тесты (Шапиро-Уилк, Левен) для этого, но иногда и глазами видно, что это хуй с горы, а не нормальное распределение.
  2. Если тыкаешь во всё подряд, рано или поздно попадёшь. Запустил десять гипотез? Жди, что одна случайно сработает просто так. Это как обезьяна с пишущей машинкой. Поэтому, если тестов много, применяй поправки — Бонферрони или Бенджамини-Хохберга. Без этого доверия ебать ноль к твоим результатам.
  3. Сила — не только в мышцах, но и в тесте. Перед экспериментом прикинь, сколько данных нужно, чтобы поймать эффект того размера, который тебе интересен. Иначе можешь месяц ждать и в итоге получить «разницы нет», хотя её просто невозможно было обнаружить с твоими крошечными выборками. Это называется «расчёт мощности».
  4. P-value — это не бог. «О, p-value 0.04, ура!» — стоп, блядь. Посмотри на доверительный интервал для разницы. Он показывает не просто «есть эффект», а «эффект скорее всего от X до Y». Может оказаться, что эффект есть, но он такой мизерный, что и заморачиваться не стоило. Всегда смотри и на p-value, и на интервалы, и на сами цифры. Иначе будешь как тот полупидор, который радуется статистической значимости увеличения конверсии с 0.001% до 0.002%.

Вот, собственно, и вся философия. Главное — не быть распиздяем, проверять предпосылки и смотреть на задачу с разных сторон. А то ведь можно и впиздюрить себе в отчёт красивую, но совершенно ложную находку.