← Назад к вопросам

Что такое effect size и почему он важен наряду с p-value?

2.0 Middle🔥 191 комментариев
#A/B тестирование#Статистика и математика

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Effect Size и его роль наряду с P-Value

P-value и effect size — это два разных измерения, которые часто путают. P-value говорит "есть ли эффект", effect size говорит "насколько большой эффект". Понимание обоих критически важно для правильных выводов.

Что такое P-Value

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

  • P-value = 0.05: 5% вероятность ошибки типа I
  • Принимается условно (p < 0.05 = статистически значимо)
  • Проблема: зависит от размера выборки

Пример: Конверсия подскочила с 5.0% на 5.1%, n=1,000,000. Это даст p < 0.05, но эффект бесполезен!

Что такое Effect Size

Effect size — величина практического различия между группами. Это измерение абсолютно независимо от размера выборки.

Главная идея: Статистическая значимость ≠ Практическая значимость

Типы effect size:

1. Cohen's d (для непрерывных данных, например средний spending)

import numpy as np
from scipy.stats import ttest_ind

def cohens_d(group1, group2):
    """Рассчитывает Cohen's d effect size"""
    n1, n2 = len(group1), len(group2)
    var1, var2 = np.var(group1, ddof=1), np.var(group2, ddof=1)
    
    # Pooled standard deviation
    pooled_std = np.sqrt(((n1 - 1) * var1 + (n2 - 1) * var2) / (n1 + n2 - 2))
    
    # Cohen's d
    d = (np.mean(group1) - np.mean(group2)) / pooled_std
    return d

# Пример
control_spending = np.array([50, 55, 48, 60, 52, 49, 51])
variant_spending = np.array([58, 62, 55, 65, 60, 57, 59])

d = cohens_d(control_spending, variant_spending)
print(f"Cohen's d = {d:.3f}")

# Интерпретация Cohen's d:
# |d| < 0.2: маленький эффект
# 0.2 ≤ |d| < 0.5: малый эффект
# 0.5 ≤ |d| < 0.8: средний эффект
# |d| ≥ 0.8: большой эффект

2. Odds Ratio (для бинарных данных, конверсия, retention)

def odds_ratio(conversions_treatment, total_treatment, 
               conversions_control, total_control):
    """Рассчитывает odds ratio"""
    odds_treatment = conversions_treatment / (total_treatment - conversions_treatment)
    odds_control = conversions_control / (total_control - conversions_control)
    
    or_value = odds_treatment / odds_control
    return or_value

# Пример: новый UI повысил конверсию
converted_new = 520  # из 10,000
converted_old = 500  # из 10,000

or_val = odds_ratio(520, 10000, 500, 10000)
print(f"Odds Ratio = {or_val:.3f}")

# OR = 1.04 означает: шансы конвертиться в 1.04 раза выше (4% увеличение)

3. Relative Lift (самый понятный для бизнеса)

def relative_lift(treatment_rate, control_rate):
    """Процентный прирост"""
    return (treatment_rate - control_rate) / control_rate * 100

control_cr = 0.05      # 5%
treatment_cr = 0.0525  # 5.25%

lift = relative_lift(treatment_cr, control_cr)
print(f"Relative Lift = {lift:.1f}%")  # +5%

Почему p-value недостаточно

Проблема 1: P-value зависит от размера выборки

from scipy.stats import chi2_contingency

# Сценарий: конверсия выросла на 0.1 процентный пункт (5% → 5.1%)
# Эффект = 0.1 п.п. (практически незначительный)

for n in [1000, 10000, 100000, 1000000]:
    # Control: 5% конверсия
    control_conversions = int(n * 0.05)
    # Treatment: 5.1% конверсия
    treatment_conversions = int(n * 0.051)
    
    # Chi-square test
    contingency_table = [
        [control_conversions, n - control_conversions],
        [treatment_conversions, n - treatment_conversions]
    ]
    
    chi2, p_value, dof, expected = chi2_contingency(contingency_table)
    
    print(f"n={n}: p-value={p_value:.4f}, Effect=0.1% (одинаков везде!)")

# Результат:
# n=1000: p-value=0.9234  ← не значим
# n=10000: p-value=0.2893 ← не значим
# n=100000: p-value=0.0047 ← значим!
# n=1000000: p-value<0.0001 ← очень значим!

Одинаковый эффект (0.1%) становится "значимым" просто потому, что выборка больше!

Проблема 2: Маленький эффект может быть статистически значимым

Сценарий A (маленький эффект, большая выборка):
- Control CR: 5.0%, n=100,000
- Treatment CR: 5.1%, n=100,000
- p-value = 0.002 (статистически значим!)
- Effect size (relative lift) = +2%
- **Вывод:** Статистически значимо, но практически бесполезно

Сценарий B (большой эффект, маленькая выборка):
- Control CR: 5.0%, n=100
- Treatment CR: 10.0%, n=100
- p-value = 0.11 (не значим)
- Effect size (relative lift) = +100%!
- **Вывод:** Практически важно, но статистически не доказано

Как правильно использовать оба метрики

Шаг 1: Рассчитайте Effect Size перед запуском теста

def required_sample_size(baseline_cr, desired_lift, alpha=0.05, power=0.80):
    """Рассчитывает нужный размер выборки"""
    from scipy.stats import norm
    
    effect = baseline_cr * desired_lift / 100  # Lift в абсолютных значениях
    target_cr = baseline_cr + effect
    
    # Две стороны: контроль и вариант
    p_avg = (baseline_cr + target_cr) / 2
    
    z_alpha = norm.ppf(1 - alpha/2)
    z_beta = norm.ppf(power)
    
    n = 2 * (z_alpha + z_beta)**2 * (p_avg * (1 - p_avg)) / (effect**2)
    
    return int(n)

# Пример: baseline 5%, хотим +10% lift
n = required_sample_size(baseline_cr=0.05, desired_lift=10)
print(f"Нужно {n} пользователей в каждой группе")

Шаг 2: Сообщайте результаты с обоими показателями

❌ ПЛОХО:
"p-value = 0.03, результат значим!"

✅ ХОРОШО:
"Конверсия выросла с 5.0% до 5.2% (p=0.03, относительный lift +4%, 95% CI: [1%, 7%])
Это статистически значимо, но практический эффект мал для нашей метрики ROI."

Таблица интерпретации

P-ValueEffect SizeДействие
< 0.05Большой (d > 0.8)✅ Деплоить! Статистически значимо + практически важно
< 0.05Маленький (d < 0.2)⚠️ Осторожно. Может быть математическая артефакт
> 0.05Большой (d > 0.8)⚠️ Нужна большая выборка. Effect есть, но недостаточно доказательств
> 0.05Маленький (d < 0.2)❌ Откатить. Нет эффекта

Практические рекомендации

  1. Всегда рассчитывайте effect size перед тестом

    • Минимальный приемлемый эффект (MAPE — Minimum Acceptable Practical Effect)
    • На основе этого определите размер выборки
  2. Не полагайтесь только на p-value

    • Используйте доверительные интервалы (confidence intervals)
    • Они показывают диапазон вероятных значений эффекта
  3. Проверяйте практическую значимость

    • Даже если p < 0.05, спросите: "Это достаточно большой эффект для нас?"
    • ROI при 2% lift может быть отрицательным
  4. Документируйте pre-registration

    • Минимальный эффект, который вас интересует
    • Это предотвратит p-hacking (поиск любого результата)
  5. Используйте sequential testing с бэйесовыми методами

    • Вероятность того, что вариант лучше (Probability of Being Best — PBB)
    • Более интуитивно, чем p-value
# Бэйесовский подход: какова вероятность, что вариант лучше?
# (вместо "есть ли различие")
from scipy.stats import binom

def bayesian_credible_interval(successes, trials, credibility=0.95):
    """Простой бэйесовский доверительный интервал"""
    alpha = (1 - credibility) / 2
    from scipy.stats import beta
    
    # Beta distribution (после наблюдения successes из trials)
    low = beta.ppf(alpha, successes + 1, trials - successes + 1)
    high = beta.ppf(1 - alpha, successes + 1, trials - successes + 1)
    
    return low, high

ci_low, ci_high = bayesian_credible_interval(520, 10000)
print(f"95% вероятность, что истинная конверсия между {ci_low:.3f} и {ci_high:.3f}")

Вывод: p-value отвечает на вопрос "есть ли эффект?" (да/нет). Effect size отвечает на вопрос "насколько большой эффект?" (величина). Для хорошего решения нужны оба ответа.