TGViewer
Мир аналитика данных Мир аналитика данных @analysts_world · 4.56K subscribers
Post #337 1.65K
📊 А/Б-тесты. Считаем MDE до того, как запустим тест.

Хочу показать вам рабочий код для расчета минимального детектируемого эффекта (MDE) - это самый маленький истинный эффект, который тест сможет признать значимым.

🎯 На каком примере будем считать?

Покажу на примере конверсии (бинарная метрика). Её часто ставят как приемочную метрику. Она просто считается и легко интерпретируется.

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

⭐Пусть наша приемочная метрика - конверсия пользователей в заказ.
Нам нужны два простых поля на каждого активного пользователя:
● active_user_id - кто заходил в приложение за последние 2 недели;
● order_flg - совершил ли он целевое действие (1 - да, 0 - нет).
✅ Генерируем синтетический датасет на 50 000 пользователей с базовой конверсией 5%:
n = 50000                   
baseline_conversion = 0.05
np.random.seed(42)
active_user_id = np.arange(1, n + 1)
order_flg = np.random.binomial(1, baseline_conversion, size=n)
df = pd.DataFrame({'active_user_id': active_user_id,'order_flg': order_flg})
df.head()

Смотрим на фактические цифры:
active_users = df['active_user_id'].nunique()
users_with_order = df[df['order_flg']==1]['active_user_id'].nunique()

share_users_with_order = users_with_order / active_users

print('Активные клиенты:', active_users)
print('Клиенты с заказом:', users_with_order)
print('Конверсия в заказ:', round(share_users_with_order * 100, 2), '%')

✅Формула расчёта MDE
Я использую стандартный подход на основе z-критерия для двух пропорций
Это означает, что мы предполагаем: при больших выборках распределение разности конверсий между тестом и контролем стремится к нормальному (ЦПТ).
В функции n_total — это суммарное количество пользователей в обеих группах. То есть при n_total = 50 000 в каждой группе будет по 25 000 человек.
def mde_for_conversion(baseline, n_total, alpha=0.05, power=0.80, two_sided=True):
    """
    baseline - конверсия в контроле
    n_per_group - размер каждой группы
    """
    n_per_group = n_total / 2
    p = baseline
    # Квантили нормального распределения
    if two_sided:  z_alpha = norm.ppf(1 - alpha/2)
    else: z_alpha = norm.ppf(1 - alpha)
    z_beta = norm.ppf(power)
   
    se = math.sqrt(p * (1 - p) / n_per_group + p * (1 - p) / n_per_group)
    mde_abs = (z_alpha + z_beta) * se
    mde_rel = mde_abs / baseline * 100
    lower = baseline - mde_abs
    upper = baseline + mde_abs
    return mde_abs, mde_rel, lower, upper

Считаем mde
n_total = int(active_users) #всего пользователей для обеих групп
baseline_conversion =share_users_with_order
 
mde_abs, mde_rel, lower, upper = mde_for_conversion(baseline_conversion, n_total)
print("\n" + "=" * 60)
print("MDE ДЛЯ КОНВЕРСИИ (n_total=" + format(n_total, ",") + "):")
print("=" * 60)
print("Baseline:", round(baseline_conversion*100, 2), "%")
print("MDE: ±", round(mde_abs*100, 2), "п.п. (", round(mde_rel, 2), "% )")
print("Диапазон:", round(lower*100, 2), "% –", round(upper*100, 2), "%")

📈 Смотрим на вывод и делаем выводы 😀

MDE ДЛЯ КОНВЕРСИИ (n_total=50,000):
Baseline: 4.94 %
MDE: ± 0.54 п.п. ( 10.99 % )
Диапазон: 4.4 % – 5.48 %

Как это читать?
1️⃣ Сейчас базовая конверсия - 4.94%.

2️⃣ Мы сможем статистически значимо зафиксировать изменение, только если конверсия в тесте выйдет за пределы коридора 4.4% – 5.48%.

3️⃣Это означает, что тест «увидит» только прирост от 10.99% относительно базового значения.

Если ваше дизайн-решение даёт +5% к конверсии (а это очень даже неплохо!), то при 50 000 пользователях тест не сможет это доказать. 😭 Вы получите p-value > 0.05 и решите, что эффекта нет, хотя он есть. Но чтобы «поймать» +5%, нужно кратно увеличивать выборку, например держать тест не две недели, а три.

📎 Вот ноутбук, чтобы Вы смогли поэкспериментировать.

Если нравятся такие посты с реальным кодом и разбором A/B, то ставьте огонечки 🔥😀
  • 🔥 28
  • ❤‍🔥 5
  • ❤ 3
  • ⚡ 1
More from @analysts_world
  1. Sep 21, 2026📊 Задачка с собеседования Ну что, по итогам голосования большинство хотят задачки и sql.…
  2. Sep 14, 2026Post #345
  3. Sep 14, 2026Что-то я тут прям зачастила с A/B тестами 😅 Смотрю на последние посты и такое чувство, чт…
  4. Sep 1, 2026🎒 С 1 сентября, друзья! Сегодня как раз отправила своих детей в школу – и вот это чувство…
  5. Aug 24, 2026Вне выборки Обычно здесь про SQL, Python и AB-тесты. Но не всё, что важно, попадает в выбо…
  6. Aug 20, 2026Fuckup Night от создателей Trisigma, Ares и karpov.courses Согласитесь, ивенты, где все де…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →