Если вы являетесь аналитиком данных в SaaS-компании или предприятии электронной коммерции с компонентом подписки, вас обязательно попросят проанализировать такие метрики, как отток, апгрейды и даунгрейды. Данные показатели помогают бизнесу понять состояние вашей базы подписчиков:
Состояние вашей абонентской базы независимо от сезонности продлений (поскольку вы амортизируете доходы по месяцам).
Источник изменения доходов: новые клиенты, апгрейды, даунгрейды, оттоки или реактивации.
Ценность клиента, основанная на том, насколько долго данный пользователь продолжает платить вам деньги: пожизненная ценность клиента, средняя сумма контракта.
Часто такой анализ называют анализом регулярного ежемесячного дохода (Monthly Recurring Revenue, MRR), и его результаты выглядят примерно так:
Иногда аналитики создают MRR-дашборд путем написания запросов на основе необработанных данных. Со временем эти запросы могут стать слишком сложными и громоздкими, а логика будет повторяться в нескольких местах. В нашем дашборде вместо этого выбирается следующая модель: таблица, содержащая преобразованные данные, готовые к анализу. Поэтому запросы в нашем дашборде очень просты — вот один из них для расчета количества клиентов и общего MRR за каждый месяц:
select
date_month,
sum(is_active::integer) as customers,
sum(mrr) as mrr,
from analytics.fct_mrr
group by 1
Построение дашборда на основе модели данных, вместо необработанной информации, дает ряд преимуществ:
Ваша бизнес-логика кодифицирована: Каждый бизнес уникален. То, как в вашей компании определяют отток, скорее всего, будет незначительно отличаться от другой организации. Построив модель данных, содержащую эту бизнес-логику, вы сможете гарантировать, что в вашей деятельности все используют одинаковые формулировки.
Вам придется преобразовывать данные только в одном месте: Скорее всего, ваши исходные данные для подписок не идеальны. Вам может понадобиться их подчистить, например, преобразовать пустые строки в значения NULL. Или вам может потребуется изменить свойство (grain) таблиц, например, преобразовать таблицу с одной записью на подписку с датами начала и окончания в таблицу в виде одной записи на текущий месяц. Выполнив эту работу в модели данных, вам не придется делать ее повторно при построении аналогичных анализов или в различных BI-инструментах.
Ваши предложения относительно модели данных могут быть проверены: Создавая модели в dbt, вы можете явно указать и затем проверить любые предположения, которые вы делаете о своих данных. Например, если ваша бэкенд-команда сообщает, что клиент не может иметь две активные подписки одновременно, неплохо было бы добавить тест для подтверждения этого (вот пример).
Бизнес-пользователи могут более простым способом изучать данные: Если в этом месяце количество оттоков выглядело несколько выше обычного, бизнес-пользователи могут зайти в систему и написать простой SQL, чтобы понять, какие именно клиенты ушли, не перегружая себя сложными данными.
Модель данных MRR
Каждая диаграмма на этом дашборде выбирается из модели данных. Эта модель содержит по одной записи на каждого клиента в месяц (часто это называют "grain" модели). Каждая запись содержит соответствующий регулярный доход за месяц (MRR), а также категорию изменения, которые описывают доход клиента по сравнению с прошлым месяцем.
date_month | customer_id | mrr | mrr_change | change_category |
---|---|---|---|---|
2018-11-01 00:00:00.000 | 1 | 50 | 50 | new |
2018-12-01 00:00:00.000 | 1 | 50 | 0 |
|
2019-01-01 00:00:00.000 | 1 | 50 | 0 |
|
2019-02-01 00:00:00.000 | 1 | 0 | -50 | churn |
2019-03-01 00:00:00.000 | 1 | 0 | 0 |
|
2019-04-01 00:00:00.000 | 1 | 50 | 50 | reactivation |
2019-05-01 00:00:00.000 | 1 | 50 | 0 |
|
2019-06-01 00:00:00.000 | 1 | 75 | 25 | upgrade |
2019-07-01 00:00:00.000 | 1 | 75 | 0 |
|
2019-08-01 00:00:00.000 | 1 | 0 | -75 | churn |
… | … | … | … | … |
Следует рассмотреть категории изменений:
новый: клиент - новый клиент, у которого не было предыдущей подпискиф;
отток: в прошлом месяце клиент оплатил подписку, а в этом месяце нет. Клиент может покидать сервис много раз;
апгрейд: клиент прибавил в объеме использования и теперь платит вам больше денег в месяц;
даунгрейд: клиент уменьшил потребление и теперь платит вам меньше денег в месяц;
реактивация: клиент, который ранее отключался, возобновил подписку.
Если вы сразу готовы окунуться в проект dbt, чтобы увидеть, как это делается, посмотрите наш образец MRR-модели здесь. Или читайте дальше, для более детального ознакомления.
Как смоделировать данные по доходам от подписки
Шаг 1: Соберите необходимые источники данных
Прежде чем преобразовывать данные, они должны быть в вашем хранилище. Данные о подписке в зависимости от вашей исходной системы могут иметь различную структуру - приведем некоторые из них:
Таблица периодов подписки с датами начала и окончания.
Таблица ежемесячных инвойсов, в которой сумма счета представляет собой стоимость подписки.
Таблица событий изменения, таких как апгрейды, даунгрейды и аннулирования.
Для данного учебного пособия мы будем использовать таблицу периодов подписки.
subscription_id | customer_id | start_date | end_date | monthly_amount |
---|---|---|---|---|
1 | 1 | 2018-11-01 | 2019-02-01 | 50 |
2 | 1 | 2019-04-01 | 2019-06-01 | 50 |
3 | 1 | 2019-06-01 | 2019-08-01 | 75 |
Если у вас данные о подписке выглядят иначе, используйте dbt, чтобы преобразовать их в эту форму.
К счастью, наши данные также оказались абсолютно чистыми:
Каждый клиент одновременно обладает только одной активной подпиской.
Дата старта и окончания каждой подписки приходится на начало месяца.
Мы даже подтвердили верность наших предположений, применив некоторые dbt-тесты!
Шаг 2: Распределите подписки по датам так, чтобы на каждого клиента приходилось по одной записи в месяц
Требуемая техника SQL: распределение дат (date spining) | Требуемая техника dbt: пакеты
Поскольку мы хотим просмотреть ежемесячные изменения, нам нужно так распределить наши подписки, чтобы иметь одну запись на месяц, а не одну запись на действующую подписку:
date_month | customer_id | monthly_amount |
---|---|---|
2018-11-01 | 1 | 50 |
2018-12-01 | 1 | 50 |
2019-01-01 | 1 | 50 |
2019-04-01 | 1 | 50 |
2019-05-01 | 1 | 50 |
2019-06-01 | 1 | 75 |
2019-07-01 | 1 | 75 |
… | … | … |
Обычно мы делаем это за счет добавления к таблице "месяцев":
select
months.date_month,
subscriptions.customer_id,
subscriptions.subscription_id,
susbcriptions.monthly_revenue
from subscriptions
inner join months
-- all months after start date
on months.date_month >= customers.date_month_start
-- and before end date
and months.date_month <= customers.date_month_end
Мы используем макрос date_spine из пакета dbt-utils для создания таблицы всех месяцев.
Но подождите, у нашего клиента не было подписки с февраля (2019-02-01) по апрель (2019-04-01), и мы хотим зафиксировать это как "отток" и "реактивацию"!
Для MRR-анализа важно "заполнить" все пропущенные месяцы, когда клиент не был активен. Вместо того, чтобы использовать простое распределение даты для разворачивания периода подписки, мы напишем дополнительный SQL, чтобы сначала найти все месяцы между первой и последней активностью клиента, и использовать их в качестве начальной и конечной дат.
with customers as (
select
customer_id,
date_trunc('month', min(start_date)) as date_month_start,
date_trunc('month', max(end_date)) as date_month_end
from subscription_periods
group by 1
),
customer_months as (
select
customers.customer_id,
months.date_month
from customers
inner join months
-- all months after start date
on months.date_month >= customers.date_month_start
-- and before end date
and months.date_month < customers.date_month_end
)
-- join the customer_months spine to MRR base model
joined as (
select
customer_months.date_month,
customer_months.customer_id,
coalesce(subscription_periods.monthly_amount, 0) as mrr
from customer_months
left join subscription_periods
on customer_months.customer_id = subscription_periods.customer_id
-- month is after a subscription start date
and customer_months.date_month >= subscription_periods.start_date
-- month is before a subscription end date
and customer_months.date_month < subscription_periods.end_date
)
...
Шаг 3: Определить первый и последний месяцы активности клиента
Требуемая SQL-техника: Оконные функции
Теперь, располагая всеми месяцами по клиенту, можно начать создавать некоторые поля, которые помогут нам классифицировать изменения. В частности, мы собираемся выяснить, какая запись является первым или последним месяцем для клиента.
...
final as (
select
date_month,
customer_id,
mrr,
mrr > 0 as is_active,
-- calculate first and last months
min(case when is_active then date_month end) over (
partition by account_id
) as first_active_month,
max(case when is_active then date_month end) over (
partition by account_id
) as last_active_month,
-- calculate if this record is the first or last month
first_active_month = date_month as is_first_month,
last_active_month = date_month as is_last_month
from joined
)
select * from final
Шаг 4: Создайте месяц "оттока"
Последняя подписка нашего клиента закончилась в июле, следовательно, в августе он должен получить отметку об оттоке. Однако у нас нет записи для августа в нашем распределении дат! Поэтому давайте создадим ее.
Для этого выберите последнюю активную запись для каждого клиента и установите значение месяца на один вперед. Заполните остальные столбцы, чтобы обозначить, что этот месяц не был активным.
select
dateadd(month, 1, date_month)::date as date_month,
customer_id,
0::float as mrr,
false as is_active,
first_active_month,
last_active_month,
false as is_first_month,
false as is_last_month
from mrr
where is_last_month
Затем, далее, объедините результаты этого запроса с другими записями, чтобы в итоге мы получили все месяцы для клиента.
with unioned as (
select * from {{ ref('mrr_xf') }}
union all
select * from {{ ref('mrr_last_churn') }}
)
...
Шаг 5: Используйте оконные функции для определения MRR за предыдущий месяц
Помните, что наша категория изменений относится к прошлому месяцу, поэтому мы должны использовать какие-то функции lag, чтобы выяснить, сколько клиент заплатил в прошлом месяце, а также относительное изменение курса доллара.
select
*,
coalesce(
lag(is_active) over (partition by customer_id order by date_month),
false
) as previous_month_is_active,
coalesce(
lag(mrr) over (partition by customer_id order by date_month),
0
) as previous_month_mrr,
mrr - previous_month_mrr as mrr_change
from unioned
Шаг 6: Добавьте категорию изменения MRR
Теперь у нас есть все необходимое для категоризации изменений! И как оказалось, SQL в итоге оказался достаточно простым и легким.
...
case
when is_first_month
then 'new'
when not(is_active) and previous_month_is_active
then 'churn'
when is_active and not(previous_month_is_active)
then 'reactivation'
when mrr_change > 0 then 'upgrade'
when mrr_change < 0 then 'downgrade' end as change_category
Шаг 7: Напишите тесты данных и документацию
Мы поместили это здесь в качестве заключительного шага, однако данный процесс должен быть непрерывным, пока вы разрабатываете свои модели. Всякий раз, когда вы делаете предположения о своих данных, добавьте тест, чтобы убедиться, что оно верно (как мы сделали для наших исходных данных). Нам также нравится добавлять тесты на уникальность, и утверждать, какие столбцы не должны быть нулевыми.
Данная модель должна работать как минимум со следующими тестами: Тесты на исходных данных для проверки ваших предположений, Тесты на конечной модели, чтобы убедиться, что вы не допустили ошибок в SQL, в частности: Проверьте ваш первичный ключ, используя тест unique
и not_null
(это поможет выявить фанауты!). Включите тесты not_null
для других столбцов. Кроме того, со временем вы, вероятно, добавите тесты для выявления edge кейсов, которые нельзя предугадать прямо сейчас.
Наконец, добавьте документацию, чтобы ваши стейкхолдеры понимали каждый столбец.
Шаг 8: Запускайте!
Ваша первая версия не будет идеальной. Как только вы получите работающий MVP, закоммитите его в свой проект dbt и создайте несколько дашбордов, чтобы ваши стейкхолдеры смогли его получить.
Проведите пользовательское тестирование со стейкхолдерами, чтобы найти все возможные проблемы и понять, достаточно ли они важны для исправления, прежде чем предоставлять дашборд широкой аудитории. Не дожидайтесь, пока ваша работа станет идеальной, прежде чем поделиться ею.
Создание собственного продукта
Хотя методология моделирования выручки от подписки одинакова для всех бизнесов с регулярным доходом, нюансы работы вашего предприятия могут изменить некоторые детали того, как вы это реализуете.
Как ваш бизнес относится к цифрам, которые не полностью совпадают с финансовыми отчетами?
Позволяет ли ваш бизнес пользователям приостанавливать подписку? Должно ли это отслеживаться иначе, чем отток?
Какова ваша базовая единица измерения (т.е. клиент, аккаунт, продукт)?
Существуют ли дополнительные расходы, которые полезно добавить в качестве атрибута в эту таблицу?
Как следует обрабатывать неполные месяцы?
Все ли подписки оплачиваются в один и тот же день, или они оплачиваются в разные дни для каждого клиента? Если последнее, то каково бизнес-правило для месяца, в котором признается доход?
Это может показаться быстрым решением, но основная часть работы будет проделана именно здесь. Многие из перечисленных примеров требуют компромиссов - помните, что MRR-дашборд должен помогать бизнес-пользователям в оценке состояния компании, а не использоваться для финансовой сверки. Хотя идеальная сверка может выглядеть как хорошая цель, но в итоге вы можете попасть в черную дыру, пытаясь выяснить, куда делись $0,02.
Мы рекомендуем вам пообщаться со стейкхолдерами, чтобы согласовать, для чего нужно и не следует использовать MRR-дашборд, а также убедиться, что вы понимаете, насколько ваш бизнес допускает незначительные несоответствия в цифрах.
Посмотрите код
Мы создали проект dbt с некоторыми примерами данных, чтобы увидеть его в действии:
Сегодня состоится открытое занятие по вопросам оптимизации производительности. На уроке обсудим:
— Какие проблемы с производительностью будоражат хранителей DWH;
— Существующие подходы, ключевые принципы и практики оптимизации;
— Как легко положить базу данных (конечно же, для того, чтобы так не делать).
Регистрация на урок доступна по ссылке.