Привет, Хабр!

Каждому аналитику знакомо: при анализе A/B‑тестов важно выжимать максимум информации из данных. Но высокая дисперсия шума часто заставляет нас наращивать выборку и затягивать эксперименты. Как ускорить получение результата и повысить чувствительность теста? Один из способов — сократить разброс метрики без изменения ее среднего. Классическая формула размера выборки показывает, что количество данных N прямо пропорционально дисперсии σ². Получается, уменьшая σ², мы автоматически уменьшаем нужный объем данных или можем детектировать меньший эффект при тех же N.

Поэтому методы снижения дисперсии в A/B‑тестах становятся очень востребованными. В индустрии известны разные приемы: стратификация, бутстреп, сложные байесовские тесты, и, конечно же, ковариатный подход (CUPED/CUPAC и т. д.). Мы остановимся на одном из самых простых и эффективных приемов — CUPED (Controlled‑experiment Using Pre‑Experiment Data). Его суть проста и понятна: перед экспериментом у каждого пользователя была определенная метрика (скажем, прошлые покупки), и мы можем использовать эту информацию, чтобы скорректировать итоговую метрику и снизить шум.

Идея CUPED формулируется через новую скорректированную метрику:


Ycuped=Y−θ X,

где Y — исходная метрика эксперимента (например, выручка в тесте), а X — ковариата, измеренная до эксперимента и коррелирующая с Y, но не зависящая от распределения пользователей на контроль/тест. Параметр θ обычно берут равным

\theta = \frac{\mathrm{cov}(Y,X)}{\mathrm{Var}(X)}​ — это оптимальный выбор, минимизирующий дисперсию разности. По сути

он равен коэффициенту регрессии YYY на XXX. Фактически мы вычитаем из результата ту часть, которую можно предсказать по предэкспериментальным данным. Как отмечено в Glovo Tech Blog, при любом θ разность средних для скорректированной метрики будет несмещенной, а минимум дисперсии достигается именно при \theta = \mathrm{cov}(Y,X)/\mathrm{Var}(X).

Ковариата X не должна зависеть от назначения варианта. Обычно берут именно данные до эксперимента, чтобы гарантировать независимость. Ковариаты — это показатели, измеренные до вмешательства и не зависящие от treatment. Часто просто используют ту же метрику, что и в эксперименте, посчитанную за прошлый период. Тогда при корректном применении средний эффект останется несмещенным, а дисперсия метрики существенно уменьшится.

Давайте посмотрим, как это воплотить на практике через SQL. Допустим, у нас есть две таблицы: во‑первых, user_activity с историей активности пользователей, где мы можем вычислить метрику до эксперимента (кавариату), и во‑вторых, та же таблица (или другая) с метриками во время эксперимента. Например, хотим сравнить суммарную выручку пользователя за период эксперимента и использовать выручку в прошлом месяце как X. В PostgreSQL можно написать следующий запрос, который агрегирует данные:

WITH pre AS (
    SELECT user_id,
           SUM(metric) AS metric_before
    FROM user_activity
    WHERE event_date BETWEEN '2024-12-01' AND '2024-12-31'
    GROUP BY user_id
), exp AS (
    SELECT user_id,
           variation,
           SUM(metric) AS metric_during
    FROM user_activity
    WHERE event_date BETWEEN '2025-01-01' AND '2025-01-07'
    GROUP BY user_id, variation
)
SELECT e.variation,
       COUNT(*) AS users_count,
       AVG(e.metric_during) AS avg_after,
       AVG(p.metric_before) AS avg_before
FROM exp e
LEFT JOIN pre p ON e.user_id = p.user_id
GROUP BY e.variation;

Запрос объединяет данные до (pre) и во время (exp) эксперимента по каждому пользователю. В результате мы получаем по каждой вариации (контроль/тест): число пользователей, среднее после и среднее до. По сути это основа — теперь можем переходить к расчету CUPED.

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

WITH data AS (
    SELECT e.variation,
           e.metric_during AS y,
           p.metric_before AS x
    FROM exp e
    LEFT JOIN pre p ON e.user_id = p.user_id
),
stats AS (
    SELECT variation,
           COUNT(*) AS n,
           AVG(y) AS ybar,
           AVG(x) AS xbar,
           STDDEV_SAMP(y) AS s_y,
           STDDEV_SAMP(x) AS s_x,
           CORR(x, y) AS r
    FROM data
    GROUP BY variation
)
SELECT
    variation,
    ybar AS original_mean,
    ybar - (r * s_y / s_x) * (xbar - (SELECT AVG(xbar) FROM stats)) AS cuped_mean
FROM stats;

В этом примере в CTE stats считаем нужные агрегаты: ybar, xbar, стандартные отклонения и корреляцию. Затем для каждой вариации (arm) вычисляем скорректированное среднее: по формуле из ANCOVA мы отнимаем (r s_y / s_x) (xbar - xbar_all), где xbar_all — среднее ковариаты по всем участникам. Результат — две группы со скорректированными средними. Мы видим, что благодаря учету предэкспериментальной метрики разброс снижается, а ожидаемая разность между средними (эффект) остается тем же.

Еще один вариант — сразу посчитать θ и применить его к каждому пользователю. Например:

WITH params AS (
  SELECT
    COVAR_POP(e.metric_during, p.metric_before) / VAR_POP(p.metric_before) AS theta
  FROM exp e
  JOIN pre p ON e.user_id = p.user_id
)
SELECT
  e.variation,
  AVG(e.metric_during - params.theta * p.metric_before) AS adjusted_avg
FROM exp e
JOIN pre p ON e.user_id = p.user_id
CROSS JOIN params
GROUP BY e.variation;

Здесь сначала вычисляем общий theta как ковариацию деленную на дисперсию ковариаты. Затем для каждой вариации усредняем Y - theta * X. По сути это то же самое: получаем скорректированные средние по группам.

Однако важно не забывать: ковариата должна быть действительно независима от treatment. Если вместо предэкспериментальной метрики взять что‑то, что меняется в ходе теста (или выбрать плохую ковариату), мы получим смещенный результат. LaunchDarkly особо подчёркивает: ковариаты — это то, что измерено до вмешательства. Если нарушение этого условия, то «эффект коврика» (как шуточно говорят) может превратить шум в ложный эффект.


В завершение: мы убедились, что легко можно повысить чувствительность теста, написав пару SQL‑запросов. Собирать предэкспериментальные метрики, рассчитывать θ и корректировать итоговую метрику — всё это укладывается в пару CTE. Такой приём доступен в любом SQL‑движке и уже применяется в аналитических платформах. Главное — следить за корректностью данных. Ковариата должна быть адекватной и независимой от назначения групп. Тогда ваши A/B‑эксперименты зашевелятся: эффекты начнут обнаруживаться быстрее и надёжнее. Удачи и удачных тестов!

Если вам близка тема экспериментов и анализа данных, обратите внимание на курс «Аналитик данных». На нем рассмотрите ключевые методы и инструменты работы с данными, включая подходы к проведению A/B‑тестов и уменьшению дисперсии метрик.

Готовы ли вы к серьезному обучению? Пройдите вступительный тест.

А если вы хотите подробнее изучить направления, связанные с аналитикой и анализом данных, рекомендуем ознакомиться с каталогом курсов, где представлены разные программы по аналитике — от базовых до продвинутых.

Комментарии (0)