Условие
В образовательной платформе средний цикл от регистрации до покупки курса — 14 дней. Маркетинг утверждает, что conv = 8% (за последние 7 дней регистраций). Что не так?
Решение
Проблема
Конверсия 8% за 7 дней — это неполная: половина пользователей просто не успели конвертнуться. Если медианное время до покупки = 5 дней, 7-дневная конверсия покажет 60% от финальной; если медиана = 14 дней — 30%.
Правильный способ
Cohort-based conversion с полным окном:
WITH cohort AS (
SELECT user_id, MIN(ts) AS reg_ts
FROM users
WHERE reg_ts BETWEEN '2024-01-01' AND '2024-01-07'
GROUP BY user_id
),
purch AS (
SELECT user_id, MIN(purchase_ts) AS first_purchase
FROM purchases
GROUP BY user_id
)
SELECT
COUNT(*) AS cohort_size,
COUNT(p.user_id) FILTER (WHERE p.first_purchase <= reg_ts + INTERVAL '7 days') / COUNT(*)::float AS d7_conv,
COUNT(p.user_id) FILTER (WHERE p.first_purchase <= reg_ts + INTERVAL '14 days') / COUNT(*)::float AS d14_conv,
COUNT(p.user_id) FILTER (WHERE p.first_purchase <= reg_ts + INTERVAL '30 days') / COUNT(*)::float AS d30_conv,
COUNT(p.user_id) / COUNT(*)::float AS ever_conv
FROM cohort c LEFT JOIN purch p USING (user_id);Для cohort, у которой прошло меньше 30 дней, d30_conv показать нельзя — это censored data.
Что показывать в дашборде
- D7, D14, D30 для зрелых cohort.
- Кривая «conversion vs days since signup» (доля купивших в течение N дней).
- Predicted final conversion — экстраполяция по форме кривой (для свежих cohort).
Survival analysis
Лучшая техника для длинных циклов — Kaplan-Meier:
from lifelines import KaplanMeierFitter
kmf = KaplanMeierFitter()
kmf.fit(durations=time_to_purchase, event_observed=did_purchase)
kmf.plot_survival_function()
# 1 - survival = cumulative conversionKM правильно обрабатывает censored cases (пользователь ещё не купил, но и не выпал).
Подводные камни
- «Конверсия за период X» без cohort — смешивает зарегистрировавшихся вчера и месяц назад. Это не средняя конверсия.
- Censored data: для свежих cohort нет полной информации. Не показывайте «d30 conv = 1%» для cohort недельной давности.
- Если поменялся продукт, cohort до и после смешивать нельзя.
- Считать first purchase, а не «купил когда-либо» — иначе двойной учёт.
- Lag-эффект разных каналов разный (search быстрый, бренд медленный) — сравнивать каналы только на одинаковом окне.
Эталонный ответ
Cohort + явное окно (D7, D14, D30, ever). Для свежих cohort с censored data — Kaplan-Meier. Не смешивать «зарегился вчера» и «зарегился месяц назад» в одну метрику.