Как посчитать p-value в SQL

Проверь себя · 1/3разбор после ответа
Как корректно интерпретировать значение p-value = 0.03 в проверке статистических гипотез?

Что такое p-value и что он показывает

P-value — вероятность увидеть такой же или более сильный эффект, чем наблюдаемый, при условии, что нулевая гипотеза верна (то есть реальной разницы между вариантами нет). Если p-value мало, наблюдаемый результат плохо согласуется с гипотезой «разницы нет», и её отвергают.

Ключевое, что путают: p-value — это не вероятность того, что нулевая гипотеза верна, и не вероятность того, что вариант B лучше. Это вероятность данных при предположении об отсутствии эффекта. Порог α = 0.05 — это соглашение, а не закон природы: результат при p < 0.05 называют статистически значимым, но само значение 0.05 выбрано исторически, и в части задач берут 0.01.

На собесе по A/B-тестам p-value спрашивают почти всегда, и чаще проверяют не формулу, а корректность интерпретации — умение не сказать «p < 0.05, значит B лучше на столько-то процентов».

z-test для пропорций

Для конверсий (доля сконвертившихся в A против B) используют z-тест для двух пропорций:

WITH stats AS (
    SELECT
        COUNT(DISTINCT user_id) FILTER (WHERE variant = 'A') AS n_a,
        COUNT(DISTINCT user_id) FILTER (WHERE variant = 'A' AND converted) AS x_a,
        COUNT(DISTINCT user_id) FILTER (WHERE variant = 'B') AS n_b,
        COUNT(DISTINCT user_id) FILTER (WHERE variant = 'B' AND converted) AS x_b
    FROM ab_test
    WHERE test_id = 123
),
calc AS (
    SELECT
        x_a::NUMERIC / n_a AS p_a,
        x_b::NUMERIC / n_b AS p_b,
        (x_a + x_b)::NUMERIC / (n_a + n_b) AS p_pool,
        n_a, n_b
    FROM stats
)
SELECT
    p_a, p_b,
    p_b - p_a AS effect,
    (p_b - p_a) / SQRT(p_pool * (1 - p_pool) * (1.0/n_a + 1.0/n_b)) AS z_stat
FROM calc;

Из z-статистики получают двустороннее p-value как 2 × (1 − Φ(|z|)), где Φ — функция стандартного нормального распределения. В PostgreSQL встроенной Φ нет, поэтому её выражают через erf: Φ(z) = 0.5 × (1 + erf(z / sqrt(2))), либо считают p-value в Python.

t-test для непрерывных метрик

Для непрерывных метрик (средний чек, выручка на пользователя) применяют t-тест. Дисперсии групп обычно не равны, поэтому берут вариант Уэлча:

WITH a AS (
    SELECT AVG(amount) AS mean_a, VAR_SAMP(amount) AS var_a, COUNT(*) AS n_a
    FROM ab_test_arpu WHERE variant = 'A'
),
b AS (
    SELECT AVG(amount) AS mean_b, VAR_SAMP(amount) AS var_b, COUNT(*) AS n_b
    FROM ab_test_arpu WHERE variant = 'B'
)
SELECT
    b.mean_b - a.mean_a AS effect,
    (b.mean_b - a.mean_a) / SQRT(a.var_a/a.n_a + b.var_b/b.n_b) AS t_stat
FROM a, b;

P-value из t-статистики берут по распределению Стьюдента — это удобнее считать в Python (scipy.stats.t.sf), потому что в SQL нет готовой функции.

Двусторонний и односторонний тест

Двусторонний тест отвечает на вопрос «различаются ли A и B» и даёт p = 2 × (1 − Φ(|z|)). Односторонний отвечает «лучше ли B, чем A» и даёт p = 1 − Φ(z), но он корректен только если направление гипотезы зафиксировано до теста. Задним числом выбирать сторону нельзя — это занижает p-value искусственно. По умолчанию используют двусторонний тест.

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

Как интерпретировать p-value

P-value ниже порога говорит, что данные плохо согласуются с гипотезой «разницы нет», — но не более того. Несколько правил, за которые ценят на собесе:

  • p = 0.04 и p = 0.06 — это практически одинаковая сила доказательства, а не «значимо против незначимо»; порог 0.05 условен.
  • p-value ничего не говорит о размере эффекта. Значимый результат может быть практически бесполезным, если эффект крошечный.
  • Значимость не равна практической важности. Решение принимают по размеру эффекта и доверительному интервалу, а не по одному p-value.

Примеры с собеседований

P-value на собесе проверяют на корректности интерпретации.

«Что такое p-value своими словами?» Слабый ответ — «вероятность, что B лучше» или «вероятность ошибки». Сильный: вероятность увидеть такой или более сильный эффект, если реальной разницы нет. И оговорка, что это не вероятность истинности гипотезы.

«p = 0.06 — эксперимент провалился?» Сильный ответ: не обязательно. 0.06 и 0.04 — почти одинаковая сила доказательства, а порог условен. Стоит посмотреть на размер эффекта, доверительный интервал и мощность теста, а не отбрасывать результат по факту пересечения 0.05.

«p > 0.05 — значит, разницы нет?» Слабый ответ — «да, варианты одинаковы». Сильный: отсутствие значимости не доказывает отсутствие эффекта — тест мог быть недостаточно мощным (мала выборка). Прогнать такие вопросы можно в Карьернике.

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

Трактовать p < 0.05 как «B лучше». Отклонение нулевой гипотезы означает лишь наличие различия, а его знак задаёт эффект — при отрицательном эффекте «значимость» означает, что хуже.

Трактовать p > 0.05 как «разницы нет». Отсутствие доказательства — не доказательство отсутствия. Часто это просто недостаток мощности из-за маленькой выборки.

Игнорировать множественные сравнения. Пять метрик при α = 0.05 дают вероятность хотя бы одного ложного срабатывания около 23%. Нужна поправка (Бонферрони, Холм) или контроль FDR.

Подглядывать в тест (peeking). Если смотреть p-value каждый день и останавливаться при первом p < 0.05, доля ложных срабатываний резко растёт. Для ранней остановки нужен последовательный тест.

Применять t-тест к сильно скошенным данным. Выручка с «китами» далека от нормального распределения; на малых выборках уместнее бутстрап или непараметрические методы.

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

FAQ

Почему порог именно 0.05?

Это историческое соглашение, а не закон. В части областей используют 0.01, где цена ошибки выше. Важно фиксировать порог до теста, а не подбирать под результат, и помнить, что 0.05 — условная граница, а не физическая константа.

Как посчитать p-value, если в PostgreSQL нет функции нормального распределения?

Функцию Φ выражают через erf: Φ(z) = 0.5 × (1 + erf(z / sqrt(2))). Для t-распределения готовой функции нет, и p-value обычно досчитывают в Python через scipy.stats. В SQL считают статистику (z или t), а перевод в p-value делают на стороне анализа.

Что информативнее — p-value или доверительный интервал?

Доверительный интервал, потому что он показывает и факт значимости, и размер эффекта с погрешностью. «Прирост 2–5%» полезнее для решения, чем «p < 0.05», где величина эффекта скрыта.

Что такое поправка Бонферрони?

Способ контроля ложных срабатываний при множественных сравнениях: порог делят на число сравнений, α_adj = α / k. Метод консервативный — при большом k он сильно снижает мощность, поэтому иногда предпочитают менее строгие процедуры (Холм, контроль FDR).