Как посчитать t-test в SQL

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

Зачем t-test

T-test проверяет, отличаются ли средние двух выборок. В A/B используют, когда метрика непрерывная — выручка на юзера, время на странице, число просмотров. Welch's t-test — версия для разных дисперсий и размеров групп (default в современных стат-пакетах).

Welch's формула

t = (μ_A − μ_B) / sqrt(σ_A²/n_A + σ_B²/n_B)
df = (σ_A²/n_A + σ_B²/n_B)² / ((σ_A²/n_A)² / (n_A − 1) + (σ_B²/n_B)² / (n_B − 1))

При df > 30 распределение t практически совпадает с нормальным — критическое значение для α=0.05 (двусторонний) ≈ 1.96.

T-test в SQL

WITH stats AS (
    SELECT
        variant,
        COUNT(*) AS n,
        AVG(revenue) AS mean,
        VAR_SAMP(revenue) AS variance
    FROM ab_users
    WHERE experiment_id = 'pricing_v2'
    GROUP BY variant
),
pair AS (
    SELECT
        MAX(CASE WHEN variant = 'A' THEN n END) AS n_a,
        MAX(CASE WHEN variant = 'A' THEN mean END) AS mean_a,
        MAX(CASE WHEN variant = 'A' THEN variance END) AS var_a,
        MAX(CASE WHEN variant = 'B' THEN n END) AS n_b,
        MAX(CASE WHEN variant = 'B' THEN mean END) AS mean_b,
        MAX(CASE WHEN variant = 'B' THEN variance END) AS var_b
    FROM stats
)
SELECT
    mean_a,
    mean_b,
    mean_b - mean_a AS diff,
    (mean_b - mean_a) / SQRT(var_a / n_a + var_b / n_b) AS t_statistic,
    POWER(var_a / n_a + var_b / n_b, 2)
    / (POWER(var_a / n_a, 2) / NULLIF(n_a - 1, 0)
       + POWER(var_b / n_b, 2) / NULLIF(n_b - 1, 0)) AS welch_df,
    CASE
        WHEN ABS((mean_b - mean_a) / SQRT(var_a / n_a + var_b / n_b)) > 1.96
        THEN 'significant at ~0.05'
        ELSE 'NOT significant'
    END AS verdict
FROM pair;

A/B-тест по ARPU

То же, но revenue per user считают агрегацией ордеров:

WITH user_revenue AS (
    SELECT
        u.user_id,
        u.variant,
        COALESCE(SUM(o.amount), 0) AS revenue
    FROM ab_users u
    LEFT JOIN orders o
      ON o.user_id = u.user_id
     AND o.created_at >= u.bucket_at
    WHERE u.experiment_id = 'paywall_v3'
    GROUP BY u.user_id, u.variant
),
stats AS (
    SELECT
        variant,
        COUNT(*) AS n,
        AVG(revenue) AS mean,
        VAR_SAMP(revenue) AS variance
    FROM user_revenue
    GROUP BY variant
)
SELECT * FROM stats;

Дальше из stats тот же блок Welch-формулы, что в предыдущем примере.

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

Paired t-test

Когда сравниваете одного юзера в двух условиях (до/после):

WITH paired AS (
    SELECT
        user_id,
        revenue_before,
        revenue_after,
        revenue_after - revenue_before AS diff
    FROM user_before_after
    WHERE revenue_before IS NOT NULL
      AND revenue_after IS NOT NULL
)
SELECT
    COUNT(*) AS n,
    AVG(diff) AS mean_diff,
    STDDEV_SAMP(diff) AS sd_diff,
    AVG(diff) / NULLIF(STDDEV_SAMP(diff) / SQRT(COUNT(*)), 0) AS t_statistic
FROM paired;

df = n − 1.

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

Ошибка 1. Population vs sample variance. В SQL VAR_POP делит на N, VAR_SAMP — на N-1. Для t-test нужен VAR_SAMP (sample).

Ошибка 2. T-test на сильно скошенных данных. Revenue distributions обычно log-normal. На малых выборках t-test перекосит. Bootstrap или log-transform.

Ошибка 3. Считать без NULL-фильтра. AVG игнорирует NULL, но COUNT(*) — нет. n окажется завышен → дисперсия среднего занижена.

Ошибка 4. Один юзер в двух группах. SRM / cross-contamination. Перепроверьте ab_users на дубли user_id.

Ошибка 5. Сравнивать с z-test на маленьких выборках. До n ≈ 30 разница t и z существенна. После — пренебрежима.

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

FAQ

Welch's vs Student's?

Welch's — для разных дисперсий и разных n. Default в scipy и большинстве A/B-калькуляторов. Student's — только если дисперсии равны (проверяют редко).

t-statistic, t-statistic с p-value?

t-stat сам по себе бесполезен. P-value через scipy.stats.t.sf(|t|, df) * 2 (двусторонний).

df < 30, что делать?

Используйте точное t-распределение. Не приближайте нормальным.

Skewed revenue?

Bootstrap CI или log-transform. На большой n (10k+) Welch остаётся валидным даже на skewed.

A/B по конверсии — t-test нужен?

Нет, для бинарных метрик — chi-square или two-proportion z-test.