Условие
Менеджер каждый день смотрит p-value в дашборде и говорит «остановим, когда p < 0.05». Покажите симуляцией, что реальный type I error будет сильно выше 5%.
Решение
Подход
Если в A/A повторно смотреть p-value и останавливаться при первом p < α — реальный false positive rate взрывается. При бесконечном peeking он → 1.
Реализация
import numpy as np
from scipy.stats import ttest_ind
def simulate_peeking(n_total=10000, n_peeks=20, alpha=0.05,
n_simulations=5000, seed=42):
rng = np.random.default_rng(seed)
peek_points = np.linspace(n_total/n_peeks, n_total, n_peeks).astype(int)
false_pos = 0
for _ in range(n_simulations):
# A/A: оба из N(0,1)
a = rng.normal(0, 1, n_total)
b = rng.normal(0, 1, n_total)
stopped = False
for k in peek_points:
_, p = ttest_ind(a[:k], b[:k])
if p < alpha:
false_pos += 1
stopped = True
break
return false_pos / n_simulations
for k in [1, 5, 10, 20, 50]:
rate = simulate_peeking(n_peeks=k)
print(f"{k} peeks: FPR = {rate:.3f}")
# 1 peek: ~0.050 (норма)
# 5 peeks: ~0.14
# 10 peeks: ~0.19
# 20 peeks: ~0.23
# 50 peeks: ~0.29Решения
- Fixed-horizon test: фиксируем N до теста, смотрим p только в конце. Самое простое.
- Group sequential (Pocock, O'Brien-Fleming): α-spending — корректируем порог на каждый peek.
- Always-valid p-values / mSPRT: можно peek сколько угодно без поправки.
Подводные камни
- «Я смотрю в дашборд, но не останавливаю тест» — всё равно влияет, если решение об остановке принимается по этим взглядам.
- Поправка O'Brien-Fleming требует заранее запланированных точек peek; ad-hoc взгляды — нарушение.
- Bayesian не имеет проблемы peeking по форме, но имеет проблему calibration prior — это не magic решение.
- Длинные тесты создают организационное давление peeking — лучше дизайнить тест на 14 дней максимум через MDE.
Эталонный ответ
Peeking без поправки раздувает Type I error: 5 peeks → 14%, 20 peeks → 23%, бесконечный → 100%. Решения: fixed-horizon, group sequential (O'Brien-Fleming/Pocock) или always-valid p-values (mSPRT).