Статистические тесты: проверяем гипотезы

«Это различие реально или случайно?» — на этот вопрос отвечают статистические тесты. Разберём их логику и инструменты SciPy.

Статистический тест проверяет, согласуются ли данные с нулевой гипотезой (например, «разницы нет»), и выдаёт p-value — вероятность увидеть такие данные, если гипотеза верна.

Зачем нужны тесты

Допустим, новое удобрение дало урожай в среднем на 5% больше. Это эффект удобрения — или просто повезло с погодой на этих грядках? Глаз тут бессилен: нужна процедура, которая отделит сигнал от шума. Статистический тест формализует это: он считает, насколько удивительны данные при предположении «эффекта нет».

Логика проверки гипотез

1. Нулевая гипотеза H0:  "эффекта нет"  (по умолчанию верна)
2. Считаем статистику теста по данным
3. Считаем p-value = P(увидеть такие или более крайние данные | H0 верна)
4. Если p-value < α (обычно 0.05) -> отвергаем H0 ("эффект значим")
   Иначе -> данных недостаточно, чтобы отвергнуть H0

Важно: маленький p-value не «доказывает» эффект и не говорит о его величине — он лишь сообщает, что данные плохо вяжутся с «эффекта нет».

Основные тесты в scipy.stats

ТестФункцияКогда
t-тест (две группы)ttest_indСравнить средние двух выборок
t-тест (парный)ttest_relДо/после на одних объектах
Хи-квадратchi2_contingencyСвязь категориальных признаков
КорреляцияpearsonrЛинейная связь двух величин
НормальностьshapiroПохоже ли на нормальное распределение
from scipy.stats import ttest_ind

control   = [4.1, 3.9, 4.0, 4.2, 3.8]
treatment = [4.5, 4.7, 4.6, 4.4, 4.8]
stat, pvalue = ttest_ind(control, treatment)
print(pvalue)   # ~6e-05 -> различие значимо

Корреляцию посчитаем руками

Коэффициент корреляции Пирсона измеряет линейную связь от −1 до +1. Реализуем на stdlib и проверим на «идеальной» прямой:

import statistics

def pearson(x, y):
    mx, my = statistics.mean(x), statistics.mean(y)
    cov = sum((a - mx) * (b - my) for a, b in zip(x, y))
    sx = sum((a - mx) ** 2 for a in x) ** 0.5
    sy = sum((b - my) ** 2 for b in y) ** 0.5
    return cov / (sx * sy)

x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]          # ровно y = 2x -> корреляция +1
z = [10, 8, 6, 4, 2]          # ровно обратная -> -1
print("corr(x, y) =", round(pearson(x, y), 4))
print("corr(x, z) =", round(pearson(x, z), 4))

Вывод:

corr(x, y) = 1.0
corr(x, z) = -1.0

Как работает под капотом

t-тест считает статистику t = (разница средних) / (оценка разброса): чем больше разница относительно шума, тем больше t. Затем по распределению Стьюдента (которое тоже есть в scipy.stats как t) вычисляется, насколько вероятно получить такое t случайно — это и есть p-value. Распределение Стьюдента, а не нормальное, потому что при малых выборках мы не знаем истинный разброс и оцениваем его по тем же данным — это добавляет неопределённости, которую «толстые хвосты» Стьюдента учитывают.

Частые ошибки

  • p-value = вероятность того, что H0 верна. Нет! Это P(данные | H0), а не P(H0 | данные). Распространённейшее заблуждение.
  • Не значимо = эффекта нет. Высокий p-value лишь означает «данных мало, чтобы что-то утверждать», а не доказывает отсутствие эффекта.
  • Множественные сравнения. Прогнав 20 тестов, по чистой случайности получите один «значимый» при α=0.05 — нужна поправка (Бонферрони и т.п.).
  • Путать корреляцию с причинностью. corr=0.9 не значит, что одно вызывает другое.

Итог

  • Тест проверяет согласие данных с нулевой гипотезой через p-value.
  • p-value — это P(данные | H0), а НЕ вероятность гипотезы.
  • В SciPy: ttest_ind, chi2_contingency, pearsonr, shapiro и др.
  • Корреляцию легко посчитать руками; тесты лучше доверять SciPy.
Проверьте себя
1. Что такое p-value?
AВероятность того, что нулевая гипотеза верна
BВероятность увидеть такие (или более крайние) данные, ЕСЛИ нулевая гипотеза верна
CВеличина эффекта
DВероятность ошибки измерения
2. Значение коэффициента корреляции Пирсона −1 означает:
AСвязи нет
BИдеальную обратную линейную связь (одно растёт — другое строго пропорционально падает)
CОшибку в данных
DИдеальную прямую связь
3. Почему опасно прогонять много статистических тестов сразу?
AЭто медленно
BПри множественных сравнениях случайно появятся ложно-значимые результаты — нужна поправка (например, Бонферрони)
CSciPy этого не умеет
DТесты начнут противоречить