Как посчитать path analysis в SQL

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

Зачем path analysis

Path analysis выявляет, какие последовательности действий приводят к конверсии чаще остальных. Если 80% покупателей прошли через pricing → faq → checkout, а 20% — напрямую, то страница FAQ полезна. Если из 100 путей landing → demo → support только 2% покупают, а landing → blog → demo даёт 30% — что-то сломано в первом сценарии.

Топ путей до конверсии

WITH paths AS (
    SELECT
        user_id,
        STRING_AGG(event_name, ' -> ' ORDER BY event_timestamp) AS path
    FROM events
    WHERE event_timestamp >= CURRENT_DATE - INTERVAL '30 days'
    GROUP BY user_id
    HAVING SUM(CASE WHEN event_name = 'purchase' THEN 1 ELSE 0 END) > 0
)
SELECT
    path,
    COUNT(*) AS users,
    COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () AS share_pct
FROM paths
GROUP BY path
ORDER BY users DESC
LIMIT 20;

Биграмные переходы

Считаем переходы «событие i → событие i+1»:

WITH ordered AS (
    SELECT
        user_id,
        event_name,
        LEAD(event_name) OVER (PARTITION BY user_id ORDER BY event_timestamp) AS next_event
    FROM events
),
transitions AS (
    SELECT
        event_name AS from_event,
        next_event AS to_event,
        COUNT(*) AS transitions
    FROM ordered
    WHERE next_event IS NOT NULL
    GROUP BY event_name, next_event
)
SELECT
    from_event,
    to_event,
    transitions,
    transitions * 100.0 / SUM(transitions) OVER (PARTITION BY from_event) AS share_from_pct
FROM transitions
ORDER BY from_event, transitions DESC;

Видно, куда чаще уходят с конкретной страницы. pricing → close_tab — плохой сигнал.

Конверсия по пути

Какая доля юзеров с данным путём купила:

WITH user_paths AS (
    SELECT
        user_id,
        STRING_AGG(event_name, ' -> ' ORDER BY event_timestamp)
            FILTER (WHERE event_name <> 'purchase') AS path_before,
        MAX(CASE WHEN event_name = 'purchase' THEN 1 ELSE 0 END) AS converted
    FROM events
    WHERE event_timestamp >= CURRENT_DATE - INTERVAL '30 days'
    GROUP BY user_id
),
agg AS (
    SELECT
        path_before,
        COUNT(*) AS users,
        SUM(converted) AS conversions,
        SUM(converted) * 100.0 / COUNT(*) AS cr_pct
    FROM user_paths
    WHERE path_before IS NOT NULL
    GROUP BY path_before
)
SELECT *
FROM agg
WHERE users >= 50
ORDER BY cr_pct DESC
LIMIT 20;

users >= 50 отсекает single-юзер пути — иначе CR 100% будет у любого случайного юзера.

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

Find high-converting sequences

Ищем 3-граммы, которые часто встречаются перед конверсией:

WITH numbered AS (
    SELECT
        user_id,
        event_name,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_timestamp) AS step
    FROM events
),
trigrams AS (
    SELECT
        n1.user_id,
        n1.event_name || ' -> ' || n2.event_name || ' -> ' || n3.event_name AS trigram,
        MAX(CASE WHEN nc.event_name = 'purchase' THEN 1 ELSE 0 END) AS converted
    FROM numbered n1
    JOIN numbered n2 ON n2.user_id = n1.user_id AND n2.step = n1.step + 1
    JOIN numbered n3 ON n3.user_id = n1.user_id AND n3.step = n1.step + 2
    LEFT JOIN events nc ON nc.user_id = n1.user_id AND nc.event_name = 'purchase'
    GROUP BY n1.user_id, n1.event_name, n2.event_name, n3.event_name
)
SELECT
    trigram,
    COUNT(*) AS occurrences,
    SUM(converted) AS conversions,
    SUM(converted) * 100.0 / COUNT(*) AS cr_pct
FROM trigrams
GROUP BY trigram
HAVING COUNT(*) >= 100
ORDER BY cr_pct DESC
LIMIT 20;

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

Ошибка 1. Считать пути на разнородных событиях. Если в events смешаны page views и backend events — путь становится мусором. Фильтруйте до значимых.

Ошибка 2. Игнорировать длинный хвост путей. Топ-20 покрывает 30% юзеров, остальные 70% — уникальные. Анализируйте биграммы вместо полных путей.

Ошибка 3. CR без статистической значимости. Путь «A → B → C» с CR 60% при 10 юзерах — шум. users >= 50 минимум.

Ошибка 4. Pre/post-конверсия в одном пути. Если конверсия в середине, дальнейшие события — это уже post-покупка (поддержка, refund). Обрезайте после первой purchase.

Ошибка 5. Не учитывать sessionization. Один юзер ↔ много сессий с разрывом в дни. Объединять в один путь — спорно. Иногда лучше split по 30-минутным окнам.

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

FAQ

Биграммы или полные пути?

Биграммы дают больше данных и стат-значимости. Полные пути — для качественного анализа топ-сценариев.

Сколько событий минимум?

Триграмма — минимум 3 события. Для CR-сравнения — 100+ повторений сетапа.

Path analysis в Postgres медленный?

На 10M+ событий — да. ClickHouse / DuckDB подходят лучше.

Как обработать петли?

landing → blog → landing → pricing — циклы можно свернуть или оставить как есть. Зависит от вопроса.

Path vs Markov chain?

Markov даёт transitional probabilities. Path analysis — фактические наблюдения. Markov делает предсказание.