Как проверить значимо ли отличается среднее
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
Сравнение средних — базовая задача аналитика. «CR A/B-теста 10.2% vs 10.5% — это реально разница или случайность?». «Средний чек после релиза 1500 → 1520, значим ли lift?». Без статистического теста ответ гадание.
На собесе спросят: «какой тест для сравнения средних?». Junior скажет t-test. Middle добавит Welch's для неравных variance. Senior обсудит — когда t-test не работает и что делать (Mann-Whitney, bootstrap).
В статье:
- T-test: Student's vs Welch's
- Assumptions и когда они нарушаются
- Mann-Whitney U при non-normal
- Bootstrap как универсал
- Python код для всех
Тесты для сравнения средних
Independent samples t-test (Student's)
Сравнение двух independent групп.
Assumptions:
- Независимость наблюдений
- Approximately normal distribution (для small N)
- Equal variance (Student's) или не equal (Welch's)
from scipy.stats import ttest_ind
# Student's (assumes equal variance)
t_stat, p_value = ttest_ind(group_a, group_b, equal_var=True)
# Welch's (не предполагает equal variance) — recommended
t_stat, p_value = ttest_ind(group_a, group_b, equal_var=False)Paired t-test
Для связанных выборок (до/после, один и тот же user).
from scipy.stats import ttest_rel
t_stat, p_value = ttest_rel(before, after)Когда t-test не работает
Non-normal distribution
Для heavy-tailed (чек, время) t-test менее точен. Но при N > 100 CLT помогает.
Extreme outliers
Один outlier может сильно повлиять.
Малые выборки non-normal
N < 30 + non-normal → t-test ненадёжен.
Альтернативы
Mann-Whitney U test
Non-parametric. Сравнение медиан (точнее — распределений).
from scipy.stats import mannwhitneyu
stat, p_value = mannwhitneyu(group_a, group_b)Работает на любых distributions. Теряет power на truly normal.
Bootstrap
Универсальный. Обработка любых метрик.
import numpy as np
def bootstrap_test(a, b, n_iter=10000):
obs_diff = np.mean(a) - np.mean(b)
combined = np.concatenate([a, b])
diffs = []
for _ in range(n_iter):
sample = np.random.choice(combined, len(combined), replace=True)
diff = np.mean(sample[:len(a)]) - np.mean(sample[len(a):])
diffs.append(diff)
p_value = np.mean(np.abs(diffs) >= np.abs(obs_diff))
return p_valueМедленнее, но гибче.
Permutation test
Похож на bootstrap, но перемешивает метки:
def permutation_test(a, b, n_iter=10000):
obs_diff = np.mean(a) - np.mean(b)
combined = np.concatenate([a, b])
count = 0
for _ in range(n_iter):
np.random.shuffle(combined)
diff = np.mean(combined[:len(a)]) - np.mean(combined[len(a):])
if abs(diff) >= abs(obs_diff):
count += 1
return count / n_iterКак выбрать тест
Normal + equal variance → Student's t-test
Normal + different variance → Welch's t-test (default recommendation)
Non-normal, large N → t-test ок (CLT)
Non-normal, small N → Mann-Whitney U
Сложная metric / custom → Bootstrap
Paired data → Paired t-test / WilcoxonПрактика: A/B-тест
import numpy as np
from scipy.stats import ttest_ind, mannwhitneyu
# control group
control = np.random.lognormal(3, 1, 1000) # heavy-tailed
# test group (с эффектом)
test = np.random.lognormal(3.05, 1, 1000)
# Welch's
t, p = ttest_ind(control, test, equal_var=False)
print(f't-test p = {p:.4f}')
# Mann-Whitney
u, p = mannwhitneyu(control, test)
print(f'Mann-Whitney p = {p:.4f}')Проверка assumptions
Нормальность
from scipy.stats import shapiro, normaltest
stat, p = shapiro(data) # для small samples
stat, p = normaltest(data) # для largeЕсли p < 0.05 → не normal. Но на больших N эти тесты почти всегда отвергают.
Визуально
import matplotlib.pyplot as plt
from scipy.stats import probplot
probplot(data, plot=plt) # Q-Q plot
plt.show()Prямая линия → нормально.
Equal variance
from scipy.stats import levene
stat, p = levene(group_a, group_b)
# p < 0.05 → разные variance → Welch'sВ A/B-тестах
Default рекомендация:
- Конверсия (proportion): z-test для пропорций или chi-square
- Continuous с нормальным: Welch's t-test
- Continuous с heavy tail (чек): Mann-Whitney или bootstrap
- Составные метрики: bootstrap
На собесе
«Какой тест для сравнения средних?» Welch's t-test (не предполагает equal variance).
«Когда Mann-Whitney?» Non-normal distribution с малой выборкой или heavy tails.
«Bootstrap vs t-test?» Bootstrap универсальнее, но медленнее. T-test — если assumptions выполнены.
«Paired vs independent?» Paired — одни и те же subjects (до/после). Independent — разные.
Частые ошибки
1. Student's вместо Welch's
В A/B variances часто разные. Default — Welch's.
2. t-test на small N с outliers
Ненадёжно. Bootstrap или Mann-Whitney.
3. p < 0.05 → значимо навсегда
Multiple testing! Поправка Bonferroni для множественных сравнений.
4. Игнорировать effect size
Значимость ≠ практическая важность. Смотрите и p-value, и effect size.
Связанные темы
- T-test vs z-test
- T-test vs chi-square
- Mann-Whitney test
- Bootstrap простыми словами
- P-value простыми словами
FAQ
p = 0.049 — significant?
Technically да (при α = 0.05). Но очень близко. Проверьте mbet повторить.
Nonparametric always safer?
Не всегда. На truly normal данных t-test power выше.
В Python или R?
Scipy / statsmodels в Python. В R — более широкий набор.
One-sided или two-sided?
Default — two-sided. One-sided только если теоретически excluded одна сторона.
Тренируйте статистику — откройте тренажёр с 1500+ вопросами для собесов.