Короткий ответ
Строится матрица «когорта × период с момента привлечения» со значением доли удержанных пользователей в ячейке, отображаемая как heatmap с цветом, кодирующим процент; важно не путать ещё не наступившие периоды с нулевым удержанием и использовать согласованную цветовую шкалу.
Как это работает подробнее
Пользователи группируются по когорте привлечения (например, месяц регистрации), для каждого последующего периода считается доля всё ещё активных, результат раскладывается в матрицу когорта (строка) × номер периода (столбец) и раскрашивается по значению.
Пример кода
retention_heatmap.py
import pandas as pd
import numpy as np
import seaborn as sns
np.random.seed(3)
cohort_sizes = {"2024-01": 1000, "2024-02": 1200, "2024-03": 900}
rows = []
for cohort, size in cohort_sizes.items():
retention = [1.0, 0.45, 0.30, 0.22]
for period, rate in enumerate(retention):
rows.append({"cohort": cohort, "period": period, "retention": rate})
df = pd.DataFrame(rows)
pivot = df.pivot(index="cohort", columns="period", values="retention")
sns.heatmap(pivot, annot=True, fmt=".0%", cmap="Blues")Что использовать на практике
- Применение. Используйте последовательную (не «радужную») цветовую шкалу, чтобы тренд читался однозначно; оставляйте пустыми/серыми ячейки периодов, которых когорта ещё не достигла (это не нулевое удержание, а отсутствие данных); выравнивайте когорты по номеру периода, а не по календарной дате, чтобы столбцы были сопоставимы.
- Проверка.
pivot.loc[:, 0]должен быть равен 1.0 (100%) для каждой когорты в нулевом периоде по определению когорты.
Подводные камни
- Частые ошибки. Сравнение раннего периода молодой когорты с поздним периодом старой когорты как будто это сопоставимые по полноте данные; использование абсолютных чисел вместо процентов для цвета (искажает восприятие из-за разного размера когорт); выбор неудачной цветовой карты, не монотонно отражающей величину.