Реализация Retention-анализа по когортам для мобильного приложения
Самая распространённая ситуация: маркетинг рапортует о росте установок, а MAU стоит на месте. Проблема в том, что без когортного retention-анализа продукт не видит, когда именно пользователи уходят — на второй день, через неделю или после первой транзакции. Метрика Day 1, Day 7, Day 30 Retention — это не просто цифры для дашборда, а диагностический инструмент, который указывает на конкретную точку отвала.
Что такое когорта и почему агрегированный retention врёт
Когорта — группа пользователей, объединённых датой первого события. Чаще всего это дата установки (install_date), реже — дата первой покупки или регистрации.
Без когортной разбивки retention считается по формуле: активные сегодня / активные за период. Это усреднение по всем пользователям смешивает новых и старых. Приложение может показывать стабильный 30-дневный retention 20%, хотя последние когорты деградируют до 8% — просто старые, лояльные пользователи тянут среднее вверх.
Когортный анализ считает для каждой когорты отдельно:
Day N Retention = unique_users_active_on_day_N / cohort_size
Где Day 0 — день установки, Day 1 — следующий календарный день (не 24 часа). Разница в интерпретации дня важна: Firebase по умолчанию считает по календарным дням в timezone пользователя, Amplitude и Mixpanel можно настроить и так, и так.
Инструментарий и архитектура событий
Что трекать на клиенте
Минимальный набор для retention-анализа:
-
app_open— факт запуска (Firebase Analytics логирует автоматически какsession_start) -
user_engagement— Firebase считает это сам, но лучше определить свойmeaningful_action— действие, которое для вашего продукта означает «пользователь нашёл ценность» -
install— атрибуция установки, нужна для правильного определения cohort_date
На iOS через Firebase SDK:
// AppDelegate или SceneDelegate
Analytics.logEvent("meaningful_action", parameters: [
"action_type": "first_purchase" as NSObject,
"item_category": product.category as NSObject
])
На Android (Kotlin):
firebaseAnalytics.logEvent("meaningful_action") {
param("action_type", "first_purchase")
param("item_category", product.category)
}
Ключевая ошибка — логировать app_open вместо meaningful_action. Тогда retention считается от факта запуска, а не от реального использования. Пользователь открыл приложение случайно — и он уже «retained» на этот день.
BigQuery + Firebase: настоящий когортный анализ
Firebase Console показывает retention только в виде усреднённой кривой. Для когортных таблиц нужен экспорт в BigQuery (бесплатный Spark-план Firebase его поддерживает с лимитами).
После подключения BigQuery события льются в таблицы вида events_YYYYMMDD. Запрос для когортной таблицы Day 0–7:
WITH installs AS (
SELECT
user_pseudo_id,
DATE(TIMESTAMP_MICROS(event_timestamp), "Europe/Moscow") AS cohort_date
FROM `project.analytics_XXXXXXXXX.events_*`
WHERE event_name = 'first_open'
),
activity AS (
SELECT
user_pseudo_id,
DATE(TIMESTAMP_MICROS(event_timestamp), "Europe/Moscow") AS activity_date
FROM `project.analytics_XXXXXXXXX.events_*`
WHERE event_name = 'session_start'
)
SELECT
i.cohort_date,
COUNT(DISTINCT i.user_pseudo_id) AS cohort_size,
DATE_DIFF(a.activity_date, i.cohort_date, DAY) AS day_n,
COUNT(DISTINCT a.user_pseudo_id) AS retained_users,
ROUND(COUNT(DISTINCT a.user_pseudo_id) / COUNT(DISTINCT i.user_pseudo_id), 3) AS retention_rate
FROM installs i
LEFT JOIN activity a
ON i.user_pseudo_id = a.user_pseudo_id
AND a.activity_date BETWEEN i.cohort_date AND DATE_ADD(i.cohort_date, INTERVAL 30 DAY)
GROUP BY 1, 3
ORDER BY 1, 3
Этот запрос даёт таблицу: каждая строка — когорта + день + retention rate. Из неё строится тепловая карта в Looker Studio, Data Studio или прямо в Metabase.
Amplitude и Mixpanel как альтернатива
Для продуктов без BigQuery-экспертизы Amplitude — удобнее. Встроенный Retention Analysis строит когортные таблицы в несколько кликов. Но важно правильно настроить User ID:
На iOS нужно передавать стабильный идентификатор до первого identify:
Amplitude.instance().setUserId(user.stableId)
Amplitude.instance().logEvent("meaningful_action")
Если userId не задан, Amplitude создаёт device-based identity — и один пользователь с двумя устройствами считается двумя разными. Retention занижается.
Типичные ошибки при настройке когортного анализа
Смешение timezone. Если сервер логирует события в UTC, а Firebase считает Day N по локальному времени пользователя — когорты расползаются. Пользователь установил приложение в 23:50 по московскому времени, сервер записал это в UTC следующего дня. Когорта сдвигается на сутки.
Пересчёт cohort_date при переустановке. После удаления и повторной установки Firebase генерирует новый instance_id и новый first_open. Пользователь попадает в новую когорту. Если это не учесть в аналитике, retention когорт занижается — возвращающиеся пользователи выглядят как новые.
Маленькие когорты и статистический шум. Когорта из 15 пользователей даёт бессмысленные цифры: ±1 пользователь — это ±7% retention. Когортный анализ даёт надёжные данные при размере когорты от 200–300 пользователей.
Визуализация и продуктовые выводы
Стандартная тепловая карта выглядит так:
| Когорта | Размер | Day 1 | Day 3 | Day 7 | Day 14 | Day 30 |
|---|---|---|---|---|---|---|
| 2024-01-01 | 420 | 38% | 22% | 14% | 9% | 6% |
| 2024-01-08 | 380 | 41% | 25% | 16% | 11% | 7% |
| 2024-01-15 | 510 | 29% | 18% | 11% | 7% | 4% |
Когорта от 15 января резко хуже — совпадает с релизом версии 2.3.0. Продукт видит это немедленно и откатывает или фиксит до того, как деградация распространится на весь пользовательский базис.
Что входит в работу
- Аудит текущей схемы событий, проверка наличия
first_open/meaningful_action - Настройка BigQuery-экспорта из Firebase или конфигурация Amplitude Retention
- SQL-запросы для когортных таблиц с учётом timezone
- Дашборд в Looker Studio / Metabase / Redash
- Документация: словарь событий, описание cohort_date logic
Сроки
Настройка с нуля: 3–5 дней (зависит от текущего состояния аналитики и наличия BigQuery). Если события уже настроены — 1–2 дня на запросы и дашборд. Стоимость рассчитывается индивидуально после анализа требований.







