Продуктовый аналитик: Путешествие туда и обратно

Глава 4. Глубина познания в пещере оракула

4.2. A/B-тест как священный ритуал в Пещере Оракула

Перед вами вход в Пещеру Оракула. Стены здесь покрыты живыми рунами, они едва заметно дышат, перетекают одна в другую, складываются в слова и тут же рассыпаются. В воздухе висят неподвижные капли: подземная река, текущая сквозь Пещеру, давно остановилась, чтобы не мешать тем, кто слушает. А из глубины слышен низкий шёпот Оракула. Священный ритуал позволит вам задать Оракулу вопрос и получить ответ. Но если вопрос был сформулирован плохо или ритуал был нарушен, то пророчество окажется бесполезным или, что хуже, гибельным. Пророчества Оракула туманны, а слова режут слух непривычному уху: «при условии нулевой гипотезы вероятность увидеть такую или более экстремальную статистику равна 0,018». Новичку кажется, что он понял смысл: «эффект есть с вероятностью 98%». Но старший жрец слышит ровно то, что сказано. Между этими двумя способами понимать пророчества лежит путь, который вам предстоит пройти.
-Роль A/B теста в продуктовом цикле
-Язык Оракула, ЦПТ, p-value, Стохастические Демоны, t-test
-Кинжал Статистической Значимости («α», «1 − β», «N», «MDE»)
-Бутстрап и тест с перестановкой в Python
-Ratio-метрики
-A/B тест на смешанной аудитории

Продуктовый цикл работает при непосредственном участии аналитика: вы изучаете данные и метрики, проводите анализ, находите проблему или возможность, доносите это до команды, с вашим участием происходит проектирование изменений, разработка выкатывает обновления на прод и дальше приходит время оценить успешность. Стало лучше или хуже? «Кажется, ARPU подрос» это не тот ответ, который нужен. ARPU меняется под действием сезонности, Ивентов, качества входящего трафика, удержания аудитории. Если вы хотите знать, что сделало именно ваше изменение вам нужен инструмент, который изолирует эффект изменения от фонового шума. Статистический тест на рандомизированных группах это самый точный из существующих способов такой изоляции, но не единственный.

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

4.2.1. Священный ритуал и его место в цикле

Прежде чем подражать старшим жрецам, присмотритесь: что вообще происходит в Пещере? Жрецы говорят, что Священный ритуал это лишь звено в большем жизненном цикле развития. Сам по себе священный ритуал, оторванный от жизненного цикла, не приносит пользы. Без понимания цели исследования это лишь кривляние в свете факелов – загадочное, технически безупречное и совершенно бессмысленное.

Продуктовый цикл и место A/B-теста

Продуктовый цикл, который мы собирали по кусочкам прошлую главу, выглядит так:

Метрики → Анализ → Вывод → Концепция решения → Действие → Оценка результата
   ↑                                                              |
   └──────────────────────────────────────────────────────────────┘

A/B-тест живёт в заключительном звене цикла, в оценке результатов. Это важно проговорить, потому что самая частая системная ошибка аналитиков это попытка применить A/B на весь цикл: «давайте просто протестируем десять вариантов и посмотрим, что выстрелит» (для мальчика, у которого в руках молоток все вокруг это гвозди). Поиск решения перебором ближе к комбинаторике чем к аналитике. A/B-тест отвечает на вопрос работает ли это конкретное изменение, но он не подскажет, какое изменение стоит делать. Поиск точек роста это ваша предварительная глубокая аналитическая работа на этапах метрики → анализ → вывод. Аналитическая работа строится на ваших наблюдениях, исследованиях, сегментациях, когортах и воронках, поиске аномалий. A/B лишь замыкает цикл, а не заменяет этап глубоких исследований. Именно поэтому, если вы не можете объяснить, на основе каких данных вы предполагаете, что фича окажет воздействие, какой именно результат вы ожидаете, тест запускать рано. Не «продюсеру интересно посмотреть», а «на основе анализа данных мы принимаем, что, если эффект ≥ X то катим фичу на всех, если < X то откатываем и возвращаемся к гипотезе Y».

С другой стороны, насколько оправданы сложности, которыми сопровождается проведение теста? Представим, что у вашей команды нет аналитика и сложно вникнуть в текущую теорию A/B тестов, при этом разработка, например, механики оферов уже проведена и в вашей игре технически не сложно разделить часть аудитории по четным и нечетным user_id. После этого можно без особых усилий одним предложить скидку больше, а другим меньше, и через неделю сопоставить выручку двух групп. Там где выручка больше там и скидка, вероятно, подобрана лучше, значит это и оставляем на проде. Сопоставить что больше совершенно не сложно, неясно зачем аналитики придумали такие сложности с теорией A/B тестов.

В чем может быть проблема с таким наивным экспериментом?
 1. Допустим, рандомизация честная. В группе «A» выручка 480К, а в «B» выручка 496К. Но почему мы уверены, что это повторится на проде, когда раскатим на всю аудиторию? Выручка в F2P это случайная величина и при перезапуске на следующей неделе порядок может перевернуться. Разница в 16К может быть случайна, и целиком объяснена тем, в какую группу случайно попали два-три крупных плательщика.
 2. Один или два кита не изменят метрику PU% или удержания, но выручка сильно зависит от наличия крупных плательщиков. Поэтому в монетизационных тестах мало даже честной рандомизации и нужна «ранжированная змейка» (код был разобран в прошлой главе), которая разводит ранжированный список плательщиков по группам поровну.
 3. Офер в котором скидка побольше предсказуемо даст больше выручки в течение теста, ведь ты продаёшь дешевле. Но что будет с долгосрочными метриками, когда это выкатят на прод?
  • Каннибализация. Большая скидка переманила тех, кто купил бы и по полной цене.
  • Якорение цены. Игроки запоминают, что «этот бандл стоит вот столько», и перестают покупать по обычной цене, ожидая следующей скидки.
  • Разный эффект на сегментах. Предположим, большая скидка действительно подняла платежи мало платящей аудитории, но обвалила платежи китов (зачем киту платить полную сумму, если есть скидка). Среднее же показало плюс, но ты только что научил китов ждать распродаж.

Тест, который длится, например, 1-2 недели позволил сопоставить выручку, полученную здесь и сейчас, а проект планирует горизонтом не менее года. Сделать тест «просто» можно, и иногда на огромных аудиториях с простыми метриками это даже сработает случайно. Проблема в том, что наивная схема организации теста систематически принимает шум за сигнал и краткосрочную выгоду за гарантию роста метрик в будущем и делает это уверенно, с конкретными цифрами, как и положено в «data-driven» компании.

4.2.2. Туманные пророчества Оракула: язык вероятностей

Оракул говорит на языке математической статистики, потому что мир, будущее состоит не из фактов, а из возможностей. Новичок, пришедший в Пещеру без знания этого языка, услышит знакомые слова «p-value», «значимость», «доверительный интервал» и сделает вид, что помнит их смысл. Но даже хорошим ученикам бывает полезно вернуться к мудрости скрижалей старших жрецов.

Данная глава не заменит вам забытый курс вузовской статистики. Если у вас не было курса статистики, то я не смогу заменить весь учебник одной главой. Но попробуем быстро освежить ваши знания с упором на интуицию и понимание процессов. Уверен, что чаще именно неправильные интерпретации, а не незнание формул, ломают тесты на практике.

Дисперсия, стандартное отклонение, стандартная ошибка

Поскольку распределение платежей в F2P сильно перекошено, может показаться, что в A/B-тестах нам стоило бы сравнивать медианы, а не средние. Ведь медиана устойчивее к выбросам. Но на самом деле руководство проекта интересует, сколько денег заработает проект. А совокупная выручка равна среднему ARPU, умноженному на число пользователей. Медиана же отвечает больше на вопрос сколько платит типичный игрок, и в F2P, где медианный игрок платит ноль, этот вопрос почти всегда бесполезен. И более того, монетизационная фича может удвоить платежи китов (огромный прирост выручки) и не сдвинуть медиану. Как следствие, тест по медиане скажет, что эффекта нет, и формально не соврёт, просто ответит на вопрос, который вы не задавали.

Напомню, что любая случайная величина имеет некоторое статистическое отклонение от своего среднего значения вверх или вниз. Мера такого разброса называется дисперсия (Var или σ²). Чтобы её посчитать, мы берём каждое число, вычитаем из него среднее значение, возводим полученную разницу в квадрат (чтобы избавиться от минусов), а затем находим среднее арифметическое этих квадратов. Важная оговорка: это формула для дисперсии генеральной совокупности, где сумма квадратов делится на n. На практике же в A/B-тесте мы почти всегда считаем выборочную дисперсию (s²) по данным группы, и там сумма квадратов делится не на n, а на (n−1):

s² = 1/(n−1) · Σᵢ₌₁ⁿ (xᵢ − x̄)²

При больших выборках разница между делением на n и на (n−1) исчезающе мала, но при малых группах (скажем, n в районе 10–30, что случается при сегментации результатов теста) она уже заметно влияет на итоговый расчёт.
Сигма (σ) или стандартное отклонение (Standard Deviation, SD) — это просто квадратный корень из дисперсии. Величина стандартного отклонения характеризует, насколько в среднем конкретный объект (игрок) отклоняется от общего среднего.

Есть ещё стандартная ошибка (Standard Error, SE). Это разброс не отдельных игроков, а разброс средних значений выборок. Предположим, что из одной огромной выборки наших игроков (генеральной совокупности) мы случайным образом возьмём 50 разных групп по 100 игроков и увидим, что у каждой группы будет своё среднее значение метрики. Однако для этих 50 средних значений тоже можно посчитать среднее и стандартное отклонение. Такое стандартное отклонение средних (SE) считается как σ генеральной совокупности, делённое на корень из размера выборки. То есть если для всех игроков σ = 7, а в наши выборки мы берём по 100 игроков, то SE = 0,7. Это значит, что наше выборочное среднее отличается от истинного среднего примерно на 0,7.

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

SE ≈ s/√n

Именно эта оценка SE и попадает в знаменатель t-статистики, о которой пойдёт речь дальше. Таким образом, истинное значение метрики (например, среднее генеральной совокупности) это фиксированная, но неизвестная нам величина. Случайным является сам интервал, который меняется от одной выборки к другой. И ширина такого интервала зависит от размера выборки.

Центральная Предельная Теорема (ЦПТ)

ЦПТ упрощённо выглядит так: даже для самого странного распределения величины, если брать из него выборки размера n и считать в каждой выборке среднее, то распределение этих выборочных средних с ростом числа выборок стремится к нормальному, с дисперсией σ²/n. Это, правда, немного звучит как магия, и на ней и работает «ритуал» A/B теста: мы не знаем распределения выручки, но знаем (приближённо) распределение средней выручки по группе, поэтому сравниваем мы именно средние по группам. Это, кстати, ещё одна причина, почему мы тестируем именно среднее, а не медиану: для среднего у нас есть удобная и хорошо изученная асимптотика (ЦПТ), а для медианы в разреженном у нуля распределении F2P-платежей аналогичные гарантии работают куда хуже.

Но у этой магии есть ограничения.
 • Независимость наблюдений. Пользователи не должны влиять друг на друга.
 • Конечная дисперсия. Платежи всё же ограничены, но в играх они могут действительно быть очень высокими. Дело не только в теоретическом «а вдруг дисперсия бесконечна», на практике важнее то, что чем тяжелее хвост распределения (чем ближе оно к степенному закону, что типично для платежей китов), тем медленнее сходимость к нормальности даже при формально конечной дисперсии. То есть условие может формально выполняться, а сходимость всё равно быть далека от нормальной формы при разумных n.
 • Достаточный размер выборки. Для F2P-выручки достаточное число наблюдений может быть более 10К или даже 50К. Точный порог зависит от степени скошенности распределения выручки в конкретной игре: чем больше вклад топовых китов в общую выручку, тем больше N нужно ЦПТ, чтобы «победить» этот перекос. Универсального числа нет, но на практике порог стоит проверять симуляцией на исторических данных (ресэмплинг реальной выручки игры и наблюдение, при каком n распределение выборочных средних визуально приближается к нормальному) или через регулярные A/A-тесты.

В чём смысл статистического теста (t-test)

Представим, что у нас есть две выборки наших игроков (контроль и тест), у каждой из них есть своё некоторое распределение этой метрики вокруг среднего значения. Однако, если сравнить средние двух выборок, то разница почти никогда не равна нулю просто из-за случайности выборки. Поэтому главный вопрос t-теста: эта разница средних действительно отражает реальный эффект от некоторого воздействия на контрольную группу, или это просто случайность?

A/B-test

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

t = (X̄₁ − X̄₂) / SE

Чем больше расстояние между средними (числитель) и чем меньше разброс внутри групп (знаменатель), тем больше t-статистика и тем увереннее можно сказать, что разница не случайна.
Для двух независимых выборок итоговая формула будет иметь вид:

t = (X̄₁ − X̄₂) / √(s₁²/n₁ + s₂²/n₂)

где
 X̄₁, X̄₂ — средние в группах
 s₁², s₂² — выборочные дисперсии
 n₁, n₂ — размеры выборок

Обратите внимание, что эта формула не предполагает равенства дисперсий в группах, формально это так называемый t-тест Уэлча (Welch’s t-test). Для F2P это не техническая деталь, а осознанный выбор: дисперсия выручки в тестовой и контрольной группе почти никогда не совпадает (тестовое изменение сплошь и рядом само по себе меняет разброс платежей, а не только их среднее), поэтому «классический» t-тест с общей дисперсией на практике почти не используется. Если позже будете переносить формулу в код, в scipy.stats.ttest_ind за это отвечает параметр equal_var=False.

P-value

P-value это вероятность увидеть такую или более экстремальную статистику при условии, что нулевая гипотеза (H0) верна. Звучит как очередное туманное пророчество Оракула, верно? Но это один из самых важных и одновременно неверно интерпретируемых показателей в анализе данных.

Полученная t-статистика будет иметь некоторое значение, но в общем случае будет распределена вокруг среднего нулевого значения с некоторой вероятностью даже если никаких отличий между двумя выборками нет. Эти отличия случайны, но если мы бы мы делали наши сопоставления многократно, то более вероятно мы бы видели значения близкие к нулю и чем дальше от нуля тем менее вероятно их получить. Но мы можем задать некоторые границы (на рисунке ниже p-value это границы, отмеченные слева и справа пунктиром).

A/B-test2

Если верна нулевая гипотеза и выборки идентичны то, например, в 95 случаев из 100 мы получим значения из синей области. Как следствие, если t-статистика после расчета попадает в синюю область, то мы не можем отклонить нулевую гипотезу. Но чем дальше от нуля наше наблюдаемое t (попало в красные зоны слева или справа), тем меньше p-value и тем менее правдоподобно, что нулевая гипотеза верна. Предположим, что чёрная линия справа это полученное из нашего эксперимента значение. Как видно, оно попадает в правую критическую область → H0 отклоняется и эффект признается статистически значим.

Чем p-value НЕ является.
 -Это не вероятность того, что H0 верна.
 -Это не вероятность того, что результат случаен.
 -Это не показатель силы эффекта.
 -Это не вероятность ошибки (вероятность ошибки I рода это α).
 -Это не связано с тем, что результат воспроизведётся (воспроизводимость зависит от мощности).

Запомните, что p-value конкретного теста это не вероятность ошибки в этом тесте, а мера того, насколько неожиданны наблюдаемые данные, если бы H₀ была верна. Предположим, что вы провели 20 A/B-тестов на разных фичах, и ни одна из них на самом деле реально не работает (H₀ на самом деле верна везде). Однако даже при идеально корректной методологии в среднем один из этих 20 тестов покажет p < 0,05 просто по случайности. Если интерпретировать p-value как вероятность того, что фича не работает, можно ошибочно решить, что раз p-value равна 0,03, то с 97% уверенностью фича работает, но это не то, что говорит тест. Тест говорит, что если бы фича не работала, такой или более сильный результат наблюдался бы только в 3% повторов эксперимента.

Если всё же вы так и не разобрались пока до конца, то запомните самое простое: если p-value меньше выбранного уровня значимости (обычно 0,05), нулевая гипотеза отвергается. Это значит, что полученный результат признаётся статистически значимым.

Ошибки I и II рода

У ритуала есть неустранимая цена, и стохастические демоны это её олицетворение.
Ошибка I рода, или Демон Альфа. Подскажет, что эффект есть, когда его нет. Выбирая α = 0,05, вы соглашаетесь, что в 5% тестов Демон Альфа вас всё-таки обманет. И, например, по воле случая, в группу «B» зайдут чуть более активные покупатели и этот успех будет просто случаен.
Ошибка II рода, или Демон Бета. Замаскирует эффект, когда он есть. Выбирая мощность 0,8 (это 1 − β), вы соглашаетесь что, если эффект заданного размера реально существует, вы поймаете его в 80% запусков, а в 20% Бета спрячет вашу находку за статистическим «шумом», и вы даже не узнаете об этом.

Перед тем как рассматривать теорию дальше, скажу возможно шокирующий факт. Доля успешных экспериментов (так называемый win rate) в компаниях уровня BigTech на удивление мала и составляет всего от 10% до 20%! Если принять за правило, например, 15%, то мы можем посчитать, чему равна доля ложноположительных открытий (False Discovery Rate, FDR).

PPV

Предположим, из 1000 гипотез реальный эффект есть у 150, но до теста мы не знаем, у каких. Если мы выбрали мощность 0,8, Демон Бета позволит обнаружить только 120 из них (крайний левый вариант на рисунке). Вместе с этим у 850 гипотез эффекта на самом деле нет, но Демон Альфа сделает значимыми 42,5 из них (на рисунке я округлил до 43). То есть в 43 тестах из 1000, даже если вы сделали все идеально, вы ошибочно найдете эффект (что составит примерно 26% из всех тестов, где вы обнаружите эффект, 43/(43+120)). Другими словами, как бы вы ни старались провести «ритуал A/B-теста», но каждый четвёртый «успешный» по вашему мнению тест на самом деле будет ошибкой! И наоборот, вероятность того, что эффект действительно есть, если аналитик увидел значимый результат, составляет только 74%. В статистике этот показатель называется прогностическая ценность положительного результата (Positive Predictive Value, PPV).

4.2.3. Кинжал Статистической Значимости

Старший жрец протягивает вам подготовить Кинжал Статистической Значимости. Для проведения ритуала вам нужно активировать руны на клинке: «α», «1 − β», «N» и «MDE».

«Кинжал Статистической Значимости» это метафора нашей дисциплины расчёта параметров теста, выполненная до запуска теста.
1. Руна α, или уровень значимости. Наше соглашение с Демоном Альфа, или порог, ниже которого вы объявляете p-value значимым. Общепринятый стандарт, равный 0,05, выбран исторически, и может быть пересмотрен (например, в медицине это может быть 0,001).

2. Руна 1 – β, или мощность. Наше соглашение с Демоном Бета, или вероятность обнаружить эффект, если он реально существует и равен заданному минимально детектируемому эффекту (MDE). Общепринятый стандарт, равный 0,8, тоже договорённость. Например, мощность теста 0,8 при MDE 2% означает, что, если фича реально даёт +2%, мы поймаем это в 80% запусков такого эксперимента.

3. Руна N, или размер выборки, необходимый для обнаружения нужного эффекта (MDE). Но для расчёта, кроме α и β, будут нужны ещё σ и MDE. Для t-теста сравнения средних формула размера группы:
  N = 2σ² (z₁₋α/₂ + z₁₋β)² / MDE²
  • При α = 0,05 и мощности 0,8 множитель (z₁₋α/₂ + z₁₋β)² = (1,96+0,84)² ≈ 7,85, отсюда популярное соотношение N ≈ 16σ²/MDE² на одну группу (то есть в обеих группах вместе суммарно потребуется примерно вдвое больше пользователей). Важно, что MDE в этой формуле это абсолютная величина, в тех же единицах, что и σ. Если бизнес формулирует MDE в процентах (например, +1,5% к ARPU), его сначала нужно перевести в абсолютные единицы через среднее базовое значение (baseline).
  • Для пропорций (PU, Retention) σ² заменяется на p(1−p), где p это базовая конверсия. Это приближение справедливо, когда эффект небольшой и p₁≈p₂; при большом ожидаемом сдвиге конверсии точнее использовать отдельные p₁(1−p₁) и p₂(1−p₂) для каждой группы.

4. Руна MDE (Minimum Detectable Effect) это бизнес-величина, тот эффект, начиная с которого фича окупает свою разработку, поддержку и риски. И здесь начинаются проблемы. Допустим, вам необходимо «+1,5%» по ARPU. Вы считаете N и получаете 5 миллионов пользователей на группу, а у вас в месяц столько нет во всей игре. Что тогда делать, выбрать MDE побольше? Но ваша фича не обязана давать высокие значения только для того, чтобы вам удобно было их искать. Вы можете снизить дисперсию (метод CUPED не включен в главу), заменить метрику на другую (например, PU%) или просто честно признать, что этот тест будет бесполезен.

Поправка на ratio-метрики

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

Выглядит так, что ARPU и ARPPU это не должны быть ratio-метрики, ведь они нормированы на игрока или плательщика. Но это не совсем так, проблема как раз в сильном перекосе с числом плательщиков в F2P игре, когда, фактически, доля плательщиков в каждой из групп в тесте становится величиной случайной и очень низкой. Строго говоря, для классического t-теста в геймдеве остается совсем мало места: метрики игрового времени, вовлеченности, конверсии, LT и Retention. Далее мы рассмотрим, что же делать с ratio-метриками.

Сколько ждать

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

4.2.4. Дизайн эксперимента

Что еще кроме «Кинжала Статистической Значимости» нам необходимо зафиксировать до запуска.

Гипотеза: формулировка, которую можно опровергнуть

Корректная формулировка гипотезы должна быть достаточно конкретна, например, «новый туториал увеличит Retention D1 (удержание в первый день после первого входа) новых игроков на ≥1.5 п.п. в течении 14 дней при сохранении ARPU D7 на текущем уровне». В гипотезе есть метрика, сегмент для тестирования, размер эффекта, период теста, условие-ограничитель и эта гипотеза проверяема. Например, если через 14 дней Retention D1 новых игроков вырос на 0,3 п.п. то гипотеза не подтвердилась, и никакие «зато длительность сессии подросла» этого не отменяют.

Не только основная метрика

 - Главная метрика (Primary): метрика которую мы улучшаем, она одна и для нее проводится тест.
 - Диагностические или вторичные (Secondary): метрики, которые помогают интерпретировать механизм эффекта, как возможность для будущих тестов, но в эксперименте они не влияют на результат теста.
 - Барьерные или проверочные (Guardrail): метрики, которые не должны просесть по итогам теста, например, выручка. Нужны для контроля, что локальная оптимизация не вредит успеху всей игры. Кстати, эти метрики также подвержены Стохастическим Демонам и, если таких метрик много, то какая-то из них наверняка сработает просто случайно. Предположим, у вас 40 таких барьерных метрик, тогда вероятность сработать хоть одной из них (если α=0,05) равна 1-(1-0,05)⁴⁰ = 0,87. То есть вы заблокируете 87% тестов. Самое простое решение это поправка на множественные сравнения Бонферрони, когда α делят на число попарных метрик (в нашем примере α_new=0,05/40).

A/A-тест

A/A-тест это предварительный эксперимент, где обе группы не получают воздействия, а значит если все сделано правильно, то мы не должны увидеть статистической разницы в результатах между группами (если бы не Демон Альфа, конечно).
Что мы проверяем таким способом:
 1. сплит система делит на группы неотличимые по составу;
 2. отсутствуют проблемы с логированием и расчетами;
 3. метрики в группах не отличаются значимо;
 4. из-за Демона Альфа при обнаружении разницы повторите A/A тест многократно, ошибка не должна быть чаще α.

4.2.5. Разбивка на группы

Рандомизация это ключевое условие A/B-теста, как делить пользователей правильно, и как проверять, что деление не сломалось.

Бакетирование и хеширование

Самый простой способ это случайным образом разделить всех игроков на группу A (контроль) и B (тест). Работает, возможно, не плохо, но предпочтительнее делать иначе: всех игроков поделить на группы (бакеты, buckets), например, 1000, а в каждом эксперименте использовать только свой диапазон из них. Для разделения можно использовать хеш-функцию от идентификатора игрока user_id, но с подмешиванием некоторого уникального ключа эксперимента (salt), например, «tutor_2026». В результате чего получается уникальная строка «user_12345_ tutor_2026», после чего строка хеш-функцией превращается в число (hash), которое делим с остатком (%) на общее количество групп (N_buckets).
bucket = hash(user_id + experiment_salt) % N_buckets

Хеш-функция превращает строку в псевдослучайное число детерминированно, другими словами, один и тот же вход дает всегда один и тот же выход. Это даёт воспроизводимость (бакет можно пересчитать в любой момент в любом сервисе) при статистической случайности распределения. А cоль (salt), уникальная строка конкретного эксперимента, подмешивается в хеш, чтобы один и тот же игрок не попадал всегда в группу control/treatment каждого теста. Тогда группа, условно, «бакеты 0–99» не будет со временем систематически отличается от «100–199» по накопленному опыту.

Стратификация

Чистая рандомизация балансирует группы примерно равномерно, но в каждом конкретном тесте возможен случайный дисбаланс и, как следствие, результаты теста будут определяться соотношением групп, а не воздействием. Поэтому необходимо соблюдение баланса ключевых сегментов в каждой группе или стратификация. Далее в этой главе мы еще рассмотрим это подробнее. А пока короткий план.
 1. Выбираем ключевые признаки (ковариаты) которые предопределяют характер взаимодействия с treatment, из-за чего метрика сильно различна, например, платформа, гео, вовлеченность, прогресс в игре, платёжный статус.
 2. Разбиваем аудиторию на сегменты (или страты) по комбинациям ковариат (iOS × Tier-1 × платящий, …).
 3. Внутри каждого сегмента (или страты) независимо делим пользователей на control/treatment в нужной пропорции.
Как результат, состав групп по стратам идентичен, и на метриках, сильно зависящих от ковариат (например, выручка сильно зависит от прошлого платёжного статуса), выигрыш в мощности теста будет ощутимый. Если вы не заложили этого в сплитование заранее, стратификацию можно сделать и после проведения теста.

Ранжированная змейка для платёжных данных

Мы уже рассматривали тест на платящей аудитории и код ранжирования платящих «змейкой», когда сортировали платящих по убыванию исторических платежей и раздавали в группы по очереди, так что соседние по рангу и максимально похожие по платёжной силе игроки гарантированно расходятся в разные группы. Но, в целом, если ваша выборка сильно скошена не по платежам, а по другой важной для теста метрике, то аналогичный подход с ранжированием и последовательным отбором можно применить и там.

Что может пойти не так

 • Вы наблюдаете статистически значимое расхождение между ожидаемым и фактическим соотношением размеров групп, не смотря на то что делали все правильно (Sample Ratio Mismatch, SRM). Возможно, что механизм сплитования выборочно теряет или добавляет пользователей в одну из групп, а раз он влияет на попадание в выборку, он почти наверняка коррелирует с метриками.
 • Результат может быть неверен, если сплит должен был быть не на аккаунт. Предположим, что играть в вашу игру можно с разных устройств или у одного игрока несколько аккаунтов, как следствие, игрок получает разные группы на разные аккаунты.
 • Назначение группы «тест» настроено на клиенте без проверки на сервере, и это может привести к тому, что игроку присвоена группа, но сервер о ней не знает и фичу не показывает, но вы об этом не узнаете.

4.2.6. Бутстрап и тест с перестановкой (Permutation test)

Возможно, лучшее что есть в статистике A/B тестов это бутстрап и тест с перестановкой (Permutation test). Они не защитят вас от плохого дизайна эксперимента (плохой дизайн одинаково сломает вам любой статистический тест), если не брать в расчет усложнение расчета, они не хуже условного t-теста и часто строго лучше: они не требуют допущения о нормальности выборочного среднего и корректно обрабатывают любую форму распределения.

Вся частотная статистика построена вокруг одного мысленного эксперимента а что, если бы мы набрали выборку из генеральной совокупности заново, то отличался бы полученный результат?. Бутстрап обходит это ограничение оригинальным способом: раз мы не можем повторно взять выборку из генеральной совокупности то давайте сделаем новую выборку с возвращением (ресэмплинг) из исходной. Действительно, распределение исходной выборки единственная доступная нам оценка истинного распределения. Тогда многократный ресэмплинг из неё имитирует ресэмплинг из генеральной совокупности, и распределение статистики по новым полученным выборкам усредняет истинное выборочное распределение.

Потренируемся в написании кода на Python.
import numpy as np импортируем numpy для генератора случайных чисел (np.random.choice) и работы с массивами
def bootstrap_mean(data, n_iterations=10000): Определяем функцию с двумя параметрами: data (исходная выборка, обычно numpy.array или pandas.Series с сырыми значениями метрики, например ARPU по каждому игроку в группе) и n_iterations=10000 (сколько раз мы будем пересэмплировать данные).
boot_means = [] Задаем пустой список, в который мы будем складывать результат расчета по каждой итерации. В конце в нём окажется 10К результатов расчетов среднего.
n = len(data) Используем размер исходной выборки чтобы каждый ресэмпл был того же размера n.
for _ in range(n_iterations): Чистый Python цикл повторяется n_iterations раз, а символ _ вместо переменной это питоновская идиома, означающая «переменная цикла не используется внутри тела». Ведь нам не важен номер итерации, важно само повторение.
Дальше идет сердце всего алгоритма бутстрапа
  resample = np.random.choice(data, size=n, replace=True) Используем импортированный генератор случайных чисел и задаем ему аргументы: data (источник данных), size=n (сколько элементов выбрать), replace=True (означает выбор с возвращением, когда элемент выборки выбран, но кладётся обратно в выборку и может быть выбран снова, это критично важное требование).
  boot_means.append(resample.mean()) Считаем среднее (.mean()) по получившемуся ресэмплу и добавляем это одно число в список boot_means. После 10К итераций в списке будет 10К средних, которые могли бы получиться, если бы мы действительно взяли 10К выборок из той же генеральной совокупности.
return np.array(boot_means) Превращаем обычный питоновский список в numpy-массив для удобства работы

Например, нам необходимо посчитать нашей функцией (bootstrap_mean) 95% доверительный интервал для выборки (data).
boot_result = bootstrap_mean(data)
ci_lower = np.percentile(boot_result, 2.5)
ci_upper = np.percentile(boot_result, 97.5)

Как бутстрап применяется к тесту гипотезы
 1. Бутстрапим отдельно контрольную и тестовую группу и получаем два распределения (по 10K средних в каждом).
 2. Считаем разность X̄test[i] − X̄control[i] для каждой из 10K пар, то есть получаем распределение разницы.
 3. Если 95% доверительный интервал этой разницы не содержит ноль, то эффект статистически значим.

Возможен и второй вариант с перестановкой (Permutation test). Идея предельно простая. Если H₀ верна и никакого реального эффекта нет, тогда ярлыки «A» и «B» на самом деле произвольны: разбиение игроков на две группы не должно систематически влиять на значение метрики. Значит, если мы перемешаем эти ярлыки случайным образом много раз и каждый раз посчитаем разницу средних между «новым A» и «новым B» то мы получим распределение того, какие разницы средних возникают просто от случайного разбиения, без всякого реального эффекта. Наблюдаемая (реальная) разница средних затем сравнивается с этим распределением: если она редкий гость среди случайных перестановок, значит, разбиение на группы не было случайным по своей сути, а отражало реальный эффект и нулевая гипотеза не верна. Фактически, мы буквально считаем p-value по определению, моделируя в какой доле случайных перестановок разница оказалась настолько же или более экстремальной, чем реально наблюдаемая.

Порядок действий в тесте с перестановкой
 1. Считаем разницу метрик между A и B группами, которую нужно проверить на значимость.
 2. Объединяем группу A и B, стираем деление на группы и складываем все наблюдения (и контроль, и тест) в один общий массив. Это прямое воплощение H₀: если группа не влияет на метрику, то все они просто взаимозаменяемые наблюдения из одного распределения.
 3. Делаем перемешанную копию массива, где каждый элемент остаётся и происходит просто случайная перестановка порядка.
 4. Режем перемешанный массив на две части ровно тех же размеров, что и исходные группы. Поскольку порядок был случайным, в «новый A» с равной вероятностью может попасть любой игрок хоть из реального A, хоть из реального B.
 5. Считаем разницу средних для одного случайного разбиения и сохраняем. Повторяем цикл, например, 10К раз и получаем распределение разниц, которые возникают исключительно от случайности разбиения, без всякого реального эффекта (потому что сами метки были перемешаны случайно).
 6. Сравниваем, как часто разница из случайной перестановки оказывается настолько же или более экстремальной, чем реально наблюдаемая. Для двустороннего теста (когда заранее не знаем знак ожидаемого эффекта, что почти всегда так) сравнивать нужно модули разностей: считаем долю перестановок, где |permuted_diff| ≥ |observed_diff|. Эта доля и есть p-value по определению как вероятность увидеть такой же или более экстремальный результат, если H₀ верна.

В чем разница между классическим бутстрапом и тестом перестановок
 • В бутстрапе мы ресэмплируем каждую группу отдельно от себя же (контроль ресэмплируется из контроля, тест из теста), сохраняя структуру групп.
 • В permutation test мы, наоборот, стираем структуру групп, объединяя их, потому что сама идея теста в том, чтобы смоделировать мир, где деления на группы не существует.

4.2.7. Ratio-метрики и зачем что-то еще если есть бутстрап

Бутстрап честен и универсален, но его цена в многократном проходе по данным. Для разового анализа аналитика это несущественно, но для продуктового решения лишняя нагрузка может стать критичной. Вместе с этим, мы не можем применить быстрый t-тест к ratio-метрикам, а это почти все главные метрики F2P!

Напомню, что ratio-метрика в общем смысле это отношение сумм двух случайных величин по группе:

(Σᵢ₌₁ⁿ Yᵢ) / (Σᵢ₌₁ⁿ Xᵢ)

Например, для ARPPU: Yᵢ — платежи игрока i, Xᵢ — индикатор что игрок платит (0/1). Проблема, в том, что Yᵢ и Xᵢ коррелируют на уровне одного игрока, ведь платящий игрок вносит вклад и в числитель, и в знаменатель одновременно, что нарушает правила применения t-теста. Это проблема тестировать не только ARPPU, но и, например, ARPDAU, средний чек, конверсию из открытия магазина в покупку, конверсию во вторую покупку, прогноз ROAS, среднюю длину сессий, среднее число сессий в день или матчей за сессию, win rate (победы / число матчей) и много других важных метрик геймдева.

Вместе с этим, математики подготовили решение задолго до современных проблем с A/B тестами (разложение в ряд Тейлора, если вы еще помните такое с первого курса Матана ;) ). Но сделать из этого два элегантных решения для A/B тестов предложили ученые Майкрософт (Delta-метод, Alex Deng и другие) и Яндекса (Линеаризация, Roman Budylin, Alexey Drutsa, Ilya Katsev, Valeriya Tsoy) в 2018. Согласно результатам команды Яндекса на 390 реальных A/B-тестах, при линеаризации они получили +34% к чувствительности тестов по сравнению с bootstrap и delta-методом. Как следствие, имеет смысл рассмотреть линеаризацию как основной метод по умолчанию для регулярных ratio-метрик, а бутстрап применять как второе мнение. Но F2P вряд ли был среди тестов Яндекса, поэтому сейчас мы можем ограничиться только бутстрапом.

Bootstrap для ratio-метрик
Ключевой принцип bootstrap для ratio-метрик в том, что единица ресэмплинга (числитель и знаменатель), берутся привязанными к конкретному игроку. Мы генерируем n индексов с возвращением, и тянем оба значения (и числитель и знаменатель для выбранного игрока) по этим индексам синхронно, и пересчитываем ratio-метрику, суммируя числитель и знаменатель по всей выборке целиком. Важно, что берем не среднее личных ratio каждого игрока, а считаем метрику отношений сумм числителей в выборке к сумме знаменателей в выборке. Это сохраняет внутреннюю корреляцию числителя и знаменателя (платящий пользователь несёт и единицу в знаменатель ARPPU, и свою выручку в числитель).

Потренируемся с Python еще раз. Предположим, у нас есть датафрейм с двумя столбцами revenue и is_payer.
import numpy as np
numerator = df['revenue'].values наш числитель, технически запись numerator = df['revenue'] тоже сработает
denominator = df['is_payer'].values наш знаменатель

def bootstrap_ratio(numerator, denominator, n_iterations=10000): Функция принимает два массива одинаковой длины, где numerator это числитель по каждому игроку (например, revenue), а denominator это знаменатель по каждому игроку (например, is_payer: 0 или 1). Критически важно, что индекс i относиться к одному и тому же игроку.
boot_ratios = [] Пустой список куда будем складывать метрику на каждую итерацию.
n = len(numerator) Каждый ресэмпл будет по размеру как в оригинале
for _ in range(n_iterations): Цикл ресэмплинга повторяем, например, 10К раз.
  idx = np.random.choice(n, size=n, replace=True) Здесь случайно выбираем индексы, ведь у нас два связанных массива и нам нужно, чтобы для одного игрока мы взяли согласованную пару его revenue и is_payer.
  resample_num = numerator[idx] Берем числитель с индексом idx.
  resample_denom = denominator[idx] Берем знаменатель с индексом idx.
  if resample_denom.sum() > 0: Проверка на деление на ноль, вдруг мы случайно не выбрали ни одного плательщика
   boot_ratios.append(resample_num.sum() / resample_denom.sum()) Считаем ratio-метрику по всему ресэмплу целиком и сохраняем в список
return np.array(boot_ratios) Превращаем питоновский список в numpy-массив для удобства дальнейшей работы, как и ранее.

Python-библиотеки для планирования и анализа тестов
Обычно, вы не будете писать сами код t-теста или бутстрапа и можете просто воспользоваться готовыми библиотеками в Python.
 • scipy.stats Самая низкоуровневая, но универсальная библиотека сразу содержит все необходимое (включая бутстрап и перестановочный тест), но без специфики только под A/B-тесты.
 • statsmodels Классическая библиотека, есть расчет необходимого размера выборки и тест для пропорций.
 • tea-tasting Новая специализированная A/B-библиотека. Есть t-test, бутстрап, delta-метод для ratio-метрик, снижение дисперсии через CUPED/CUPAC, анализ мощности, множественные сравнения и симуляция экспериментов, включая A/A-тесты. Полезно, что библиотека умеет считать статистику прямо на стороне БД, то есть не нужно предварительно тащить сырые данные по миллионам игроков в Python, всё считается через SQL.

Для квази экспериментов тоже есть несколько библиотек которые помогут, когда A/B тест не применимы (CausalImpact, DoWhy, CausalML (для uplift-моделирования)).

4.2.8. A/B тест на смешанной аудитории

В воздухе Пещеры Оракула висят неподвижные капли: подземная река, текущая сквозь Пещеру, давно остановилась, чтобы не мешать тем, кто слушает. Старший жрец сказал ученикам: представьте врача, который пришёл в палату, не задал больным ни одного вопроса, но тем, кто сидит слева дал лекарство, а тем, кто справа дал плацебо. Ему неважно что у одного гипертония, у другого пневмония, у третьего перелом, а у четвёртого аллергия, он хочет увидеть работает ли его лекарство. Через две недели врач вернётся, измерит среднее улучшение по палате и напишет в отчёте: «препарат показал эффект 2.3% (p = 0.04), рекомендую к применению». Таков и ваш A/B-тест на смешанной аудитории, всего лишь кривляние в свете факелов – загадочное, технически безупречное и совершенно бессмысленное.

Этот короткий раздел, возможно, концептуальное сердце статьи. Если из всей главы вы вынесете одну мысль, пусть это будет она. Метафора в разделе не карикатура на медицину. Перечитайте ее, заменив «палату» на «MAU», «лекарство» на «новый оффер», «болезни» на «игровые или платежные сегменты», а «эффект» на рост ARPU D14. Получился отчет большинства A/B-тестов в F2P, проведённых без сегментной разметки. Рандомизация безупречна, статистика считалась честно при помощи лучших алгоритмов, вот только диагноза не было изначально. Кстати, рассмотренный ранее парадокс Симпсона был частным случаем такой ошибки.

Рандомизация не защищает от бессмысленности теста на смешанной аудитории. Она гарантирует несмещённость для среднего эффекта у определенной смеси разных сегментов аудитории (ATE, Average Treatment Effect), но итоговый результат будет определяться составом выборки и не воспроизведется на другом составе. Например, если эффект на одном сегменте +15%, на другом −10%, а на третьем 0%, то эффекты скомпенсируют друг друга и рандомизация добросовестно выдаст вам средневзвешенный ноль плюс шум, который вы добросовестно измерите. Оракул вам не солгал. Он ответил на вопрос «каков средний эффект на аудитории данного состава», но этот ли вопрос вы хотели задать на самом деле?

Почему это опаснее, чем кажется на первый взгляд
1. Значимый ATE не гарантирует пользы никому конкретно. Статистическая значимость усреднённого эффекта не означает, что эффект реален и стабилен. Она означает лишь, что усреднённое число статистически отличается от нуля при том, что для большинства подгрупп реальный эффект может быть либо нулевым, либо противоположного знака.
2. Опасность false negative на уровне продукта. Ещё хуже обратная ситуация: если положительный эффект у одной важной подгруппы (например, у китов) компенсируется небольшим отрицательным эффектом у массовой подгруппы (казуальные игроки, которых численно намного больше), усреднённый эффект может оказаться незначимым вообще и фичу отклонят, хотя для конкретного ценного сегмента она реально работала.
3. Врач так и не узнает, что натворил. Самое тревожное в нашей метафоре то, что врач не измеряет побочный вред пациенту с переломом отдельно, он видит только агрегированное среднее улучшение состояния по всей палате. Если считать только ARPU в целом, вы можете не заметить, что фича резко ухудшила retention у казуальных игроков, потому что в общем ARPU это утонуло на фоне роста выручки от китов, а retention вообще может считаться отдельной метрикой, на которую никто не посмотрел вовремя.

В использованной метафоре врач должен поставить диагноз перед тем как дать лекарство, задать себе вопрос о том, почему именно это лекарство должно помочь именно этому больному. Аналитик должен сделать тоже самое.
 • Глубокий анализ поведения аудитории, который мы рассмотрели в прошлой главе, позволит вам провести сегментацию до запуска теста с обоснованием почему воздействие должно оказать влияние именно на этот сегмент. В тесте анализируйте эффект внутри каждого сегмента отдельно. Важная ловушка здесь это множественные сравнения. Если вы гоняете тест по 10 сегментам, снова всплывает проблема Демона Альфа, которую мы разбирали раньше. Из-за чего вы почти гарантированно найдёте хотя бы один сегмент со значимым эффектом случайно.
 • Алгоритмы машинного обучения и, в частности, uplift-моделирование могли бы помочь предсказывать именно разницу между тем, что было бы с фичей и без неё для каждого игрока. Что, по сути, автоматизированный, статистически корректный способ найти на кого фича окажет необходимое воздействие. Рассмотрим это далее в главе по машинному обучению.
 • Стратифицированная рандомизация на этапе дизайна эксперимента позволит гарантировать баланс по ключевым сегментам между группами (стратификация по типу плательщика или паттерну). Это упрощает последующий сегментный анализ, потому что вы заранее знаете, что сегменты сбалансированы между control/test, а не полагаетесь на удачу большой выборки и неизменный состав аудитории с течением времени.

4.2.9. Когда A/B тест бесполезен

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

Недостаточная статистическая мощность
Небольшая игра или узкий сегмент, когда по формуле N≈16σ²/MDE² реально достижимое N физически меньше требуемого для разумного MDE, тест не даст ответа никогда, сколько бы вы его ни продлевали в разумных пределах. В таких случаях лучший выбор это старый добрый предварительный анализ «методом внимательного всматривания», экспертная оценка, и последующее корректное сопоставление до и после.

Системные и сетевые эффекты
В основе логики A/B теста базовое допущение, что мы сравниваем два независимых распределения (SUTVA, Stable Unit Treatment Value Assumption). Однако, ваш юнит рандомизации (игрок) в F2P своим поведением может влиять на поведение другого игрока.
 • Кланы/социальные механики. Игрок из тестовой группы с новой боевой механикой может влиять на весь клан (общие рейды, кооперативные бои, чат) из контрольной группы. Так же и, например, блогер может попасть в тестовую группу и поделиться с аудиторией тем, что у него в качестве награды за Ивент отображается более ценный приз чем у остальных.
 • PvP-матчмейкинг. Если фича меняет силу игроков тестовой группы, это напрямую влияет на исход матчей контрольных игроков, с которыми они матчатся.
 • Внутриигровой рынок/аукционы. Изменение цен или доступности предметов для тестовой группы меняет рыночное предложение/спрос, что отражается на ценах, которые видит контрольная группа.
 • Изменение курса обмена внутриигровой валюты. Если валюта торгуется на едином рынке между игроками, изменение для одной группы моментально влияет на цены, которые видит другая.

Для развитой онлайн игры сложно полностью избежать данной проблемы, но возможна смена юнита рандомизации на группу (клан/сервер целиком), формирование синтетической группы похожих игроков (synthetic control), либо чередование состояния во времени для всего сервера (switchback), либо географическая локализация. В целом, квазиэксперименты из прошлой главы могут стать вашими основными способами решения этих проблем.

Эффекты за пределами разумной длительности теста
Возможно, что реальный эффект фичи проявляется не за 1-2 недели, а за месяцы или кварталы.
 • Влияние на бренд/репутацию игры. Агрессивная монетизация может дать краткосрочный рост ARPU, но спровоцировать негативные отзывы и снижение органического притока новых игроков.
 • Каннибализация будущих покупок. Скидочная акция может поднять выручку в моменте теста, а на горизонте квартала окажется, что общая LTV не изменилась или упала.
Вместо A/B только на выручку за период теста необходим анализ изменения прокси-метрик, которые заранее провалидированы как предикторы долгосрочного исхода либо используем прогнозные модели LTV (с оглядкой на риск, что модель LTV не видела таких сдвигов поведения ранее).

Все или ничего для всех
Часто перед командой продукта стоит вопрос нужна ли эта фича в принципе как основа продукта, а не насколько сильно фича повлияет на метрику. Изменение глобальной экономики, новый рейд, ребаланс классов, серверный Ивент. Тест, когда половина игроков с одной экономикой, а половина с другой в общем мире означал бы катастрофу для крупной онлайн игры. Сюда же относятся случаи, когда рандомизировать нельзя по внешним причинам: запуск на новой платформе, изменения, видимые в коммьюнити по определению (анонсированный Ивент), контрактные обязательства перед партнёрами.

Заключение

Теперь вы понимаете, что мир и его будущее состоит не из фактов, а из вероятностей и возможностей. Старший жрец провожает вас к выходу из пещеры Оракула и у самого порога произносит: «Вы запомнили руны на Кинжале, научились слышать стохастических демонов, понимаете, как задать Оракулу правильный вопрос. Вы овладеете бутстрапом и линеаризацией за месяц усердной практики, но помните, что это лишь звено в большем жизненном цикле развития. Сам по себе священный ритуал, оторванный от жизненного цикла, не приносит пользы.

Продуктовый цикл работает при непосредственном участии аналитика: вы изучаете данные и метрики, проводите анализ, находите проблему или возможность, доносите это до команды, с вашим участием происходит проектирование изменений, разработка выкатывает обновления на прод и дальше приходит время оценить успешность. И A/B-тест живёт в заключительном звене цикла, в оценке результатов. Конечно, A/B-тест это мощный инструмент, но для узкого класса задач: оценки результатов контролируемых, изолируемых, обратимых изменений, эффект которых проявляется в разумные сроки на достаточно большой и однородной по возможности аудитории. За пределами этого класса задач, например, при системных изменениях или оценке долгосрочных эффектов настаивать на обязательном A/B-тесте лишь карго-культ, когда есть форма научного метода без содержательной способности дать честный ответ. Хороший аналитик должен уметь распознать эти границы так же уверенно, как и правильно посчитать сам тест.