MDE (Minimum Detectable Effect, минимальный детектируемый эффект) — это наименьшее изменение метрики, которое A/B-тест способен статистически надёжно отличить от нуля при заданных уровне значимости α и мощности 1−β. Если реальный эффект меньше MDE, тест с высокой вероятностью его «не заметит» (получите незначимый результат, даже если различие есть). MDE задаётся ДО запуска теста и жёстко связан с размером выборки: чем меньший эффект вы хотите ловить, тем больше нужно наблюдений — зависимость квадратичная.
Базовая формула размера выборки на одну группу для сравнения средних:
n ≈ (z_{1−α/2} + z_{1−β})² · 2σ² / Δ²
где Δ — это MDE (абсолютный), σ² — дисперсия метрики, z — квантили стандартного нормального распределения. При стандартных α = 0.05 (двусторонний) и мощности 80% множитель (1.96 + 0.84)² ≈ 7.85 ≈ 8, поэтому на две группы часто пишут удобное правило n ≈ 16·σ²/Δ². Ниже разберём каждую часть, посчитаем сходящийся пример и покажем дельта-метод для метрик-отношений (CTR, конверсия на сессию, ARPPU).
Что такое MDE простыми словами?
Представьте, что кнопка «Купить» даёт конверсию 10%. Вы меняете её цвет и надеетесь на прирост. Вопрос не «есть ли эффект вообще», а «какой минимальный прирост мы вообще сможем задетектировать на нашем трафике». Если за две недели набирается 8000 пользователей на группу, тест уловит прирост примерно от +1.3 п.п. (с 10% до 11.3%). Прирост в +0.3 п.п. останется в шуме — тест покажет «не значимо», и вы ошибочно решите, что идея не работает.
MDE бывает двух видов, и их постоянно путают:
| Вид | Обозначение | Пример при base = 10% |
|---|---|---|
| Абсолютный | Δ (п.п.) | +2 п.п. → 12% |
| Относительный | Δ/p (%) | +20% → 12% |
+2 п.п. на базе 10% — это те же +20% относительно. В интерфейсах калькуляторов (например в статистике A/B-дизайна) чаще вводят относительный MDE, но в формулу подставляется абсолютный Δ. Ошибка на этом шаге искажает выборку в разы.
Как MDE связан с размером выборки и мощностью?
Три величины образуют жёсткий треугольник: размер выборки, MDE и мощность. Зафиксировав любые две, вы определяете третью. Практический смысл:
- Хотите ловить эффект вдвое меньше — выборка растёт в 4 раза (Δ в знаменателе в квадрате).
- Хотите поднять мощность с 80% до 90% — множитель (z-часть) растёт с ~7.85 до ~10.5, выборка растёт примерно на 34%.
- Уменьшаете α с 0.05 до 0.01 — z_{1−α/2} растёт с 1.96 до 2.58, выборка тоже увеличивается.
Мощность 1−β — это вероятность обнаружить эффект, если он на самом деле равен MDE. Стандарт индустрии — 80%: то есть в 1 случае из 5 вы «промахнётесь» мимо реального эффекта размером ровно в MDE. Для критичных решений берут 90%.
Обратите внимание: MDE — это не «эффект, который мы ожидаем», а «эффект, который мы гарантированно не пропустим». Часто продакт хочет прирост +5%, а трафика хватает только на MDE +8%. Это значит: даже если фича даст ожидаемые +5%, тест с большой вероятностью закончится «не значимо». Такой тест бессмысленно запускать — либо копите трафик дольше, либо смиритесь с меньшей мощностью.
Как посчитать размер выборки: пример со сходящимися числами
Возьмём конверсию. Базовая конверсия p₁ = 0.10, хотим детектировать относительный прирост 20%, то есть p₂ = 0.12, абсолютный MDE Δ = 0.02.
Для доли дисперсия σ² = p·(1−p). Возьмём консервативно p = 0.11 (среднее двух групп): σ² = 0.11·0.89 = 0.0979.
Подставляем в формулу на группу:
n = (1.96 + 0.84)² · 2 · 0.0979 / 0.02²
n = 7.849 · 0.1958 / 0.0004
n = 1.5369 / 0.0004
n ≈ 3842
Итог: ≈ 3842 пользователя на группу, ≈ 7684 всего. Проверим правилом 16·σ²/Δ² (на две группы): 16 · 0.0979 / 0.0004 = 3916 — те же ~3900 на группу, расхождение из-за округления множителя 7.85 → 8. Числа сходятся.
Теперь уменьшим MDE вдвое — ловим +10% относительно (Δ = 0.01):
n = 7.849 · 2 · 0.0979 / 0.01² ≈ 15368 на группу
Выборка выросла ровно в 4 раза (3842 → 15368). Это и есть квадратичная плата за чувствительность.
Обратная задача — «какой MDE потяну на имеющемся трафике». Пусть есть 5000 на группу:
Δ = (z_{1−α/2} + z_{1−β}) · sqrt(2σ²/n)
Δ = 2.80 · sqrt(2·0.0979 / 5000)
Δ = 2.80 · sqrt(0.00003916)
Δ = 2.80 · 0.006258 ≈ 0.0175
MDE ≈ 1.75 п.п., то есть относительно базы 10% вы уловите минимум +17.5%. Меньше — не увидите.
Что такое дельта-метод и зачем он для ratio-метрик?
Формула выше работает для средних и долей, где единица рандомизации совпадает с единицей метрики (пользователь → его конверсия). Но в аналитике полно ratio-метрик, где числитель и знаменатель считаются на разных уровнях:
- CTR = клики / показы (рандомизируем пользователей, а показов у каждого разное число);
- средний чек = выручка / заказы;
- конверсия сессий = заказы / сессии.
Здесь наблюдения внутри пользователя коррелированы, и наивная дисперсия занижена — тест даёт ложные срабатывания. Дельта-метод — это способ приблизить дисперсию отношения двух случайных величин через разложение Тейлора первого порядка. Для метрики R = X/Y (X — сумма числителя на юзера, Y — сумма знаменателя) дисперсия среднего оценивается так:
Var(R) ≈ (1/Ȳ²)·Var(X) + (X̄²/Ȳ⁴)·Var(Y) − 2·(X̄/Ȳ³)·Cov(X, Y)
Проще запомнить эквивалентную запись через относительные величины: дисперсия отношения ≈ R² · (CV²_X + CV²_Y − 2·ρ·CV_X·CV_Y), где CV — коэффициент вариации, ρ — корреляция числителя и знаменателя. Ключевой момент: игнорируя ковариацию (последний член), вы почти всегда переоцениваете дисперсию, а значит завышаете нужную выборку. Правильный дельта-метод даёт корректный σ², который дальше подставляется в ту же формулу размера выборки.
Данные для расчёта дисперсии удобно агрегировать SQL-ом до уровня пользователя перед выгрузкой в Python:
SELECT
user_id,
variant,
SUM(clicks) AS x, -- числитель на юзера
SUM(impressions) AS y -- знаменатель на юзера
FROM ab_events
WHERE test_id = 'button_color_v3'
GROUP BY user_id, variant;
Как посчитать всё это в Python (scipy / statsmodels)?
Для долей проще всего взять готовый расчёт мощности из statsmodels. Он вернёт практически ту же выборку, что мы считали руками (арксинус-преобразование Коэна даёт чуть меньше — оно немного эффективнее грубого нормального приближения с пулированной дисперсией).
import numpy as np
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize
# Базовая 10%, целевая 12% (MDE = +2 п.п. = +20% относительно)
p1, p2 = 0.10, 0.12
effect = proportion_effectsize(p2, p1) # эффект Коэна h
n = NormalIndPower().solve_power(
effect_size=effect,
alpha=0.05,
power=0.80,
alternative='two-sided',
)
print(round(n)) # ~3836 на группу — практически совпадает с ручным расчётом ~3842
Дельта-метод для ratio-метрики считается руками — это несколько строк на numpy по агрегированным на уровень пользователя данным:
import numpy as np
# x — клики на юзера, y — показы на юзера (из SQL выше)
def ratio_var(x, y):
x, y = np.asarray(x, float), np.asarray(y, float)
n = len(x)
xbar, ybar = x.mean(), y.mean()
var_x, var_y = x.var(ddof=1), y.var(ddof=1)
cov_xy = np.cov(x, y, ddof=1)[0, 1]
# дисперсия среднего отношения по дельта-методу
var_ratio = (var_x / ybar**2
+ xbar**2 / ybar**4 * var_y
- 2 * xbar / ybar**3 * cov_xy) / n
return xbar / ybar, var_ratio
r, var_r = ratio_var(x, y)
se = np.sqrt(var_r)
# доверительный интервал для CTR
print(r, r - 1.96*se, r + 1.96*se)
Полученный se (или дисперсию на уровень наблюдения) подставляете в ту же формулу размера выборки, что и для средних. Разница только в том, что σ² берётся дельта-методом, а не наивно.
Частые ошибки при работе с MDE
| Ошибка | Последствие |
|---|---|
| Путают абсолютный и относительный MDE | Выборка занижена/завышена в разы |
| Наивная дисперсия для CTR/ARPU | Ложные срабатывания (α по факту выше 5%) |
| MDE подгоняют под имеющийся трафик задним числом | Тест недомощный, «не значимо» ничего не доказывает |
| Мощность по умолчанию не проверяют | Половина эффектов пропускается |
| Считают выборку по финальной конверсии, а не по σ | Ошибка на волатильных метриках (выручка) |
Отдельно про выручку: у неё тяжёлый хвост, дисперсия огромная, поэтому MDE по ARPU часто получается неприлично большим. Помогает либо винзоризация, либо CUPED (снижение дисперсии по ковариате), либо переход на более стабильную прокси-метрику вроде конверсии.
Краткий чек-лист перед запуском теста
- Определите метрику и единицу рандомизации. Если метрика ratio — сразу закладывайте дельта-метод.
- Оцените σ² на исторических данных (2-4 недели до теста).
- Согласуйте с продактом MDE в относительных величинах, переведите в абсолютные.
- Зафиксируйте α = 0.05 и мощность (80% или 90%).
- Посчитайте n. Сравните с реальным трафиком: хватит ли за разумный срок?
- Если не хватает — либо снижайте требования к MDE, либо копите трафик, либо снижайте дисперсию (CUPED).
MDE — это честный ответ на вопрос «что мы вообще способны увидеть на нашем объёме данных». Считать его до теста дисциплинирует: половина «неудачных» тестов на самом деле просто недомощные, и грамотный расчёт выборки экономит недели впустую потраченного трафика.
Потренировать SQL-агрегацию для расчёта дисперсии можно в SQL-тренажёре, а numpy и scipy для дельта-метода — в Python-тренажёре. Смотрите также разбор смежных метрик: конверсия и retention, подборку задач по A/B-тестам и статью A/B-тесты на Python со scipy.stats.
Хотите отработать дизайн A/B-тестов на реальных задачах с проверкой решения и разбором — в Pro-доступе открыты все 425 SQL и 402 Python-задачи, кейсы и AI мок-собесы по статистике.