Когортный анализ удержания
Кратко
Когортный анализ группирует пользователей по моменту старта и отслеживает, какая доля возвращается в каждый следующий период. Это отвечает на вопрос «удерживает ли продукт пользователей со временем» — но честнее, чем единое число retention.
Когорта — группа людей, у которых первое действие пришлось на один период (обычно месяц). Матрица удержания: строки — когорты, столбцы — месяцы с момента старта, ячейка — доля вернувшихся. Получается треугольная таблица: у свежих когорт заполнены только первые столбцы.
Содержание
Пример
Построение матрицы в Pandas. На входе таблица событий events с user_id и event_date:
import pandas as pd
import numpy as np
df = pd.read_csv(
'events.csv', parse_dates=['event_date']
)
# Месяц первого действия = когорта
cohort = (
df.groupby('user_id')['event_date'].min()
.reset_index()
.rename(columns={'event_date': 'cohort_month'})
)
cohort['cohort_month'] = (
cohort['cohort_month'].dt.to_period('M')
)
# Сливаем когорту обратно к событиям
events = df.merge(cohort, on='user_id')
events['activity_month'] = (
events['event_date'].dt.to_period('M')
)
# Номер месяца с момента старта (period diff)
events['period'] = (
events['activity_month'].astype('int64')
- events['cohort_month'].astype('int64')
)
# Матрица: уникальные пользователи по когортам
counts = (
events
.groupby(['cohort_month', 'period'])
['user_id'].nunique()
.reset_index()
)
size = cohort.groupby('cohort_month')['user_id'].nunique()
retention = counts.pivot(
index='cohort_month',
columns='period',
values='user_id',
)
# Делим на размер когорты → доли
retention = retention.divide(size, axis=0)
На выходе retention — DataFrame, где retention.loc['2025-01', 3] — доля январской когорты, вернувшейся через 3 месяца.
Как пишется
Определение когорты
Когорта задаётся точкой первого действия. Что считать «первым» — решает задача:
- Первый визит — для продукта, где активность = присутствие.
- Первая покупка — для монетизации и LTV.
- Регистрация — для онбординг-метрик.
Первый визит безопаснее по умолчанию: он ловит всех, кто попал в продукт. Первая покупка отсекает тех, кто зарегистрировался, но не купил — они отдельная история.
Период
Период — шаг столбцов. Месяц — стандарт для B2C, неделя — для ранних стартапов с быстрым циклом, день — для мобильных игр. Период выбирают так, чтобы внутри него большинство пользователей совершили возврат хотя бы раз.
Смещение периода
period — разница между месяцем активности и месяцем когорты. period = 0 — месяц старта, period = 1 — следующий месяц. Это не «календарный месяц номер 2», а «месяц N с момента старта конкретной когорты».
Как понять
Почему матрица треугольная
Когорта «январь 2025» к марту имеет period 0, 1, 2. Когорта «март 2025» к тому же моменту имеет только period 0. Поэтому правый верхний угол пустой — будущие периоды ещё не наступили. Это не пропуски в данных, а структура.
Правый край ненадёжен
У поздних столбцов меньше когорт, поэтому среднее по столбцу прыгает. Если в period = 6 попала только одна когорта, её 35% — это не «удержание на шестом месяце», а «удержание одной когорты». Не делайте выводов о долгосрочном удержании по правому краю, пока не наберётся 5–10 когорт.
Столбец 0 = 100% по определению
В нулевом месяце клиент присутствует всегда — он же только что стартовал. Поэтому period = 0 всегда 100% (или близко). Этот столбец не несёт сигнала об удержании, он — база. Его оставляют для самопроверки: если period = 0 не 100%, в коде ошибка.
Диагонали и сезонность
Если у всех когорт провал на period = 3, это цикл продукта. Если провал в конкретном календарном месяце у всех когорт одновременно — сезонность или общая проблема (сбой, релиз). Диагональ «по календарному месяцу» ловит сезонность; «по периоду» — ловит продуктовый цикл.
Агрегатное удержание врёт
Единый «30% monthly retention» может означать:
- Все когорты удерживают по 30%.
- Новые удерживают 50%, старые скатились до 10%.
Второй случай — продуктовая проблема, но агрегат его прячет. Когортная матрица разделяет эти сценария: на строках видно, кто держится, кто падает.
Ограничения
Когортная матрица — мощный, но не единственный инструмент. Вот что она не показывает:
Классическая когорта ≠ rolling retention
Классическая когортная матрица считает удержание как «был ли пользователь активен хотя бы раз в период N». Rolling retention (или возвратное удержание) считает иначе: пользователь удержан, если он вернулся в период N или позже. Rolling retention всегда выше или равен классическому и лучше отражает долгосрочную лояльность, но не видит «провалов» в активности.
Нет взвешивания по активности
Матрица не различает пользователя, который зашёл один раз в месяц, и того, кто заходит ежедневно. Для метрик «вовлечённости» (daily active, session length) нужны отдельные инструменты — поверх когортной разбивки.
Сравнение когорт — не A/B-тест
Разница в удержании между когортами «январь» и «февраль» может быть вызвана сезонностью, изменением каналов трафика или составом аудитории — не только продуктовым изменением. Для каузальных выводов нужен A/B-тест в рамках одной когорты.
Малые когорты шумят
Если в когорте меньше ~100 пользователей, доли retention скачут от 0% до 100% от периода к периоду. Для B2B или нишевых продуктов с малым числом клиентов когортный анализ лучше делать по неделям или дням, а не по месяцам.
Survivorship bias в долгосрочных столбцах
Правый край матрицы состоит из когорт, которые «дожили» до этого периода. Если продукт менялся — а он меняется — старые когорты пережили другую версию продукта. Их удержание не предсказывает поведение новых когорт на тех же сроках.
Подсказки
- Когорту определяйте под вопрос: визит / покупка / регистрация.
period = 0всегда 100% — если нет, ищите баг в коде.- Правый край матрицы нечитаем при малом числе когорт — не делайте выводов.
- Срезайте по каналу, платформе, плану: когорты из платной рекламы ведут себя иначе, чем органика.
- Удержание без цели — просто число. Сравнивайте с бенчмарком продукта или прошлыми когортами.
- Обновляйте матрицу ежемесячно; сдвиг шага пересчитывает все когорты.
- Не путайте retention с churn: retention = доля вернувшихся, churn = доля ушедших.
На практике
В проекте volta-banking — аналитика необанка — когортная матрица нашла step-change, который агрегат прятал. В сентябре 2024 поменяли прогресс-бар в KYC-флоу (проект 2: A/B-тест дал +4,82 пп, p<0,0001). Когорты до и после фикса разошлись:
- Пост-фикс когорты (сен–дек 2024) показывают M1 retention на +10–12 пп выше, чем пре-фикс.
- Все когорты выходят на плато к M5–M6 — естественный сегмент лояльных.
- Пост-фикс плато стабилизируется выше (~25–28%), то есть фикс поднял не только активацию, но и долгосрочное удержание.
Что дала матрица, чего не дал бы агрегат:
- Видно, где именно улучшилось (M1, не M0) — значит, фикс повлиял на возврат, а не на старт.
- Видно плато — значит, после M5 качать удержание дороже, чем активацию.
- Срезы по каналу и плану показали 3× разрыв LTV между Premium и Free — отдельная гипотеза для монетизации.
Рецепт старта:
- Возьмите сырые события, определите когорту по первому визиту.
- Считайте
periodчерез разностьastype('int64')периодов (быстрее и чище, чемapply). - Пивот в матрицу, делите на размер когорты.
- Сначала читайте столбец M1 — он самый чувствительный к качеству онбординга.
- Добавьте срезы: канал, план, платформа. Один срез = одна гипотеза.
- Сравнивайте когорты до/после изменения продукта — step-change виден на строках.
На собеседовании
❓ Чем retention отличается от churn?
Retention — доля пользователей, вернувшихся в период N. Churn — доля ушедших. В простом случае retention + churn = 1, но определения расходятся: retention считают от исходной когорты, churn часто считают от активной базы на начало периода. Поэтому «retention 30%» и «churn 70%» могут описывать разное. Уточняйте определение на месте.
— Nikita Boyarkin
❓ Когорту определять по первому визиту или по первой покупке?
Зависит от вопроса. Первая покупка — для монетизации, LTV, RFM. Первый визит — для удержания как присутствия, онбординга, активации. Частая ошибка — считать retention по покупкам там, где продукт freemium и 80% никогда не платят: матрица покажет «все отваливаются», хотя пользователи просто не покупают, а заходят. Подбирайте определение под метрику вопроса.
— Nikita Boyarkin
❓ Почему правый край когортной матрицы ненадёжен и как с этим жить?
У поздних столбцов меньше когорт — к марту cohort «январь» дошла до M2, а «март» только до M0. Среднее по столбцу M6 при одной когорте — это не «удержание на шестом месяце», а удержание одной когорты. Жить так: не делайте выводов о долгосрочном удержании, пока не наберётся 5–10 когорт в столбце; для прогноза LTV используйте только заполненные столбцы и модель распадения, а не среднее по правому краю.
— Nikita Boyarkin
Ресурсы
- Cohort Analysis with Python — полный код когортной матрицы + визуализация в проекте volta-banking.
- Cohort Analysis — Wikipedia — базовая терминология и история метода.
- Retention Cohorts — Amplitude — продуктовый взгляд на когорты: когда какой тип удержания выбирать.
- How to Lie with Retention — Andrew Chen о том, как манипулируют кривыми удержания (и как не попасться).
- Volta Dashboard — интерактивный дашборд с когортной матрицей, срезами и LTV-моделью.