0Pricing
Coding Interview Prep · Урок

Распределение по группам A/B-теста и метрики

Объединение распределения участников эксперимента с результатами и вычисление метрик для каждого варианта

«Распределение по группам A/B-теста и метрики» — бесплатный урок Coding Interview Prep на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Coding Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Coding Interview Prep содержит 4 уроков всего.

Что проверяет вопрос об A/B-тестировании

Вопросы об A/B-тестировании проверяют, умеете ли Вы корректно соединять распределение участников эксперимента с результатами и вычислять корректную метрику для каждого варианта.

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

Какие две таблицы Вы получаете

Ожидайте таблицу распределения и таблицу результатов:

  • assignments(user_id, variant, assigned_at), где вариант — «контрольный» или «тестовый».
  • orders(user_id, order_id, amount, created_at) или обычную таблицу событий.

Распределение — источник истины для определения участников эксперимента. Результаты учитываются только в том случае, если пользователь есть в таблице распределения.

CREATE TABLE assignments (
  user_id     INT,
  variant     VARCHAR(20),
  assigned_at TIMESTAMP
);

CREATE TABLE orders (
  user_id    INT,
  order_id   INT,
  amount     NUMERIC,
  created_at TIMESTAMP
);

Начинайте с распределения и выполняйте LEFT JOIN с результатами

Главное правило: начинайте с таблицы распределения и выполняйте LEFT JOIN с результатами. Это сохраняет пользователей, включённых в эксперимент, но не совершивших конверсию, — они нужны для честного знаменателя.

INNER JOIN незаметно исключит пользователей без конверсии и завысит коэффициент конверсии.

SELECT
  a.user_id,
  a.variant,
  o.order_id
FROM assignments a
LEFT JOIN orders o
  ON o.user_id = a.user_id;

Подсчёт конверсии для каждого варианта

Коэффициент конверсии = пользователи с конверсией / назначенные пользователи для каждого варианта. В числителе считайте уникальных пользователей с конверсией, а в знаменателе — всех назначенных пользователей.

Используйте COUNT(DISTINCT ...) для пользователя из заказа, чтобы пользователь с тремя заказами всё равно считался одним пользователем с конверсией.

SELECT
  a.variant,
  COUNT(DISTINCT a.user_id)                              AS assigned,
  COUNT(DISTINCT o.user_id)                              AS converters,
  ROUND(100.0 * COUNT(DISTINCT o.user_id)
              / COUNT(DISTINCT a.user_id), 2)            AS conv_rate_pct
FROM assignments a
LEFT JOIN orders o ON o.user_id = a.user_id
GROUP BY a.variant;

Ловушка двойного распределения

Что произойдёт, если пользователь появится в таблице распределения дважды — по одному разу в каждом варианте? Теперь соединение посчитает его с обеих сторон, и эксперимент окажется искажён.

Интервьюеры специально добавляют такой случай. Защититесь от него: удалите дубликаты в распределении, оставив одному пользователю один вариант, обычно вариант из первого распределения, до выполнения соединения.

WITH dedup AS (
  SELECT user_id, variant,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY assigned_at) AS rn
  FROM assignments
)
SELECT user_id, variant
FROM dedup
WHERE rn = 1;

Учитывайте результаты только после распределения

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

Поместите это условие в предложение ON соединения LEFT JOIN, чтобы пользователи без конверсии по-прежнему сохранялись.

SELECT
  a.variant,
  COUNT(DISTINCT a.user_id) AS assigned,
  COUNT(DISTINCT o.user_id) AS converters
FROM assignments a
LEFT JOIN orders o
  ON o.user_id = a.user_id
 AND o.created_at >= a.assigned_at
GROUP BY a.variant;

ON и WHERE при соединении с результатами

Это гарантированный дополнительный вопрос. Если перенести o.created_at >= a.assigned_at в WHERE, Вы превратите LEFT JOIN во внутреннее соединение: строки, в которых пользователь ничего не заказал, имеют o.created_at = NULL, предикат получает значение UNKNOWN, и такие строки исчезают.

Оставляйте условия фильтрации результатов в ON, чтобы сохранять пользователей без конверсии в знаменателе.

Метрики выручки для каждого варианта

Помимо конверсии, интервьюеры спрашивают о выручке на пользователя (ARPU) и выручке на пользователя с конверсией. Сначала просуммируйте сумму, затем разделите её на правильный знаменатель.

ARPU рассчитывается по всем назначенным пользователям, а выручка на пользователя с конверсией — только по пользователям, разместившим заказ. Ясно указывайте, какой показатель нужен бизнесу.

SELECT
  a.variant,
  COUNT(DISTINCT a.user_id)                               AS assigned,
  COALESCE(SUM(o.amount), 0)                              AS revenue,
  ROUND(COALESCE(SUM(o.amount), 0)
        / COUNT(DISTINCT a.user_id), 2)                   AS arpu
FROM assignments a
LEFT JOIN orders o
  ON o.user_id = a.user_id
 AND o.created_at >= a.assigned_at
GROUP BY a.variant;

Двухуровневый шаблон агрегации

Если метрика — это «среднее число заказов на пользователя», не вычисляйте её за один проход: так Вы смешаете уровни детализации пользователя и заказа. Сначала агрегируйте данные до уровня пользователя, затем усредняйте их по пользователям.

Этот шаблон «сначала по пользователю, затем по варианту» использует правильный уровень детализации и часто помогает отличить сильного кандидата на собеседовании.

WITH per_user AS (
  SELECT a.variant, a.user_id,
    COUNT(o.order_id) AS orders_cnt
  FROM assignments a
  LEFT JOIN orders o
    ON o.user_id = a.user_id
   AND o.created_at >= a.assigned_at
  GROUP BY a.variant, a.user_id
)
SELECT variant, ROUND(AVG(orders_cnt), 3) AS avg_orders_per_user
FROM per_user
GROUP BY variant;

Полный обоснованный запрос

Объедините всё: оставьте первое распределение, начинайте с таблицы распределения, отфильтруйте результаты по времени в ON и выведите конверсию и ARPU для каждого варианта. Объясняйте назначение каждого ограничения по мере написания запроса.

WITH enrolled AS (
  SELECT user_id, variant, assigned_at
  FROM (
    SELECT user_id, variant, assigned_at,
      ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY assigned_at) AS rn
    FROM assignments
  ) x WHERE rn = 1
)
SELECT
  e.variant,
  COUNT(DISTINCT e.user_id)                            AS assigned,
  COUNT(DISTINCT o.user_id)                            AS converters,
  ROUND(100.0 * COUNT(DISTINCT o.user_id)
              / COUNT(DISTINCT e.user_id), 2)          AS conv_pct,
  ROUND(COALESCE(SUM(o.amount),0)
        / COUNT(DISTINCT e.user_id), 2)                AS arpu
FROM enrolled e
LEFT JOIN orders o
  ON o.user_id = e.user_id
 AND o.created_at >= e.assigned_at
GROUP BY e.variant;

Проверки, которых ожидают на собеседовании

Прежде чем приводить результаты, проверьте настройки эксперимента:

  • Размеры групп вариантов примерно сбалансированы? Разделение 90/10 вместо запланированного 50/50 указывает на ошибку.
  • Оказался ли какой-либо пользователь в обоих вариантах? Посчитайте пользователей, которым назначено более одного различного варианта.
  • Есть ли распределения, для которых невозможно сформировать окно результатов, например выполненные после отсечения данных?

Если предложить эти проверки без подсказки, это покажет аналитическую зрелость.

SELECT user_id, COUNT(DISTINCT variant) AS variant_count
FROM assignments
GROUP BY user_id
HAVING COUNT(DISTINCT variant) > 1;

Быстрая проверка

Вы вычисляете конверсию для каждого варианта, соединяя заказы с таблицей распределения с помощью LEFT JOIN, но помещаете o.created_at >= a.assigned_at в предложение WHERE. Что произойдёт?

Итоги: распределение участников A/B-тестирования и метрики

Теперь у Вас есть обоснованный алгоритм анализа экспериментов:

  • Считайте распределение источником истины и выполняйте LEFT JOIN с результатами.
  • Удаляйте дубликаты, оставляя одному пользователю один вариант (первое распределение).
  • Ограничивайте результаты по времени в предложении ON, а не в WHERE, чтобы сохранять пользователей без конверсии.
  • Выбирайте правильный знаменатель для конверсии, ARPU и выручки на пользователя с конверсией.
  • Сначала агрегируйте данные на уровне пользователя, когда вычисляете средние показатели на пользователя.
  • Выполняйте проверки корректности баланса групп и распределения пользователей между вариантами.

Далее: преобразование этих метрик для вариантов в прирост, статистическую значимость и защитные метрики.

Часто задаваемые вопросы

Урок «Распределение по группам A/B-теста и метрики» бесплатный?

Да — полный текст урока «Распределение по группам A/B-теста и метрики» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Coding Interview Prep, подпишись на CoddyKit PRO. Курс Coding Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «Распределение по группам A/B-теста и метрики»?

Объединение распределения участников эксперимента с результатами и вычисление метрик для каждого варианта Ты практикуешь Coding Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Coding Interview Prep?

Предыдущий опыт не требуется. Coding Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Распределение по группам A/B-теста и метрики»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Coding Interview Prep?

Да. Каждый урок Coding Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Построение многоэтапной воронки
  2. Упорядоченные события и временные окна
  3. Распределение по группам A/B-теста и метрики
  4. Прирост, значимость и контрольные показатели в SQL
← Назад к Coding Interview Prep