Как посчитать hazard rate в SQL

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

Зачем hazard rate

Survival говорит «сколько выжило». Hazard rate — «вероятность отвалиться прямо сейчас, при условии что дожил до t». Полезно, чтобы видеть когда наибольший риск: первые 7 дней trial, перед renewal, через месяц после downgrade. На отдельных периодах вырастающий hazard сигнализирует о структурной проблеме.

Формула

h(t_i) = d_i / n_i
  • d_i — события в точке t_i
  • n_i — at-risk до начала t_i

В отличие от probability вообще, hazard условный — «при условии, что юзер дожил».

Hazard rate в SQL

WITH user_times AS (
    SELECT
        CASE
            WHEN event_date IS NOT NULL THEN (event_date - start_date)::INT
            ELSE (observed_until - start_date)::INT
        END AS t,
        event_date IS NOT NULL AS event_occurred
    FROM subscriptions
    WHERE start_date >= '2026-01-01'
),
risk_set AS (
    SELECT
        t,
        COUNT(*) FILTER (WHERE event_occurred) AS events,
        SUM(COUNT(*)) OVER (ORDER BY t DESC) AS at_risk
    FROM user_times
    GROUP BY t
)
SELECT
    t,
    at_risk,
    events,
    events::NUMERIC / NULLIF(at_risk, 0) AS hazard
FROM risk_set
ORDER BY t;

Hazard 0.05 на t=14 означает «5% юзеров, доживших до 14 дней, отвалятся именно на 14-й».

Изменение во времени

Чтобы видеть тренд, агрегируйте по недельным интервалам:

WITH user_times AS (
    SELECT
        CASE
            WHEN event_date IS NOT NULL THEN (event_date - start_date)::INT
            ELSE (observed_until - start_date)::INT
        END AS t,
        event_date IS NOT NULL AS event_occurred
    FROM subscriptions
),
weekly AS (
    SELECT
        (t / 7) AS week,
        COUNT(*) FILTER (WHERE event_occurred) AS events,
        COUNT(*) AS observed
    FROM user_times
    GROUP BY (t / 7)
),
cumulative AS (
    SELECT
        week,
        events,
        SUM(observed) OVER (ORDER BY week DESC) AS at_risk_week_start
    FROM weekly
)
SELECT
    week,
    at_risk_week_start,
    events,
    events::NUMERIC / NULLIF(at_risk_week_start, 0) AS hazard_per_week
FROM cumulative
ORDER BY week;

«Горб» на week=2-3 — типичный паттерн trial-юзеров. «Горб» на week=52 — annual subscription renewal.

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

Hazard ratio между группами

Hazard ratio = h_A(t) / h_B(t). В Cox model даёт «во сколько раз риск выше». Грубо в SQL:

WITH risk_by_group AS (
    SELECT
        plan,
        (t / 7) AS week,
        COUNT(*) FILTER (WHERE event_occurred)::NUMERIC
        / NULLIF(SUM(COUNT(*)) OVER (PARTITION BY plan ORDER BY (t / 7) DESC), 0) AS hazard_week
    FROM user_times
    GROUP BY plan, (t / 7)
)
SELECT
    week,
    MAX(CASE WHEN plan = 'free' THEN hazard_week END) AS hazard_free,
    MAX(CASE WHEN plan = 'paid' THEN hazard_week END) AS hazard_paid,
    MAX(CASE WHEN plan = 'free' THEN hazard_week END)
    / NULLIF(MAX(CASE WHEN plan = 'paid' THEN hazard_week END), 0) AS hazard_ratio
FROM risk_by_group
GROUP BY week
ORDER BY week;

Hazard ratio > 1 → free отваливается быстрее. Стат-значимость через Cox / log-rank — вне SQL.

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

Ошибка 1. Считать events / total, не events / at_risk. Это conversion rate, не hazard. Hazard смотрит только на ещё «живых» в точке t.

Ошибка 2. At-risk через FORWARD running sum. Reverse cumulative: все юзеры с t >= t_current. Postgres SUM(...) OVER (ORDER BY t DESC) или эквивалент.

Ошибка 3. Hazard как survival. Survival = Π(1 - h). Hazard сам по себе — мгновенная вероятность, не накопленная.

Ошибка 4. Hazard на censored events. Censored ≠ event. Не считайте их в d_i, только в n_i.

Ошибка 5. Сравнивать hazards без учёта confounders. Free и paid могут отличаться не только тарифом, но и source-каналом. Cox model для multiple covariates.

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

FAQ

Hazard vs churn rate?

Churn rate обычно period-based и не учитывает «доживание». Hazard — условная вероятность в точке t.

Hazard и risk — одно?

Нет. Risk — кумулятивная вероятность к моменту t. Hazard — мгновенная.

Какой hazard «нормален»?

Зависит от продукта. Для SaaS week-1 hazard 5-10% типично, потом падает.

Hazard 0 — это значит «бессмертный»?

Может быть censoring или маленький at_risk. Проверяйте at_risk рядом.

Hazard ratio > 2 — много?

В medical > 2 — серьёзный effect. В продукте контекст-зависимо.