Классическая рандомизация - наша
https://t.me/abba_testingОпределимся с понятиями и начнем с обычной рандомизации: берем нашу популяцию объектов, для каждого объекта подбрасываем честную монетку, если решка (0), то отправляем его в контроль, орел (1) - в тест.
Как правило, в этом популяции мы выделяем сегменты, как минимум бизнесовые: активные/ядро/отток, много тратящие/мало тратящие. Все это можно еще дополнительно разрезать на уровне пол, возраст и так далее, итого получается множество подгрупп. При классической рандомизации подгруппы в тесте и контроле в среднем сбалансированы, например, плюс-минус одинаковое распределение по полу, возрасту пр. Однако иногда может быть перекос, который для нас важен не столько из-за ошибки 1-го рода (с ней как раз все ок и она контролируется на уровне альфы, к этому еще вернемся), сколько из-за увеличенного шума, который снижает чувствительность теста.
Чтобы избежать этого существует стратифицированная рандомизация: разбиваем популяцию разбиваем на подгруппы. Допустим, мы отслеживаем пол/возраст (молодой/старый), итого у нас 4 комбинации:
М - молодые
М - старые
Ж - молодые
Ж - старые
Подбрасываем честную монетку в первой группе, 0 - котроль, 1 - тест, потом во второй и так далее.

Группы сбалансированы, это может дать некоторый выигрыш в чувствительности, но не всегда. Более того, чем больше ваши группы для теста, чем они больше принимают очертания популяции, тем меньше этого выигрыш, то есть с некоторого порога по объему аудитории и обычная рандомизация будет давать сбалансированные группы, но разница есть: стратифицированная рандомизация сложнее по алгоритму, а значит вы обязательно столкнетесь с проблемой масштабирования, это нагрузка на платформу, начинает копиться очередь на запуск.
То есть стратифицированный отбор если и имеет смысл, то на небольших выборках, а чтобы совсем минимизировать перекос, то лучше блоковая (blocked) стратификация:
Допустим, в подгруппе “М - молодой” у нас 6 человек, [1, 2, … , 6].
1) Случайно перемешаем* пользователей внутри подгруппы.
2) Далее разобьем их на небольшие блоки фиксированного размера. Для простоты рассмотрим блок размера 2 (хотя на практике чаще используют блоки большего размера). После разбиения получим, например, три блока-пары: [1,6], [3,4] и [5,2].
3) Заранее зададим два возможных шаблона назначения: [A,B] и [B,A].
4) Для каждого блока случайно выбираем один из двух шаблонов назначения.
В результате внутри каждого блока один пользователь попадет в тест, а другой в контроль, что минимизирует возможный дисбаланс между группами. Нечетное же количество человек особо тут проблем не создаст, если совсем бесит - и на это есть алгоритмы.
* Это когда мы сами формируем группы заранее. При динамическом сплитовании (например, в онлайн-тесте) пользователи приходят последовательно в приложение / на сайт. Тогда блоки формируются из последовательно пришедших пользователей: первые два образуют первый блок, следующие два - второй и так далее. Для каждого блока независимо выбирается случайный шаблон назначения.
Но: такая блоковая стратификация это дополнительный шаг в алгоритме, да к тому же - что такое малая выборка? Кто что говорит, но всякие там 30 / 35 / 50 в рамках индустрии скорее исключение, а поэтому случайное сэмплирование для индустрии же это наше всё, которое можно, впрочем, в рамках анализа результатов дополнить постстратификацей для возможного (но не гарантированного) уменьшения шума ценой некоторого увеличения времени расчетов. Да, стратификация+постстратификация лучше всего могут снизить дисперсию, но и цена этого слишком высока.
Отсюда, если у вас AB на потоке:
1) Ресурсы ограничены, нужны стабильные запуски - обычная рандомизация
2) Вы, условно, Nvidia - делайте, что хотите, хоть трассируйте искусственных пользователей в тесте
Дисбаланс по соц.дему/метрикам - проблема ли?
Когда вы это видите на предпериоде дисбаланс, то вы видите это на базе отслеживаемых данных, а что происходит на уровне ненаблюдаемых переменных (коих тьма против ваших) вы не знаете, там может быть плюс-минус всё в балансе. То есть “не баланс по всем ковариатам” (хотя с точки зрения среднего оно так и будет), а “в целом надежность”, которая прежде всего про нивелирование selection bias, систематическую ошибку отбора, когда в тест и контроль “залетают” на базе какой-то причины, а не случайности. Например, представьте, что для теста лекарства у вас группы сравнимы по всем характеристикам, однако врач по паре-тройке показателей отбирал в тест тех, у кого есть больше шансов на выздоровление. Баланс в 99% переменных есть, а bias - есть. Рандомизация же может допустить и больший дисбаланс, но bias будет минимизирован: различия между группами будут обусловлены случайностью, а не механизмом отбора.
Представим более радикальный пример, что по всем ковариатам, - отслеживаемым и нет, - у нас прям жесткий перекос. Да, в такой реализации эксперимента мы можем получить ложноположительный результат, ошибку I рода, однако именно вероятность подобных событий и контролируется уровнем значимости альфа. Рандомизация допускает подобные перекосы, но гарантирует, что они возникают случайно, а не опять-таки вследствие систематической ошибки отбора.
И вот еще что, к вопросу об ошибку 1-го рода: Даже если вы наблюдаете по вашим метрикам на предпериоде перекос, при A/B-тесте (в отличие от A/A) невозможно понять, является ли наблюдаемая разница следствием случайного дисбаланса или реального эффекта. Ведь перекос мог вас выкинуть как область ошибки 1-го рода когда эффекта нет, так и поставить на границу значимости, которую уже пересекает случай, когда эффект все-таки есть!
Рерандомизация или - что нам делать с наблюдаемым дисбалансом.
Действительно, если мы подбираем группы на тест (например, у нас будет тест на мазагинах), почему бы нам при наблюдаемом дисбалансе не сделать пересплит, то есть повторно разбить “популяцию магазинов” до тех пор, пока не будет выравнивания по всем метриках/переменным? Но рерандомизация, - звучит даже странно, - представляет собой неслучайный отбор (“пока видимые переменные не будут в балансе”), о котором применяемый критерий ничего не знает: у вас будет ситуация подбодно с заниженной альфой, если вы делаете стратификацию и считаете разницу нестратифицированых средних - используемый критерий, - распределение его статистики, - напрямую связано с тем, как происходит разбиение.
Когда мы используем классический статистический критерий а-ля t-test, он по умолчанию предполагает, что группы были сформированы чисто случайным образом. Из этого предположения выводится стандартная ошибка разности средних. Но если мы вводим правило: “Пересплитовываем до тех пор, пока ковариаты (выручка, трафик, размер магазина) не сбалансируются”, то мы делаем следующее:
- Запрещаем те разбиения, где дисбаланс велик, то есть искусственно сжимаем фактическую дисперсию разности средних на стадии дизайна.
И когда мы запускаем тест на таких “подогнанных” группах, истинная разность средних между ними изначально зажимается около нуля, но классический t-критерий не знает, что мы отбросили тысячи плохих (с нашей точки зрения) вариантов разбиения. Он ожидает стандартную случайную дисперсию. Стандартная ошибка, которую рассчитывает критерий, оказывается завышенной относительно реальной (искусственно уменьшенной) изменчивости между группами. В целом вся дробь критерия t = (A-B)/SE становится меньше, p-value больше, мощность теста - падает. Мы получаем консервативный критерий, как в случае стратификации без страт.среднего. Чтобы эту консервативность убрать, вам нужно будет это как-то учитывать это в расчетах, что непросто.
И что тогда?
1) Оставьте полученный сплит как есть. Еще раз: cлучайный дисбаланс естественное следствие случайности. Главная задача рандомизации не добиться идеального совпадения групп, а исключить систематическое различие между ними.
2) Напишите в комментариях/замечаниях к дизайну об наблюдаемом дисбалансе. При этом мне по душе предложения градация Авито (см.источники) по важности метрик и признаков:
- Целевая > прокси
- Прошлый месяц > позапрошлый
- Гетерогенный признак > базовый
То есть не все разбегания равны, есть более и менее важные, что можно зашить в веса и вывести некоторую общую оценку критичности/некритичности расхождения для вашего домена (давно о такой оценке думаю, но еще не сформулировась)
Такое замечание по итогу может быть принято во внимание в процессе решения, что делать по результатам теста: катить или не катить.