Как обсчитать RFM-анализ за 5 шагов

RFM-анализ — это метод сегментации клиентов, основанный на их покупательской активности. С помощью RFM-анализа можно, во-первых, оценить доли клиентов в каждом из сегментов. Во-вторых, вспоминая постулат, что клиента легче удержать, чем привлечь. К пользователям, попавшими в разные сегменты, можно применять различные стратегии удержания / поощрения.

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

  • Recency (давность) — как давно клиент покупал

  • Frequency (частота) — как часто он покупает

  • Monetary (деньги) — какую сумму тратит

В каждом измерении выделяют обычно три ранга. Условно это градации «Хорошо», «Нормально», «Плохо». Ранги обычно кодируются как 1, 2, 3. Причем, как-то нет общепринятой договоренности, что кодирует «Хорошо» — 1 или 3. Поэтому лучше приводить расшифровку, как именно вы закодировали ранги.

Итого получается три измерения по три ранга или 27 сочетаний (сегментов) пользователей: от постоянно тратящих большие суммы, причем последний раз вот совсем недавно, до сделавших давным-давно один мааааленький платеж.

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

Общий алгоритм обсчета

1 Построить из исходного датасета таблицу пользователей, определив для каждого количество транзакций, общую сумму платежей, дату первой и последней операции.

2 Вычислить дополнительные показатели.

3 Определить границы рангов и присвоить их пользователям.

4 Построить RFM-таблицу, сгруппировав данные по рангам.

5 На основе полученной RFM-таблицы построить хитмап по определенному показателю

Обсчет RFM-анализа на примере Python

Допустим у нас есть датасет транзакций платежей пользователей за некоторый период. Минимальный набор данных для RFM-анализа: id транзакций, дата транзакции, id пользователя и сумма транзакции.

id

date

user_id

donate

332345

2023–01–01

406173

1000.00

332331

2023–01–01

350353

300.00

332333

2023–01–01

419745

500.00

332337

2023–01–01

343691

300.00

332339

2023–01–01

343285

1000.00

Шаг 1. Формируем таблицу клиентов

Далаем groupby по пользователям c агрегациями: количество транзакций, общая сумма транзакций, первая и последняя дата операций:

user_table = tranz.groupby('user_id').agg({
                  'id' : 'count', 
                  'donate' : 'sum'},
                  'date' : ['min', 'max']) 
user_table.columns = ('tr_count', 'donate_sum', 'first_date', 'last_date')

user_id

tr_count

donate_sum

first_date

last_date

343905

4

5280.00

2023–07–28

2024–10–23

343915

5

1500.00

2024–03–15

2024–11–06

343919

4

204000.00

2023–10–17

2024–06–11

343923

2

3200.00

2023–05–16

2023–05–16

343941

2

8710.00

2023–03–09

2023–12–29

Шаг 2. Вычисляем дополнительные показатели

В дальнейшем нам потребуется дополнительные показатели, которые можно вычислить из только что сагрегированных метрик. Например, для измерения F надо вычислить частоту — это количество операций в единицу времени жизни клиента (день / неделя / месяц). Для этого количество транзакций у нас уже есть, но надо вычислить время жизни — это разница между первой и последней операцией.

  user_table['day_on'] = user_table['last_date'] - user_table['first_date']
  user_table['day_on'] = user_table['day_on'].dt.days + 1
  user_table['oper_frec'] = user_table['tr_count'] / user_table['month_on']

user_id

tr_count

donate_sum

first_date

last_date

day_on

oper_frec

343905

4

5280.00

2023–07–28

2024–10–23

454

0.25

343915

5

1500.00

2024–03–15

2024–11–06

237

0.62

343919

4

204000.00

2023–10–17

2024–06–11

239

0.50

343923

2

3200.00

2023–05–16

2023–05–16

1

2.00

343941

2

8710.00

2023–03–09

2023–12–29

296

0.20

Шаг 3. Определение границ рангов

Это самый нетривиальный этап расчета, потому что разделить измерения RFM на ранги можно различными способами исходя из различных задач и вводных. Самый очевидный — разделить, так чтобы в каждый ранг попало примерно одинаковое количество пользователей. Проще всего это сделать через процентили 0.33 и 0.66 соответственно. Однако, стоит учесть, что очевидные границы на 0.33 и 0.66 процентилях не всегда удачно делят пользователей на три сопоставимые части. А в некоторых случаях, границы стоит выбирать в абсолютных значениях частоты, долготы или денег, исходя из поставленной задачи, практики или логики бизнес-процессов.

В Python такое деление можно сделать с помощью функций pd.cut или pd.qcut

labels = ['1', '2', '3']
user_table['R1'] = pd.cut(user_table['day_last'], bins=[-1, 31, 90, 1000], labels=labels)
user_table['F1'] = pd.cut(user_table['oper_frec'], bins=[-1, 0.9, 2, 1000], labels=labels)
user_table['M1'] = pd.qcut(user_table['oper_sum'], q=[0, .33, .66, 1.], labels=labels)

Также можно использовать конструкции с lambda x или lambda row. В этом случае можно задавать условия деление на когорты с учетом значений в других колонках. Например, ниже «разовые» клиенты определены по одному дню жизни, пусть даже они сделали 10 платежей в этот день.

user_table_rfm['R'] = user_table_rfm['day_last']\
      .apply(lambda x: '1' if x < 45 else ('2' if x < 180 else '3'))
user_table_rfm['F'] = user_table_rfm\
      .apply(lambda row: '3' if row['day_on'] == 1 else ('2' if row['oper_frec'] < 0.8 else '1'), axis=1)
user_table_rfm['M'] = user_table_rfm['donate_sum']\
      .apply(lambda x: '1' if x > 2000 else ('2' if x > 750 else '3'))

user_id

tr_count

donate_sum

first_date

last_date

day_on

oper_frec

R

F

M

343905

4

5280.00

2023–07–28

2024–10–23

454

0.25

2

2

1

343915

5

1500.00

2024–03–15

2024–11–06

237

0.62

2

2

2

343919

4

204000.00

2023–10–17

2024–06–11

239

0.50

3

2

1

343923

2

3200.00

2023–05–16

2023–05–16

1

2.00

3

1

1

343941

2

8710.00

2023–03–09

2023–12–29

296

0.20

3

2

1

Шаг 4. Строим RFM таблицу

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

rfm_table = user_table_rfm.groupby(['R', 'F', 'M'], as_index = False)\
    .agg({'tr_count' : ['count', 'sum'], 
          'donate_sum' : 'sum'})
rfm_table.columns = ['R', 'F', 'M', 'RFM', 'rfm_users', 'rfm_tr', 'rfm_sum']

Получаем таблицу, в которой максимум на 27 строк — комбинация 3 измерений по 3 ранга. Может быть и меньше, если в какое сочетание RFM не попало ни одного пользователя.

R

F

M

rfm_users

rfm_tr

rfm_sum

1

1

1

368

7694

8860328.26

1

1

2

20

158

27231.00

1

1

3

16

70

5944.00

1

2

1

152

1112

2814391.00

1

2

2

43

182

64151.00

Шаг 5. Выводим RFM таблицу в виде heatmap

Для вывода красивой RFM таблицы в виде тепловой карты надо еще провернуть один «фокус»: так как таблица плоская, а измерений у нас три, то надо комбинацию двух измерений разместить на одной оси, а третье измерение на другой оси. Например, R И F по вертикали, а M горизонтали. Делается это через сводную таблицу pivot. В итоге должна получится матрица 9×3, в которой в индексах ранги R И F, в колонках ранги M, а в ячейках значения нужного показателя — количество пользователей, количество транзакций или сумма платежей. Эту сводную таблицу уже и выводим в виде heatmap.

fig = plt.figure(figsize = (12, 6))
rfm_pivot = rfm_table.pivot(index = ['R', 'F'], columns = 'M', values = 'rfm_sum').fillna(0)
sns.heatmap(rfm_pivot, cmap='RdYlGn', annot=True, cbar=False, fmt=',.0f')
plt.title(f'Тепловая карта RFM анализа по параметру Сумма платежей')
plt.show()

В итоге получится такая тепловая карта. Останется чуть-чуть поколдовать с метками осей, чтобы на хитмапе сразу было видно, что ранг 1 — это «Хорошо».

2d5bad2cdc3b9526fa7aa894b5cac31f.png

Заключение

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

Habrahabr.ru прочитано 7786 раз