Когортный анализ удержания

Кратко

Когортный анализ группирует пользователей по моменту старта и отслеживает, какая доля возвращается в каждый следующий период. Это отвечает на вопрос «удерживает ли продукт пользователей со временем» — но честнее, чем единое число 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 — отдельная гипотеза для монетизации.

Рецепт старта:

  1. Возьмите сырые события, определите когорту по первому визиту.
  2. Считайте period через разность astype('int64') периодов (быстрее и чище, чем apply).
  3. Пивот в матрицу, делите на размер когорты.
  4. Сначала читайте столбец M1 — он самый чувствительный к качеству онбординга.
  5. Добавьте срезы: канал, план, платформа. Один срез = одна гипотеза.
  6. Сравнивайте когорты до/после изменения продукта — 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-моделью.

Смотрите также

← К статьям