Как посчитать Bonferroni correction в SQL
Содержание:
Зачем Bonferroni
Когда в одном A/B-эксперименте проверяют несколько гипотез — 5 вариантов, 10 метрик, 4 сегмента — вероятность найти хотя бы одно ложно-значимое срабатывание растёт. Family-wise error rate (FWER) надо контролировать. Bonferroni — самый простой и консервативный способ.
Формула
Если делаем m независимых тестов и хотим суммарный alpha ≤ α (обычно 0.05):
α_per_test = α / mИли, эквивалентно, корректируем p-values:
p_adjusted = min(p × m, 1)Скорректированная alpha
Например, 10 метрик в одном эксперименте, α=0.05:
WITH config AS (
SELECT 0.05::NUMERIC AS family_alpha, 10::INT AS m_tests
)
SELECT
family_alpha,
m_tests,
family_alpha / m_tests AS bonferroni_alpha
FROM config;Результат — 0.005. Каждая метрика проверяется на α=0.005, и тогда суммарный риск ложного срабатывания по семейству ≤ 5%.
Скорректированные p-values
Удобнее адекватировать p-values, оставляя α=0.05:
WITH raw_p AS (
SELECT * FROM (VALUES
('conversion', 0.003),
('revenue', 0.020),
('retention_d7', 0.045),
('time_on_page', 0.120),
('refunds', 0.500)
) AS t(metric, p_value)
),
correction AS (
SELECT COUNT(*) AS m FROM raw_p
)
SELECT
r.metric,
r.p_value,
LEAST(r.p_value * c.m, 1.0) AS p_bonferroni,
LEAST(r.p_value * c.m, 1.0) < 0.05 AS significant_bonferroni
FROM raw_p r
CROSS JOIN correction c
ORDER BY r.p_value;conversion остаётся значимой после умножения на 5 (0.003 × 5 = 0.015), revenue — нет (0.020 × 5 = 0.10).
Holm-Bonferroni
Holm-Bonferroni — менее консервативный «sequential» вариант. P-values сортируют по возрастанию, корректируют по индивидуальной формуле:
p_(1) × m, p_(2) × (m−1), ..., p_(m) × 1WITH raw_p AS (
SELECT * FROM (VALUES
('conversion', 0.003),
('revenue', 0.020),
('retention_d7', 0.045),
('time_on_page', 0.120),
('refunds', 0.500)
) AS t(metric, p_value)
),
ordered AS (
SELECT
metric,
p_value,
ROW_NUMBER() OVER (ORDER BY p_value) AS rnk,
COUNT(*) OVER () AS m
FROM raw_p
)
SELECT
metric,
p_value,
LEAST(p_value * (m - rnk + 1), 1.0) AS p_holm,
LEAST(p_value * (m - rnk + 1), 1.0) < 0.05 AS significant_holm
FROM ordered
ORDER BY rnk;Holm пропускает больше «реальных» эффектов, сохраняя контроль FWER.
Частые ошибки
Ошибка 1. Считать m без учёта direction.
Если для каждой метрики проверяете и «больше» и «меньше» — это два теста, не один. m удваивается.
Ошибка 2. Bonferroni для коррелированных метрик.
Conversion и revenue сильно коррелируют. Bonferroni считает их независимыми → слишком консервативно. Используйте Šidák или Hochberg.
Ошибка 3. Применять post-hoc.
«У нас вышло 8 метрик не значимых, попробуем ещё 5» — это p-hacking. Bonferroni спасает только если m зафиксирован до эксперимента.
Ошибка 4. Не различать FWER и FDR.
Bonferroni контролирует FWER (хоть одно ложное). Для большого m (50+ метрик) консервативность убивает power — лучше Benjamini-Hochberg (FDR).
Ошибка 5. LEAST(p × m, 1.0) без ::NUMERIC.
В Postgres 1.0 иногда воспринимается как double, в других СУБД могут быть нюансы. Используйте ::NUMERIC для предсказуемости.
Связанные темы
- Как посчитать chi-square test в SQL
- Как посчитать t-test в SQL
- Как посчитать sample size в SQL
- Как посчитать MDE в SQL
FAQ
Bonferroni vs Holm?
Holm не менее строг по FWER, но сильнее по power. Если выбираете один — Holm.
При каком m Bonferroni «слишком жёсткая»?
При m > 20 теряете значимые эффекты. Лучше Benjamini-Hochberg.
Можно ли Bonferroni для нескольких t-тестов в одном A/B/C/D?
Да, попарные сравнения = m = 6 для 4 групп. Делите α на 6.
Где брать p-values?
Из SQL для chi-square / Z-аппроксимации. Точные — из scipy / R.
FWER vs FDR?
FWER = P(хотя бы одно ложное). FDR = E(доля ложных среди значимых). FDR разумнее при большом числе метрик.