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

Как использовать p-value при проверке гипотезы?

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

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

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

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

Как использовать p-value при проверке гипотезы?

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

Концепция p-value

Пример: A/B тест

Предположим, мы тестируем новый дизайн кнопки:

  • Контрольная группа (старый дизайн): 100 кликов из 1000 = 10%
  • Тестовая группа (новый дизайн): 120 кликов из 1000 = 12%

Вопрос: Это реальное улучшение или просто случайность?

Нулевая гипотеза (H₀): Обе кнопки дают одинаковый CTR (Click-Through Rate)

Альтернативная гипотеза (H₁): Новая кнопка лучше

p-value отвечает: "Какова вероятность увидеть такую разницу, если на самом деле кнопки одинаковые?"

Интерпретация p-value

p-value = 0.05  →  5% вероятность, что результат случаен
p-value = 0.01  →  1% вероятность, что результат случаен
p-value = 0.5   →  50% вероятность, что результат случаен (очень слабо!)

Стандартный уровень значимости (alpha = 0.05):

Если p-value < 0.05  →  Отклоняем H₀ (результат значим)
Если p-value ≥ 0.05  →  НЕ отклоняем H₀ (результат не значим)

Практический пример: A/B тест

import scipy.stats as stats
import numpy as np

# Данные A/B теста
control_clicks = 100
control_total = 1000

treatment_clicks = 120
treatment_total = 1000

# Двухвыборочный тест пропорций (Chi-square test)
from scipy.stats import chi2_contingency

# Таблица сопряжённости
contingency_table = np.array([
    [control_clicks, control_total - control_clicks],  # контроль
    [treatment_clicks, treatment_total - treatment_clicks]  # лечение
])

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

print(f"Chi-square статистика: {chi2:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"Степени свободы: {dof}")

# Интерпретация
alpha = 0.05
if p_value < alpha:
    print(f"Результат значим (p < {alpha}). Отклоняем нулевую гипотезу.")
    print("Новый дизайн статистически значимо лучше!")
else:
    print(f"Результат НЕ значим (p ≥ {alpha}). НЕ отклоняем нулевую гипотезу.")
    print("Нет достаточных доказательств, что новый дизайн лучше.")

t-тест (для непрерывных данных)

from scipy.stats import ttest_ind

# Данные: время загрузки страницы (мс)
old_page = np.array([1200, 1300, 1100, 1400, 1250])
new_page = np.array([1000, 950, 1050, 980, 1100])

# Двухвыборочный t-тест
t_statistic, p_value = ttest_ind(old_page, new_page)

print(f"t-статистика: {t_statistic:.4f}")
print(f"p-value: {p_value:.4f}")

alpha = 0.05
if p_value < alpha:
    print(f"Результат значим (p = {p_value:.4f} < {alpha})")
    print(f"Новая страница загружается значимо быстрее!")
else:
    print(f"Результат НЕ значим (p = {p_value:.4f}{alpha})")
    print("Разница может быть случайной")

Ошибки при интерпретации p-value

Ошибка 1: Неправильная интерпретация

❌ НЕПРАВИЛЬНО: p-value = 0.05 означает, что H₀ верна с 95% вероятностью

✅ ПРАВИЛЬНО: p-value = 0.05 означает, что если H₀ верна, то вероятность
   получить такой результат = 5%

Ошибка 2: p-hacking (манипуляция результатами)

# ❌ ПЛОХО: делаем много тестов, выбираем те с малым p-value
for i in range(100):
    result = test_hypothesis()
    if result.p_value < 0.05:
        print(f"Нашли значимый результат!")
        break

# Если сделаешь 100 тестов, примерно 5 будут с p < 0.05 просто по случайности!
# Это False Positive (Type I error)

# ✅ ПРАВИЛЬНО: заранее определи тест и alpha
alpha = 0.05
p_value = test_hypothesis()
if p_value < alpha:
    print("Результат значим")

Ошибка 3: Множественные сравнения

# Если делаешь несколько тестов, нужно применить коррекцию
from scipy.stats import ttest_ind

# Тестируем 10 гипотез
p_values = []
for i in range(10):
    _, p_val = ttest_ind(group1[i], group2[i])
    p_values.append(p_val)

# Коррекция Bonferroni
alpha = 0.05
num_tests = len(p_values)
adjusted_alpha = alpha / num_tests  # 0.05 / 10 = 0.005

for i, p_val in enumerate(p_values):
    if p_val < adjusted_alpha:
        print(f"Тест {i} значим с коррекцией")
    else:
        print(f"Тест {i} НЕ значим с коррекцией")

# Альтернатива: метод Benjamini-Hochberg (FDR)
from scipy.stats import rankdata

ranks = rankdata(p_values)
fdr_threshold = 0.05
m = len(p_values)

for rank, p_val in sorted(zip(ranks, p_values), reverse=True):
    threshold = fdr_threshold * rank / m
    if p_val <= threshold:
        print(f"Тест значим (FDR контролируется на уровне {fdr_threshold})")
        break

Реальный пример: Email маркетинг

import numpy as np
from scipy.stats import chi2_contingency

# А/В тест: две версии письма

# Вариант А (контроль)
group_a_sent = 10000
group_a_opened = 2500  # 25% open rate

# Вариант B (новая тема письма)
group_b_sent = 10000
group_b_opened = 2700  # 27% open rate

print(f"Вариант A: {group_a_opened / group_a_sent:.1%} open rate")
print(f"Вариант B: {group_b_opened / group_b_sent:.1%} open rate")
print(f"Разница: {(group_b_opened / group_b_sent) - (group_a_opened / group_a_sent):.1%}")

# Тест
contingency = np.array([
    [group_a_opened, group_a_sent - group_a_opened],
    [group_b_opened, group_b_sent - group_b_opened]
])

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

print(f"\nChi-square: {chi2:.4f}")
print(f"p-value: {p_value:.6f}")

alpha = 0.05
if p_value < alpha:
    print(f"\nРезультат ЗНАЧИМ (p = {p_value:.6f} < {alpha})")
    print("Новая тема письма значимо улучшает open rate.")
    print("Рекомендация: использовать вариант B для всех писем.")
else:
    print(f"\nРезультат НЕ ЗНАЧИМ (p = {p_value:.6f}{alpha})")
    print("Разница может быть случайной.")
    print("Рекомендация: нужен больший размер выборки.")

Доверительные интервалы вместо p-value

from scipy import stats

# Вычисляем 95% доверительный интервал
group_a_mean = 50
group_b_mean = 55
group_a_std = 10
group_b_std = 10
n = 100

# Стандартная ошибка разницы средних
se_diff = np.sqrt((group_a_std**2 + group_b_std**2) / n)

# 95% CI для разницы
z_critical = 1.96  # для 95%
ci_lower = (group_b_mean - group_a_mean) - z_critical * se_diff
ci_upper = (group_b_mean - group_a_mean) + z_critical * se_diff

print(f"Разница средних: {group_b_mean - group_a_mean}")
print(f"95% доверительный интервал: [{ci_lower:.2f}, {ci_upper:.2f}]")

if ci_lower > 0:
    print("Интервал НЕ включает ноль → результат значим на уровне 0.05")
else:
    print("Интервал включает ноль → результат НЕ значим на уровне 0.05")

Размер выборки и p-value

# С большой выборкой даже маленькие различия становятся значимыми
from scipy.stats import ttest_ind

# Маленькая выборка
small_a = np.random.normal(100, 10, n=30)
small_b = np.random.normal(101, 10, n=30)
t, p = ttest_ind(small_a, small_b)
print(f"n=30: p-value = {p:.4f}")  # часто НЕ значим

# Большая выборка
large_a = np.random.normal(100, 10, n=10000)
large_b = np.random.normal(101, 10, n=10000)
t, p = ttest_ind(large_a, large_b)
print(f"n=10000: p-value = {p:.6f}")  # часто значим

# Вывод: даже маленькие эффекты значимы с большой выборкой!
# Поэтому смотри на практическую значимость, не только на p-value

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

1. Установи alpha заранее

alpha = 0.05  # стандартный уровень
alpha = 0.01  # более строгий (для критичных решений)

2. Выбери тест заранее

# t-тест для непрерывных данных
# Chi-square для категориальных
# Mann-Whitney U для ненормальных распределений

3. Вычисли размер выборки

# Используй power analysis, чтобы определить нужный n
from statsmodels.stats.power import tt_ind_solve_power

# Нам нужна выборка, чтобы обнаружить эффект с power = 0.8
required_n = tt_ind_solve_power(
    effect_size=0.5,  # небольшой эффект
    alpha=0.05,
    power=0.8,
    alternative='two-sided'
)
print(f"Требуется n = {required_n:.0f} человек в каждой группе")

4. Контролируй Type I (False Positive) и Type II (False Negative) ошибки

5. Смотри на доверительные интервалы, не только p-value

Вывод

p-value — это инструмент, а не ответ. Используй его правильно:

  1. p-value < alpha → результат статистически значим
  2. Но это НЕ означает, что эффект практически значим
  3. Смотри на величину эффекта (effect size)
  4. Контролируй множественные сравнения
  5. Определяй размер выборки заранее
  6. Используй доверительные интервалы дополнительно
Как использовать p-value при проверке гипотезы? | PrepBro