Как доказать отсутствие изменений?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как доказать отсутствие изменений?
Проблема
Обычный статистический тест показывает значимость (есть ли эффект). Но как доказать что эффекта НЕТ?
Просто сказать «p-value > 0.05» недостаточно! Это может быть из-за недостаточно большой выборки.
Решение: Эквивалентность (Equivalence Testing)
1. Определяем зону эквивалентности
Что считать «практически отсутствием изменений»?
Пример: Конверсия
- Старая версия: 5%
- Новая версия: 5.1%
- Разница: 0.1%
Вопрос: является ли 0.1% практически значимым?
Ответ: Нет, если зона эквивалентности = ±0.5%
baseline = 5.0 # %
margin_of_equivalence = 0.5 # %
lower_bound = baseline - margin_of_equivalence # 4.5%
upper_bound = baseline + margin_of_equivalence # 5.5%
# Если новая версия в [4.5%, 5.5%] → эквивалентна
2. Тест эквивалентности (TOST)
Two One-Sided Tests:
- Проверяем H1: новое >= нижней границы
- Проверяем H2: новое <= верхней границы
Если ОБЕ гипотезы верны → эффект эквивалентен нулю
from scipy import stats
import numpy as np
# Данные
control = np.array([50, 51, 49, 52, 50, 48, 51, 49, 50, 52]) # старая версия
treatment = np.array([50, 51, 50, 51, 49, 50, 51, 50, 49, 51]) # новая версия
# Параметры
margin = 2.0 # зона эквивалентности = ±2
alpha = 0.05
mean_diff = treatment.mean() - control.mean()
se_diff = np.sqrt(control.var()/len(control) + treatment.var()/len(treatment))
print(f"Разница средних: {mean_diff}")
print(f"Стандартная ошибка: {se_diff}")
# Тест 1: mean_diff > -margin
t1 = (mean_diff - (-margin)) / se_diff
p1 = 1 - stats.t.cdf(t1, len(control) + len(treatment) - 2)
# Тест 2: mean_diff < margin
t2 = (margin - mean_diff) / se_diff
p2 = 1 - stats.t.cdf(t2, len(control) + len(treatment) - 2)
print(f"\nТест 1 (выше нижней границы): p = {p1}")
print(f"Тест 2 (ниже верхней границы): p = {p2}")
if p1 < alpha and p2 < alpha:
print("\n✓ Эффект эквивалентен нулю (нет значимых изменений)")
else:
print("\n✗ Не можем утверждать отсутствие эффекта")
3. Практический пример: A/B тест
Сценарий:
- Контроль: 4500 конверсий из 100,000 → 4.5%
- Лечение: 4520 конверсий из 100,000 → 4.52%
- Разница: +0.02% (практически ничто)
Вопрос: можем ли доказать что нет эффекта?
from scipy.stats import binom
control_conversions = 4500
control_total = 100000
treatment_conversions = 4520
treatment_total = 100000
control_rate = control_conversions / control_total
treatment_rate = treatment_conversions / treatment_total
print(f"Контроль: {control_rate*100:.2f}%")
print(f"Лечение: {treatment_rate*100:.2f}%")
# Зона эквивалентности: ±0.5% (практически значимый эффект)
margin = 0.005
if abs(treatment_rate - control_rate) < margin:
print(f"✓ Различие меньше {margin*100}% → эффект отсутствует")
else:
print(f"✗ Различие {abs(treatment_rate - control_rate)*100:.2f}% >= {margin*100}%")
4. Доверительные интервалы
Классический подход: посмотри CI
from scipy import stats
control = [50, 51, 49, 52, 50, 48, 51, 49, 50, 52]
treatment = [50, 51, 50, 51, 49, 50, 51, 50, 49, 51]
mean_diff = np.mean(treatment) - np.mean(control)
se = np.sqrt(np.var(control)/len(control) + np.var(treatment)/len(treatment))
ci = stats.t.interval(0.95, len(control) + len(treatment) - 2, mean_diff, se)
print(f"95% CI разницы: [{ci[0]:.2f}, {ci[1]:.2f}]")
# Если CI полностью в [-margin, margin] → эффект отсутствует
margin = 2.0
if ci[0] > -margin and ci[1] < margin:
print("✓ Вся доверительная область эквивалентна нулю")
else:
print("✗ Не можем утверждать отсутствие эффекта")
5. Effect Size
Даже если p > 0.05, проверь практическую значимость:
import numpy as np
from scipy import stats
control = np.random.normal(100, 15, 1000)
treatment = np.random.normal(100.5, 15, 1000)
# t-test
t_stat, p_value = stats.ttest_ind(control, treatment)
print(f"p-value: {p_value:.4f}")
# Cohen's d
cohens_d = (treatment.mean() - control.mean()) / np.sqrt((control.std()**2 + treatment.std()**2) / 2)
print(f"Cohen's d: {cohens_d:.4f}")
# Интерпретация
if abs(cohens_d) < 0.2:
print("✓ Практически нулевой эффект (d < 0.2)")
elif abs(cohens_d) < 0.5:
print("⚠ Малый эффект (0.2 <= d < 0.5)")
else:
print("✗ Значимый эффект")
6. Размер выборки и мощность
Проблема: малая выборка → неправильное p > 0.05
from scipy.stats import ttest_ind_from_stats
import numpy as np
# Нужна выборка достаточного размера
# Для обнаружения малого эффекта (d=0.2): n >= 320
# Для обнаружения среднего эффекта (d=0.5): n >= 50
required_n = 320 # для малого эффекта
if len(control) < required_n:
print(f"⚠ Выборка {len(control)} < требуемой {required_n}")
print("Не можем доказать отсутствие эффекта!")
else:
print("✓ Выборка достаточна для вывода")
7. Практические шаги
-
Определи зону эквивалентности
- Какой эффект считается практически значимым?
- Обычно: 10-20% от baseline
-
Проверь размер выборки
- Хватает ли данных для надёжного вывода?
-
Используй TOST
- Проверь обе стороны доверительного интервала
-
Посмотри Effect Size
- Даже если есть статистическая значимость
-
Задокументируй результат
- «Эффект эквивалентен нулю в пределах ±margin»
8. Типичные ошибки
НЕПРАВИЛЬНО:
- «p > 0.05, значит нет эффекта!» (недостаточно)
ПРАВИЛЬНО:
- «95% CI: [-0.5%, +0.4%], что меньше margin ±1% → нет эффекта»
- «Cohen's d = 0.05 (практически нулевой) и p = 0.45»
Заключение
Для доказательства отсутствия эффекта:
- Используй тест эквивалентности (TOST)
- Определи зону практической значимости
- Проверь размер выборки
- Смотри effect size, не только p-value
- Используй доверительные интервалы