Собесов

Karpov Stepik: t-test vs Mann–Whitney U — какой когда

Статистика и теорверТесты для двух выборокСредняяMiddle

Условие

В A/B-тесте сравниваем среднюю длительность сессии в контроле и тесте. Распределение очень скошенное (длинный правый хвост, медиана 90 сек, среднее 250 сек, есть сессии по 30 минут). Какой тест выбрать?

  1. Welch's t-test (двух выборок с разной σ²).
  2. Mann–Whitney U.
  3. Bootstrap.
  4. Permutation test.

Сравните, какие предположения каждый делает. Что бы вы реально применили в продакшене?

Решение

Сравнение

Тест Что сравнивает Предположения Чувствителен к выбросам
Welch's t средние независимые наблюдения, конечные σ²; нормальность желательна (или большие N через CLT) сильно
Mann–Whitney U стохастический порядок (P(X>Y) > 0.5) независимость, непрерывность, одинаковая форма распределения (для интерпретации как медианы) слабее
Bootstrap on mean любой статистикой независимость; распределение «достаточно богатое» соответствует статистике
Permutation любой статистикой exchangeability под H₀ гибкий

Что выбрать

Welch t-test: при больших N (>10К на ветку) CLT срабатывает даже на скошенных данных. p-value обычно близок к корректному. Сравнивает средние, что соответствует выручке / экономике.

Mann–Whitney: сравнивает ранги, не средние. Менее чувствителен к выбросам, но интерпретация «B стохастически больше A», а не «среднее в B больше». На очень скошенных данных может сместить результат, если distributions меняют форму, а не только сдвиг.

Bootstrap: повторяем sampling с возвращением из обоих ветвей, считаем разность средних (или медиан). Лучший выбор, когда σ² нестабильна или есть очень тяжёлые хвосты.

Permutation: точно соответствует H₀ exchangeability. Дорого: 10К пермутаций × 2М наблюдений.

Код

import numpy as np
from scipy.stats import ttest_ind, mannwhitneyu
 
# Welch's t
t, p_t = ttest_ind(a, b, equal_var=False)
 
# Mann–Whitney
u, p_u = mannwhitneyu(a, b, alternative='two-sided')
 
# Bootstrap CI на разность средних
rng = np.random.default_rng(0)
def boot_diff(a, b, n_iter=10000):
    diffs = np.empty(n_iter)
    for i in range(n_iter):
        sa = rng.choice(a, len(a), replace=True)
        sb = rng.choice(b, len(b), replace=True)
        diffs[i] = sb.mean() - sa.mean()
    ci = np.percentile(diffs, [2.5, 97.5])
    return diffs.mean(), ci

Тактический совет для проды

  1. Выбор статистики по продукту: для выручки / времени сессии бизнес интересуется средним (т.к. оно агрегируется в total). Mann–Whitney не отвечает на этот вопрос.

  2. Тяжёлые хвосты → стабилизация дисперсии:

    • Log-преобразование (log(1 + x)) — часто превращает в почти нормальное; t-test на логарифмах ок, но интерпретация: «среднее лог дохода», не средний доход.
    • Winsorize на 99% / 99.9% — обрезать выбросы; t-test становится стабильным.
    • CUPED-стратификация — снижает дисперсию через ковариату (например, активность в предпериоде).
  3. Большие N (>50К): CLT + Welch почти всегда корректен, но дисперсия оценки может быть огромной — отсюда нужны CUPED или bootstrap CI.

CUPED как стандарт в индустрии

Y' = Y - θ · (X - E[X])     где X — pre-period covariate
θ = Cov(Y, X) / Var(X)

Снижает Var(Y') в 30-70% — эффективно как «бесплатное» увеличение n.

Подводные камни

  1. Mann–Whitney = «тест медианы» — только если формы distributions одинаковы. Иначе он тестирует общее «stochastic dominance».
  2. t-test на 100 наблюдениях с экстремальным skew — p-value сильно смещён; bootstrap безопаснее.
  3. equal_var=True в ttest_ind (Student t) предполагает одинаковую дисперсию; Welch's = equal_var=False. На A/B всегда Welch.
  4. Permutation не помогает с heavy tails: тоже сравнивает средние, которые нестабильны.
  5. Multiple metrics: если считаете 10 метрик, делите α / 10 (Bonferroni) или FDR.
  6. Outlier removal post-hoc — leakage: убираете «странные» строки на основании результата → искажение p-value.
  7. Двусторонний vs односторонний: бизнес обычно хочет «лучше или хуже» → двусторонний.

Эталонный ответ

В продакшене на тяжёлых хвостах:

  1. Welch's t-test с CUPED — стандарт индустрии для больших N.
  2. Bootstrap CI на разность средних — sanity-check.
  3. Mann–Whitney — sanity, но не основная метрика (не отвечает на «средний выше или ниже»).
  4. Winsorize / log-преобразование для стабилизации дисперсии — если нет CUPED.

t-test сравнивает средние (то, что нужно бизнесу для выручки/времени), Mann–Whitney — стохастический порядок. На скошенных малых выборках — bootstrap.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти