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

Какой критерий обычно используешь?

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

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

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

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

Статистические Критерии в A/B Тестировании

При анализе A/B тестов я использую различные статистические критерии в зависимости от типа данных и гипотезы. Расскажу о моём подходе.

1. Chi-Square (χ²) Тест — Для Пропорций

Это основной критерий, который я использую для большинства A/B тестов в SaaS и e-commerce.

Когда использовать:

  • Когда метрика бинарная (converted/not converted, clicked/not clicked, paid/not paid)
  • Когда есть две или более группы

Пример: Conversion Rate Test

Control:  1000 users → 45 conversions (4.5% conversion)
Test A:   1000 users → 55 conversions (5.5% conversion)

Мне нужно проверить, значимо ли отличие. Использую Chi-Square тест:

import numpy as np
from scipy.stats import chi2_contingency

# Contingency table
contingency = np.array([
    [45, 1000-45],  # Control: converted, not converted
    [55, 1000-55]   # Test: converted, not converted
])

chi2, p_value, dof, expected = chi2_contingency(contingency)

print(f"Chi-Square: {chi2:.4f}")
print(f"P-value: {p_value:.4f}")
print(f"Significant at 0.05? {p_value < 0.05}")

Интерпретация:

  • p-value < 0.05 → разница статистически значимая (можно внедрять изменение)
  • p-value ≥ 0.05 → разница случайна (нет доказательства эффекта)

Этот критерий мне нравится потому что он:

  • Прост в интерпретации
  • Быстро считается
  • Работает хорошо для больших выборок (n > 30)

2. Z-Test Для Пропорций — Быстрая Альтернатива

Это эквивалент Chi-Square, но более старший. Я использую когда нужно быстро посчитать.

from statsmodels.stats.proportion import proportions_ztest

count = np.array([45, 55])  # Conversions
nobs = np.array([1000, 1000])  # Total users

z_stat, p_value = proportions_ztest(count, nobs)

print(f"Z-score: {z_stat:.4f}")
print(f"P-value: {p_value:.4f}")

Этот тест даёт примерно такие же результаты как Chi-Square (для таблиц 2x2).

3. T-Test — Для Непрерывных Метрик

Когда метрика непрерывная (Revenue, Lifetime Value, Session Duration), использую t-test.

Пример: A/B Тест ценообразования

Control:  средняя цена $99,  std dev = $15
Test A:   средняя цена $120, std dev = $18
from scipy.stats import ttest_ind

control_prices = [99, 105, 95, 110, ...]
test_prices = [120, 118, 125, 115, ...]

t_stat, p_value = ttest_ind(control_prices, test_prices)

print(f"T-statistic: {t_stat:.4f}")
print(f"P-value: {p_value:.4f}")

Важно: Перед тем как использовать t-test, нужно проверить предположения:

  • Нормальное распределение (Shapiro-Wilk test)
  • Равные дисперсии (Levene test)

Если предположения нарушены, используют Welch's t-test или Mann-Whitney U test.

4. Mann-Whitney U Test — Непараметрический Альтернатив

Когда данные не нормально распределены (что часто случается с финансовыми метриками).

from scipy.stats import mannwhitneyu

control_values = [...]  # Any distribution
test_values = [...]

statistic, p_value = mannwhitneyu(control_values, test_values)
print(f"P-value: {p_value:.4f}")

Когда использовать:

  • Когда распределение сильно асимметрично (много outliers)
  • Когда есть экстремальные значения (один пользователь потратил $10,000)
  • Когда выборка маленькая

5. Fishers Exact Test — Для Малых Выборок

Когда выборка маленькая (< 30) и есть бинарная метрика.

from scipy.stats import fisher_exact

# 2x2 contingency table
# [[converted control, not converted control],
#  [converted test, not converted test]]

odds_ratio, p_value = fisher_exact([[45, 955], [55, 945]])
print(f"P-value: {p_value:.4f}")

Этот критерий более консервативен, чем Chi-Square, для малых выборок.

Мой Процесс Выбора Критерия

Я пользуюсь простой блок-схемой:

Вопрос: Метрика бинарная?
  ├─ ДА → Метрика пропорция (CR, CTR, etc)
  │  ├─ Выборка > 30? → Chi-Square
  │  └─ Выборка < 30? → Fisher Exact
  └─ НЕТ → Метрика непрерывная
     ├─ Данные нормальны? → T-Test (Welch's if unequal variance)
     └─ Данные асимметричны? → Mann-Whitney U

На Практике: Мой Стандартный Анализ A/B Теста

import pandas as pd
from scipy.stats import chi2_contingency, ttest_ind, mannwhitneyu
import numpy as np

def analyze_ab_test(control_df, test_df, metric_name, metric_type='binary'):
    """
    Анализирует A/B тест и выдаёт полный отчёт
    """
    
    if metric_type == 'binary':
        # Binary metric (conversion, clicked, etc)
        control_converted = control_df['converted'].sum()
        control_total = len(control_df)
        test_converted = test_df['converted'].sum()
        test_total = len(test_df)
        
        # Control rate
        control_rate = control_converted / control_total
        test_rate = test_converted / test_total
        
        # Chi-Square test
        contingency = np.array([
            [control_converted, control_total - control_converted],
            [test_converted, test_total - test_converted]
        ])
        chi2, p_value, dof, expected = chi2_contingency(contingency)
        
        # Calculate confidence interval
        from statsmodels.stats.proportion import proportion_confint
        ci_control = proportion_confint(control_converted, control_total, alpha=0.05, method='normal')
        ci_test = proportion_confint(test_converted, test_total, alpha=0.05, method='normal')
        
        print(f"=== {metric_name} ===")
        print(f"Control: {control_rate:.4f} (95% CI: {ci_control[0]:.4f} - {ci_control[1]:.4f})")
        print(f"Test:    {test_rate:.4f} (95% CI: {ci_test[0]:.4f} - {ci_test[1]:.4f})")
        print(f"Lift:    {(test_rate/control_rate - 1)*100:.1f}%")
        print(f"P-value: {p_value:.4f}")
        print(f"Significant? {'YES' if p_value < 0.05 else 'NO'}")
        
    elif metric_type == 'continuous':
        # Continuous metric (revenue, LTV, etc)
        control_values = control_df['value']
        test_values = test_df['value']
        
        control_mean = control_values.mean()
        test_mean = test_values.mean()
        
        # T-test
        t_stat, p_value = ttest_ind(control_values, test_values, equal_var=False)
        
        print(f"=== {metric_name} ===")
        print(f"Control: {control_mean:.2f} (std: {control_values.std():.2f})")
        print(f"Test:    {test_mean:.2f} (std: {test_values.std():.2f})")
        print(f"Diff:    {test_mean - control_mean:.2f}")
        print(f"P-value: {p_value:.4f}")
        print(f"Significant? {'YES' if p_value < 0.05 else 'NO'}")

# Usage
analyze_ab_test(control_df, test_df, 'Conversion Rate', 'binary')
analyze_ab_test(control_df, test_df, 'Average Order Value', 'continuous')

Важные Поправки При Множественном Тестировании

Если я смотрю на 10 метрик одновременно, вероятность ложного положительного результата (false positive) растёт. Нужны поправки.

Bonferroni Correction:

Альфа (significance level) = 0.05 / количество тестов
Для 10 метрик: 0.05 / 10 = 0.005

Это консервативно, но гарантирует, что не ошибёмся.

Более мягкие поправки:

  • Holm-Bonferroni
  • FDR (False Discovery Rate)

Типичные Ошибки, Которые Я Вижу

Ошибка 1: Peeking (Смотреть В Процессе)

Люди хотят проверить результаты теста через 5 дней вместо запланированных 28. Это увеличивает вероятность Type I error.

Ошибка 2: Sample Ratio Mismatch (SRM)

Если Control получил 1000 пользователей, а Test только 900, это может быть признак проблемы в рандомизации.

# Chi-square SRM check
control_expected = 1000
test_expected = 1000
control_actual = 1000
test_actual = 900

srm_chi2 = ((control_actual - control_expected)**2 / control_expected + 
             (test_actual - test_expected)**2 / test_expected)
print(f"SRM Chi2: {srm_chi2} (should be < 3.84 for p-value > 0.05)")

Ошибка 3: Ignoring Outliers

Одна покупка за $10,000 может исказить результат. Нужно либо использовать непараметрические тесты, либо удалить экстремальные выброс.

Мой Финальный Чеклист

Перед тем как закончить A/B анализ:

  • ✅ Выбрал правильный критерий для типа данных
  • ✅ Проверил размер выборки (больше 30 как минимум)
  • ✅ Проверил SRM (Sample Ratio Mismatch)
  • ✅ Проверил на peeking — тест закончился до того, как я смотрел результат
  • ✅ Рассчитал не только p-value, но и confidence interval
  • ✅ Проверил на практическую значимость (не только статистическую)
  • ✅ Посмотрел на secondary metrics (нет ли side effects)
  • ✅ Если множественное тестирование — применил поправку

Когда я слежу за этим процессом, ошибок мало и результаты надёжны.

Какой критерий обычно используешь? | PrepBro