Как посчитать FDR (Benjamini-Hochberg) в SQL

Закрепи формулу fdr в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать fdr в Telegram

Зачем FDR

При 20+ метриках в одном A/B-тесте Bonferroni слишком консервативен — теряете реальные эффекты. FDR (False Discovery Rate) контролирует долю ложных открытий среди объявленных значимыми. Стандарт для метрических панелей в большом продуктовом A/B, не для критических медицинских trials.

Процедура Benjamini-Hochberg

  1. Сортируем p-values по возрастанию: p_(1) ≤ p_(2) ≤ ... ≤ p_(m).
  2. Для каждого i считаем порог q × i / m, где q — желаемый FDR.
  3. Находим максимальный k, такой что p_(k) ≤ q × k / m.
  4. Объявляем значимыми все p_(1), ..., p_(k).

BH в SQL

WITH raw_p AS (
    SELECT * FROM (VALUES
        ('conversion',     0.001),
        ('revenue',        0.012),
        ('retention_d7',   0.030),
        ('arpu',           0.045),
        ('time_on_page',   0.120),
        ('refunds',        0.310),
        ('unsubscribes',   0.500),
        ('crashes',        0.890)
    ) 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
),
bh AS (
    SELECT
        metric,
        p_value,
        rnk,
        m,
        0.05 * rnk::NUMERIC / m AS threshold,
        p_value <= 0.05 * rnk::NUMERIC / m AS below_threshold
    FROM ordered
),
max_rnk AS (
    SELECT MAX(rnk) AS k FROM bh WHERE below_threshold
)
SELECT
    b.metric,
    b.p_value,
    b.rnk,
    b.threshold,
    b.rnk <= COALESCE(m.k, 0) AS significant_bh
FROM bh b
CROSS JOIN max_rnk m
ORDER BY b.rnk;

Adjusted p-values

Если хочется именно «BH-adjusted p»:

p_adjusted(i) = min over j >= i of (p_(j) × m / j)
WITH ordered AS (
    SELECT
        metric,
        p_value,
        ROW_NUMBER() OVER (ORDER BY p_value) AS rnk,
        COUNT(*) OVER () AS m
    FROM raw_p
),
candidate AS (
    SELECT
        metric,
        p_value,
        rnk,
        m,
        p_value * m / rnk::NUMERIC AS raw_adj
    FROM ordered
)
SELECT
    metric,
    p_value,
    LEAST(MIN(raw_adj) OVER (ORDER BY rnk DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 1.0)
    AS p_bh_adjusted
FROM candidate
ORDER BY rnk;

Window ORDER BY rnk DESC ROWS UNBOUNDED PRECEDING — running min от хвоста, чтобы adjusted-p были монотонными.

Закрепи формулу fdr в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать fdr в Telegram

FDR vs FWER

SELECT
    metric,
    p_value,
    LEAST(p_value * m, 1.0) AS p_bonferroni,   -- FWER
    p_bh_adjusted                              -- FDR
FROM bh_results;

FWER (Bonferroni): «вероятность хотя бы одного ложного открытия ≤ 5%». FDR (BH): «среди объявленных значимыми ≤ 5% ложных».

На 20 метриках Bonferroni α/20 = 0.0025 — реальные эффекты теряются. BH с q=0.05 отлавливает больше.

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

Ошибка 1. Сортировать по m, а не по i / m. В BH-процедуре сравнивают p_(i) ≤ q × i / m, не p_(i) ≤ q / m. i / m растёт по позиции.

Ошибка 2. Не корректировать adjusted-p на монотонность. Грубый p × m / i может дать p_adj(2) > p_adj(3). Стандарт — running min от хвоста, как в SQL выше.

Ошибка 3. BH при зависимых тестах. BH формально требует независимость p-values. На сильно коррелированных метриках — Benjamini-Yekutieli (BY) с поправкой.

Ошибка 4. Применять FDR на маленьких m. При m=3 Bonferroni и BH дают почти одно. Эффект FDR проявляется при m ≥ 10.

Ошибка 5. Использовать в medical/regulatory. FWER здесь обычно строже (FDA, MTM). FDR — для исследовательских и продуктовых задач.

Связанные темы

FAQ

BH vs Bonferroni?

BH — менее строгий, больше power. Bonferroni — строже, теряете эффекты.

Какой q выбрать?

Обычно 0.05 или 0.10. В research — 0.20 допустимо.

FDR можно для коррелированных метрик?

BY-procedure: p_(i) ≤ q × i / (m × Σ 1/j for j=1..m). Жёстче BH, но валиден при зависимости.

Можно ли BH для попарных сравнений A/B/C?

Да, если у вас m гипотез о попарной разнице.

m фиксируется?

Да, до анализа. Иначе p-hacking.