SRM в A/B-тесте: как проверить распределение пользователей

В A/B-тесте новая форма оформления заказа показала более высокую конверсию. Но в контрольной группе оказалось 5400 пользователей, в тестовой — 4600, хотя распределение планировали поровну. Перед оценкой улучшения нужно понять, насколько такое расхождение совместимо со случайным назначением.

Проверка называется Sample Ratio Mismatch, или SRM: она сопоставляет фактические размеры групп с запланированными долями. Сигнал может появиться из-за назначения вариантов, потери событий или фильтра аналитической выгрузки. Само расхождение ещё не указывает, какой участок сломан.

Кого включить в подсчёт

Начните с таблицы участников, где одна строка соответствует одному назначенному пользователю конкретного эксперимента. Повторные открытия страницы не должны увеличивать число участников. Отдельно проверьте пользователей, у которых записаны оба варианта: простое удаление повторов скроет смену назначения.

Доли возьмите из конфигурации эксперимента. План 80/20 нельзя проверять против 50/50. Если трафик меняли во времени, сначала восстановите ожидаемое распределение по этапам запуска, а затем выберите соответствующую проверку. Одна постоянная доля для такого периода может дать ложную тревогу.

Не считайте SRM только среди покупателей: покупка происходит после воздействия и может зависеть от варианта. Разное число покупателей часто и есть результат эксперимента. Для диагностики потерь полезно сравнить распределение на назначении, после отбора допустимой аудитории и в окончательной витрине метрик.

Как получить величину расхождения

Для каждой группы ожидаемое число E равно общему числу участников, умноженному на плановую долю. Затем складываем (наблюдаемое − E)² / E по двум группам. Это статистика χ². Чем она больше, тем сильнее данные расходятся с планом относительно размера выборки.

В двухгрупповом случае статистику можно перевести в p-value стандартной функцией math.erfc. Малое p-value означает, что при заданном случайном распределении такое или более сильное отклонение было бы редким. Это не вероятность ошибки в коде и не вероятность полезности новой формы.

В примере порог тревоги заранее выбран равным 0.001. Это настройка учебной проверки, которую в рабочем процессе нужно согласовать с правилами экспериментальной платформы. Для приближения χ² ожидаемые количества в каждой группе должны быть достаточно велики; код отклоняет значения меньше пяти.

Воспроизводимая проверка Python

Сохраните файл и выполните его стандартным Python. Он проверяет два распределения с одинаковым общим числом участников, а также корректный план 80/20.

import math

def srm_two_groups(count_a, count_b, share_a=0.5):
    if min(count_a, count_b) < 0 or not 0 < share_a < 1:
        raise ValueError("Invalid counts or allocation")
    total = count_a + count_b
    expected_a = total * share_a
    expected = (expected_a, total - expected_a)
    if min(expected) < 5:
        raise ValueError("Expected counts too small for chi-square approximation")
    chi2 = sum((o-e)**2/e for o,e in zip((count_a,count_b),expected))
    p_value = math.erfc(math.sqrt(chi2/2))  # chi-square with 1 degree of freedom
    return chi2, p_value

alpha = 0.001
for counts in [(5050,4950), (5400,4600)]:
    chi2,p = srm_two_groups(*counts)
    print(counts, "chi2=", round(chi2,3), "p=", format(p,'.6g'),
          "investigate=", p < alpha)

assert srm_two_groups(5050,4950)[0] == 1
assert 0.31 < srm_two_groups(5050,4950)[1] < 0.32
assert srm_two_groups(5400,4600)[0] == 64
assert srm_two_groups(5400,4600)[1] < alpha
assert srm_two_groups(8000,2000,0.8) == (0,1)
try:
    srm_two_groups(2,3)
except ValueError:
    pass
else:
    raise AssertionError("Small expected counts must be rejected")

Для 5050/4950 получится χ² = 1 и p-value около 0.317: выбранный порог тревоги не пересечён. Для 5400/4600 статистика равна 64, p-value около 1.24 × 10⁻¹⁵: распределение требует расследования. Процент расхождения без размера выборки не даёт такого решения.

Что делать после сигнала

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

Если назначение чистое, а после фильтра «взаимодействовал с новой формой» появляется перекос, пересмотрите сам фильтр. У контроля такой формы могло не быть. Удалять лишних пользователей из большей группы до равенства нельзя: это изменит выборку и не восстановит потерянные наблюдения.

До устранения причины отложите решение о раскатке по затронутому анализу. После исправления пересоберите данные и повторите проверку; изменение процедуры отбора требует нового осмысленного сравнения. Общая неопределённость конверсии — следующий вопрос, когда состав выборки уже проверен.

В платном курсе «A/B тесты и анализ данных на Python» вы пройдёте выбор метрик, планирование размера выборки, статистические тесты и интерпретацию доверительных интервалов. Практика связывает вычисления с решением по эксперименту. Откройте программу курса и продолжите от проверки данных к полному анализу теста.