Привет! Продолжем изучать Pandas. В прошлый раз я рассказал о возможностях библиотеки, а сегодня покажу, как с её помощью делать когортный анализ.

Прежде всего, вспомним, что такое когорты и как их анализировать.

Когорта — это группа людей, которая совершила нужное действие в определенный промежуток времени.

Когортный анализ — это наблюдение за когортами. Выбираем одну или несколько метрик, измеряем их и делаем выводы.


Например, социологи могут отслеживать, сколько людей, родившихся в 1980 году, получили высшее образование. Когорта здесь — те, кто родились в 1980 году. Метрика — доля людей с высшим образованием.

Еще пример: маркетологи хотят узнать, сколько заказов и выручки принесли пользователи, совершившие свой первый заказ год назад. Теперь когорта — это прошлогодние покупатели, а метрики — количество заказов и выручка.

Получается, когортный анализ состоит из трех шагов: определения нужного действия и временного промежутка когорты и выбора метрик для отслеживания:

Действие Время Метрика
Родились В 1980 году % людей с высшим образованием
Впервые что-то купили Год назад Количество заказов и выручка
Установили приложение Неделю назад % пользователей, открывших приложение еще раз

Когорты можно сравнивать между собой. Например, маркетологи измеряют, сколько заказов обычно делают пользователи в течение месяца после первой покупки и смотрят динамику:

Месяц Клиенты Покупок в 1й месяц Покупок на клиента
Январь 2018 134 161 1.20
Февраль 2018 164 194 1.18
Март 2018 193 200 1.03

Общее количество клиентов и покупок выросло — приятно. Но, сравнив когорты, видим, что в среднем клиенты стали покупать реже — тревожный знак.

Ок, теперь мы готовы к тому, чтобы научиться делать когортный анализ с помощью Pandas. Для наглядности решим задачу.

Задача

Допустим, мы работаем в интернет-магазине и хотим понять, сколько заказов и денег клиенты приносят в течение года после первой покупки. Для этого у нас есть данные о заказах:

>>> orders.head()
order_date ship_mode customer_id sales
id
100006 2014-09-07 Standard DK-13375 377.970
100090 2014-07-08 Standard EB-13705 699.192
100293 2014-03-14 Standard NF-18475 91.056
100328 2014-01-28 Standard JC-15340 3.928
100363 2014-04-08 Standard JM-15655 21.376
view raw load_orders.py hosted with ❤ by GitHub

Каждая строка таблицы orders — это покупка. Мы знаем, когда она произошла, кто её сделал и сколько денег она принесла в магазин. Дата заказа лежит в поле order_date, номер покупателя — в customer_id, а выручка — в sales.

Часто бывает, что даты загружаются в виде текста. Преобразим колонку order_date из текста в дату:

>>> orders['order_date'] = pd.to_datetime(orders['order_date'])
view raw order_date.py hosted with ❤ by GitHub

Данные перед нами, теперь можно с ними работать. Начнем с простого: выясним, сколько всего в магазине было покупок и выручки.

Считаем покупки и выручку

Чтобы посчитать общую выручку, просуммируем колонку sales:

>>> orders['sales'].sum()
2297200.8603000003
view raw orders_sum.py hosted with ❤ by GitHub

Количество заказов можно посчитать с помощью этой же колонки, но вместо суммы используем метод count():

>>> orders['sales'].count()
5009
view raw orders_count.py hosted with ❤ by GitHub

Теперь посчитаем обе метрики для каждого пользователя. Сгруппируем датафрейм по полю customer_id:

>>> orders.groupby('customer_id')['sales'].agg(['sum', 'count'])
sum count
customer_id
AA-10315 5563.5600 5
AA-10375 1056.3900 9
AA-10480 1790.5120 4
AA-10645 5086.9350 6
AB-10015 886.1560 3
AB-10060 7755.6200 8
AB-10105 14473.5710 10
...

Видим, например, что пользователь AA-10315 сделал 5 заказов и принес $5563 выручки.

Идём дальше. Чтобы построить когорты, нам нужно сгруппировать клиентов по дате их первой покупки. В данных нет такого поля, значит, нужно его посчитать.

Считаем дату первой покупки

Чтобы вычислить дату первой покупки каждого пользователя, сгруппируем данные по customer_id и найдем минимальное значение поля order_date. Результат сохраним в переменную first_orders:

>>> first_orders = orders.groupby('customer_id')['order_date'].agg({'first_order': 'min'})
>>> first_orders.head()
first_order
customer_id
AA-10315 2014-03-31
AA-10375 2014-04-21
AA-10480 2014-05-04
AA-10645 2014-06-22
AB-10015 2014-02-18
view raw first_orders.py hosted with ❤ by GitHub

Видим, что пользовать AA-10315 впервые что-то купил 31 марта 2014 года, а пользователь AA-10375 — 21 апреля того же года.

Зная даты первых покупок, можем строить когорты.

Строим когорты

Итак, когортами будем считать людей, сделавших первую покупку в тот или иной день. Метрики для отслеживания — количество заказов и выручка.

План такой: сначала добавим дату первой покупки пользователей в таблицу с заказами, затем сгруппируем по датам первой покупки и заказа и, наконец, посчитаем выручку и количество заказов каждой когорты.

Приступим. Добавим дату первой покупки с помощью метода merge() и сохраним получившийся датафрейм в переменную orders_merged:

>>> orders_merged = orders.merge(first_orders, how='inner', left_on='customer_id', right_index=True)
>>> orders_merged.head()
order_date ship_mode customer_id sales first_order
id
100006 2014-09-07 Standard DK-13375 377.970 2014-09-07
131884 2015-12-06 Same Day DK-13375 594.002 2014-09-07
145065 2015-12-12 First DK-13375 32.308 2014-09-07
133046 2017-07-27 Second DK-13375 297.990 2014-09-07
165099 2017-12-11 First DK-13375 1.392 2014-09-07

В строках получившегося датафрейма всё еще покупки, но теперь в таблице появилась новая колонка: дата первой покупки пользователя.

Агрегируем по дате первой покупки и посчитаем нужные показатели:

>>> orders_merged.groupby('first_order')['sales'].agg(['sum', 'count'])
sum count
first_order
2014-01-03 1050.6360 9
2014-01-04 1056.8580 5
2014-01-05 1428.2310 7
2014-01-06 14287.5570 22
2014-01-07 1055.9810 4
2014-01-09 370.3480 4
2014-01-10 4867.3440 7
...

Видим, что клиенты от 3 января 2014 года, всего сделали 9 заказов на $1050.6. Посмотрим, когда были эти заказы. Для этого добавим к группировке колонку order_date:

>>> orders_merged.groupby(['first_order','order_date'])['sales'].agg(['sum', 'count'])
sum count
first_order order_date
2014-01-03 2014-01-03 16.448 1
2014-11-12 153.112 1
2015-04-18 209.550 1
2015-11-24 383.610 1
2016-05-15 7.764 1
2016-11-14 37.630 1
2017-03-04 89.568 1
2017-05-14 87.994 1
2017-08-26 64.960 1
2014-01-04 2014-01-04 288.060 1
2015-09-25 183.400 1
2015-11-20 344.372 1
2015-11-29 4.304 1
2017-10-05 236.722 1

Ага, первый заказ этой когорты был 3 января на $16. В следующий раз клиент вернулся почти год спустя и купил что-то ещё, в этот раз на $153. Следующая покупка была уже в апреле 2015 и так далее.

Когорты готовы, теперь решим задачу.

Решаем задачу

Напомню, что мы хотим посчитать, сколько в среднем заказов и выручки приносят клиенты в течение года, после первой покупки.

Мы знаем, сколько магазин заработал с каждой когорты за всё время. Уточним метрику: посчитаем показатели за первый год жизни когорты.

Сначала узнаем, сколько дней прошло между первой покупкой и последующим заказом, и удалим те, которые случились позже 365 дней. Чтобы посчитать количество дней между заказами, вычтем из колонки order_date столбец first_order:

>>> orders_merged['order_date'] - orders_merged['first_order']
id
100006 0 days
131884 455 days
145065 461 days
133046 1054 days
165099 1191 days
166835 1162 days
111290 684 days
139087 679 days
...
view raw date_dif.py hosted with ❤ by GitHub

Вуаля. Видим, что, например, заказ 131884 случился 455 дней спустя первой покупки. 455 days — это тип данных под названием «Timedelta», его специально придумали, чтобы показывать временные промежутки.

Чтобы удалить поздние заказы, добавим условие <= '365 days':

>>> orders_merged['order_date'] - orders_merged['first_order'] <= '365 days'
id
100006 True
131884 False
145065 False
133046 False
165099 False
166835 False
111290 False
139087 False
...

Сохраним результат в переменную year_1_filter, отфильтруем ненужные заказы из когортного отчета и сохраним результат в переменную year_1_orders:

>>> year_1_filter = orders_merged['order_date'] - orders_merged['first_order'] <= '365 days'
>>> year_1_orders = orders_merged[year_1_filter]
>>> year_1_orders.head()
order_date ship_date ship_mode customer_id sales first_order
id
CA-2014-100006 2014-09-07 2014-09-13 Standard Class DK-13375 377.970 2014-09-07
CA-2014-100090 2014-07-08 2014-07-12 Standard Class EB-13705 699.192 2014-07-08
CA-2014-129938 2014-12-15 2014-12-17 Second Class EB-13705 445.802 2014-07-08
CA-2015-128125 2015-03-31 2015-04-05 Standard Class EB-13705 120.756 2014-07-08
CA-2014-100293 2014-03-14 2014-03-18 Standard Class NF-18475 91.056 2014-03-14

В датафрейме остались только заказы, сделанные когортами в первый год после первой покупки. Теперь сгруппируем заказы по дате первой покупки и посчитаем нужные метрики. Результат сохраним в переменную cohorts:

>>> cohorts = year_1_orders.groupby(['first_order','order_date'])['sales'].agg(['sum', 'count'])
>>> cohorts.head()
sum count
first_order order_date
2014-01-03 2014-01-03 16.448 1
2014-11-12 153.112 1
2014-01-04 2014-01-04 288.060 1
2014-01-05 2014-01-05 19.536 1
2014-01-06 2014-01-06 4407.100 3
view raw cohorts.py hosted with ❤ by GitHub

Последний шаг: посчитаем, сколько в среднем заказов и приносят клиенты в течение первого года. Для этого сначала просуммируем показатели каждой когорты, а затем усредним значения методом mean():

>>> cohorts.groupby('first_order').sum().mean()
sum 1949.850803
count 3.988789

Готово! В среднем за первый год когорты делают по 4 заказа и приносят по $1949 долларов.

Есть много способов улучшить решение, например сгруппировать дневные когорты в недельные или месячные, визуализировать отчет в таблице или на графике. Наконец, интересно разбить когорты по каким-то признакам, например, отделить частных покупателей от компаний — наверняка их показатели существенно отличаются.

Обо всем этом в следующих сериях. Подписывайтесь на канал, чтобы не пропустить.

Адиос!