Какой критерий обычно используешь?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Статистические Критерии в 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)
- ✅ Если множественное тестирование — применил поправку
Когда я слежу за этим процессом, ошибок мало и результаты надёжны.