A segmentação de clientes pode ser realizada usando diversas características do cliente. As mais comuns são região geográfica, dados demográficos (por exemplo, idade, sexo, estado civil, renda), psicografia (por exemplo, valores, interesses, estilo de vida, afiliações de grupos) e comportamento de compra (por exemplo, compras anteriores, preferências de envio, visualizações de página em seu site etc.).
A análise RFM (Recency, Frequency & Monetary) é uma técnica baseada no perfil e hábitos de compras do consumidor com o objectivo de segmentar os clientes avaliando o histórico de suas transações, tais como:
a) há quanto tempo o cliente efectuou uma compra ou qual foi a última vez que o cliente fez uma compra?
b) com que frequência faz as compras?
c) quanto o cliente gasta (valor monetário)?
Este conceito é baseado no axioma de marketing de que 80% da sua receita provém de 20% de seus clientes. Assim, RFM ajuda a identificar clientes com maior probabilidade de responder à promoções segmentando-os em várias categórias.
No presente trabalho foi usado o seguinte dataset , https://www.coursera.org/learn/foundations-marketing-analytics/supplement/M8mWk/r-files-and-dataset, extraido do curso Foundations of marketing analytics by ESSEC Business School para efeitos de mera demonstração. Trata-se de um ficheiro contendo registo de compras efectuadas por clientes entre os anos de 2005 e 2015. Não está claro se os dados são verdadeiros ou simulados. Porém, encorajo o uso da técnica RFM para dados em produção.
Pretendo com este Post partilhar a minha experiência na aplicação dessa técnica na industria de telecom e por razões óbvias não vou compartilhar convosco os dados em produção mas os procedimentos que os possa conduzir para à reprodução em vosso ambiente de produção.
Estavamos num contexto onde não conheciamos o nosso cliente e nem sabíamos se um determinado cliente era relevante ou não para a nossa organização. Em outras palavras, procuravamos responder as seguintes questões:
a) quem são os nossos clientes mais valioso?
b) como podemos adquirir novos clientes que se assemelham aos nossos (clientes) mais valiosos.
Portanto, o nosso objectivo principal visava analisar o comportamento e hábitos de consumo dos clientes para classifica-los e segmenta-los em diferentes grupos. E como objectivo específicos:
1. identificar quais são nossos clientes mais/menos valiosos;
2. identificar os clientes que podem contribuir para a taxa de desistência ou abandono (churn rate);
3. identificar os potenciais clientes valiosos;
4. identificar os clientes que podem ser retidos;
5. saber quanto valor um cliente está a gerar para a organização;
6. saber como o valor gerado vai evoluir;
7. segmentar os clientes com base no seu valor;e
8. monitorar a migração ou a movimentação dos clientes entre os segmentos definidos.
Isso porque, pretendiamos executar acções específicas para diferentes grupos de clientes. Então, precisavamos de encontrar uma maneira de segmentar a nossa base de clientes em grupos com base em seu valor para o negócio.
Para alcançar os objectivos propostos usamos os dados históricos (transações) sobre os recarregamentos (compra de recargas que o habilita a realizar uma chamada ou operação) efectuados pelos clientes pré-pagos nos últimos 02 (dois) anos. As ferramentas usadas para análise de dados foram o R e o IDE RStudio.
Normalmente, a análise RFM obdece os seguintes passos:
1. Coleccionar os dados transacionais;
2. Agregar os dados por cliente;
3. Calcular os RFM scores;
4. Definir a segmentação dos clientes.
É sempre recomendável o uso de Análise Descritiva de Dados para se familiazar e obter uma melhor compreensão de dados antes de avançar com qualquer que seja à análise.
Portanto, vamos proceder com o carregamento e leitura dos dados em R:
# __________________________________________________________
# //////////////////////////////////////////////////////////
#
# Customer Segmentation using RFM score
# __________________________________________________________
# //////////////////////////////////////////////////////////
# 1.0 --- EXPLORE THE DATA -------------------------------------
library(tidyverse)
## Registered S3 methods overwritten by 'ggplot2':
## method from
## [.quosures rlang
## c.quosures rlang
## print.quosures rlang
## -- Attaching packages --------------------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.1 v purrr 0.3.2
## v tibble 2.1.2 v dplyr 0.8.1
## v tidyr 0.8.3 v stringr 1.4.0
## v readr 1.3.1 v forcats 0.4.0
## -- Conflicts ------------------------------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(pander) # for aesthetics
## Warning: package 'pander' was built under R version 3.6.2
library(highcharter)
## Warning: package 'highcharter' was built under R version 3.6.1
## Registered S3 method overwritten by 'xts':
## method from
## as.zoo.xts zoo
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
## Highcharts (www.highcharts.com) is a Highsoft software product which is
## not free for commercial and Governmental use
# Load text file into local variable called 'data'
transaction_dat <-
read.delim(
file = 'data/purchases_transactions.txt',
header = FALSE,
sep = '\t',
dec = '.'
)
# Display what has been loaded
# transaction_dat %>% glimpse()
pander(head(transaction_dat))
| V1 | V2 | V3 |
|---|---|---|
| 760 | 25 | 2009-11-06 |
| 860 | 50 | 2012-09-28 |
| 1200 | 100 | 2005-10-25 |
| 1420 | 50 | 2009-07-09 |
| 1940 | 70 | 2013-01-25 |
| 1960 | 40 | 2013-10-29 |
Vamos adicionar o cabeçalho e tomamos o dia de 01/01/2016 como sendo a data de referência para a análise.
# Add headers and interpret the last column as a date, extract year of purchase
colnames(transaction_dat) <-
c('customer_id', 'purchase_amount', 'date_of_purchase')
# convert to date
transaction_dat$date_of_purchase <-
as.Date(transaction_dat$date_of_purchase, "%Y-%m-%d")
# date_of_analysis
date_of_analysis <- as.Date('2016-01-01')
transaction_dat$days_since <- as.numeric(
difftime(
time1 = date_of_analysis,
time2 = transaction_dat$date_of_purchase,
units = "days"
)
)
# Display the data set after transformation
head(transaction_dat) %>% pander()
| customer_id | purchase_amount | date_of_purchase | days_since |
|---|---|---|---|
| 760 | 25 | 2009-11-06 | 2247 |
| 860 | 50 | 2012-09-28 | 1190 |
| 1200 | 100 | 2005-10-25 | 3720 |
| 1420 | 50 | 2009-07-09 | 2367 |
| 1940 | 70 | 2013-01-25 | 1071 |
| 1960 | 40 | 2013-10-29 | 794 |
Vamos usar o código SQL para agregar os dados por clientes.
# 1.1 Compute key marketing indicators using SQL language -----------------------------
library(sqldf)
## Warning: package 'sqldf' was built under R version 3.6.2
## Loading required package: gsubfn
## Warning: package 'gsubfn' was built under R version 3.6.2
## Loading required package: proto
## Warning: package 'proto' was built under R version 3.6.2
## Loading required package: RSQLite
## Warning: package 'RSQLite' was built under R version 3.6.2
# Compute recency, frequency, and average purchase amount
customers_dat = sqldf(
"SELECT customer_id,
MIN(days_since) AS 'recency',
MAX (days_since) AS first_purchase,
COUNT(*) AS 'frequency',
SUM(purchase_amount) AS 'monetary'
FROM transaction_dat
GROUP BY 1"
)
head(customers_dat) %>% pander()
| customer_id | recency | first_purchase | frequency | monetary |
|---|---|---|---|---|
| 10 | 3829 | 3829 | 1 | 30 |
| 80 | 343 | 3751 | 7 | 500 |
| 90 | 758 | 3783 | 10 | 1158 |
| 120 | 1401 | 1401 | 1 | 20 |
| 130 | 2970 | 3710 | 2 | 100 |
| 160 | 2963 | 3577 | 2 | 60 |
Sumarizando os dados temos o seguinte resultado:
summary(customers_dat) %>% pander()
| customer_id | recency | first_purchase | frequency | monetary |
|---|---|---|---|---|
| Min. : 10 | Min. : 1 | Min. : 1 | Min. : 1.000 | Min. : 5.0 |
| 1st Qu.: 81990 | 1st Qu.: 244 | 1st Qu.: 988 | 1st Qu.: 1.000 | 1st Qu.: 30.0 |
| Median :136430 | Median :1070 | Median :2087 | Median : 2.000 | Median : 60.0 |
| Mean :137574 | Mean :1253 | Mean :1984 | Mean : 2.782 | Mean : 173.4 |
| 3rd Qu.:195100 | 3rd Qu.:2130 | 3rd Qu.:2992 | 3rd Qu.: 3.000 | 3rd Qu.: 150.0 |
| Max. :264200 | Max. :4014 | Max. :4016 | Max. :45.000 | Max. :24350.0 |
Para avaliar a distribuição dos parâmetros RFM podemos usar o histograma.
hist(customers_dat$recency)
hist(customers_dat$frequency)
hist(customers_dat$monetary)
hist(customers_dat$monetary, breaks = 50)
Para determinar os parâmetros RFM usa-se os quartis ou percentis. Nós vamos dividir o conjunto de dados em \(05\) partes iguais, ou seja divisão em \(20\)% cada parte. A ideia é encontrar os Top \(20\)% clientes mais valiosos fazendo jus a lei de pareto 80/20.
quantile(customers_dat$monetary, probs = seq(0, 1, 0.20)) %>% pander()
| 0% | 20% | 40% | 60% | 80% | 100% |
|---|---|---|---|---|---|
| 5 | 30 | 45 | 85 | 190 | 24350 |
O output mostra o seguinte:
- os clientes Top20 gastam em média mais de \(190.00\) u.m (unidade monetária);
- os \(20\)% seguintes gastam entre \(85.00\) e \(190.00\) u.m, enquanto que os últimos \(20\)% dos clientes gastam menos de \(30.00\) u.m em seu consumo médio.
quantile(customers_dat$frequency, probs = seq(0, 1, 0.20)) %>% pander()
| 0% | 20% | 40% | 60% | 80% | 100% |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 2 | 4 | 45 |
Quanto à frequência com que fazem as compras, podemos observar que:
- os clientes Top20 compraram mais de \(4\) vezes ao longo dos 10 anos do periódo em análise;
- os seguintes \(20\)% dos clientes compraram entre \(2\) e \(4\) vezes;
- os últimos \(20\)% dos clientes compraram \(01\) no periódo em análise.
quantile(customers_dat$recency, probs = seq(0, 1, 0.20)) %>% pander()
| 0% | 20% | 40% | 60% | 80% | 100% |
|---|---|---|---|---|---|
| 1 | 108 | 731 | 1399 | 2373 | 4014 |
E finalmente analisamos a actividade mais recente para cada cliente. Para esse parâmetro em particular recency, a ordem de classificação é inversa, isto é Top20 clientes são os primeiros \(20\)% do percentil. Portanto, o output mostra que:
- os Top20 clientes compraram nos últimos \(108\) dias;
- os próximos \(20\)% de clientes compraram no intervalo entre \(108\) e \(731\) dias;
- ao passo que os últimos \(20\)% de clientes ficam mais de \(2373\) dias sem efectuar nenhum compra, isto é a sua última compra foi há aproximadamnete \(6.5\) anos atrás.
Para determinar os RFM score, vamos usar o percentil \(20\) (P20), isto é dividir a série de dados em \(05\) partes iguais. Em outras palavras, isso significa que os Top20 ficam com pontuação máxima de \(05\) pontos, enquanto que os últimos \(20\)% ficam com a pontuação mínima de \(01\) ponto. Caso particular, vai para o parâmetro R (recency), onde a ordem deve ser invertida, isto é os valores baixos levam pontuação máxima (\(05\) pontos) e os valores altos pontuação minima (\(01\) ponto).
Portanto, o melhor cliente tem a pontuação máxima de \(555\) (RFM score) e o pior com pontuação mínima de \(111\) (RFM score) pontos.
Em código R pode ser implementado de seguinte modo:
# Calculating RFM score
rfm_data <- customers_dat
# RFM Score
rfm_data <-
rfm_data %>%
mutate(
R = ntile(desc(recency), 5),
F = ntile(frequency, 5),
M = ntile(monetary, 5)
)
rfm_data$RFM <- rfm_data$R * 100 + rfm_data$F * 10 + rfm_data$M
rfm_data %>% head(10) %>% pander()
| customer_id | recency | first_purchase | frequency | monetary | R | F | M | RFM |
|---|---|---|---|---|---|---|---|---|
| 10 | 3829 | 3829 | 1 | 30 | 1 | 1 | 1 | 111 |
| 80 | 343 | 3751 | 7 | 500 | 4 | 5 | 5 | 455 |
| 90 | 758 | 3783 | 10 | 1158 | 3 | 5 | 5 | 355 |
| 120 | 1401 | 1401 | 1 | 20 | 2 | 1 | 1 | 211 |
| 130 | 2970 | 3710 | 2 | 100 | 1 | 3 | 4 | 134 |
| 160 | 2963 | 3577 | 2 | 60 | 1 | 3 | 3 | 133 |
| 190 | 2211 | 3690 | 5 | 340 | 2 | 5 | 5 | 255 |
| 220 | 2058 | 3664 | 2 | 50 | 2 | 3 | 3 | 233 |
| 230 | 3985 | 3985 | 1 | 50 | 1 | 1 | 3 | 113 |
| 240 | 463 | 3815 | 4 | 65 | 4 | 4 | 3 | 443 |
Note que, os clientes com elevado RFM score apresentam valores baixos para variável \(RECENCY\) e a altos para as variáveis \(FREQUENCY\) e \(MONETARY\), por exemplo Customer_id = 80, enquanto que os clientes com baixo RFM score, mostram altos valores para a variável \(RECENCY\) e baixos para \(FREQUENCY\), como por exemplo Customer_id = 10. Sendo que, para clientes com valores RFM score intermédios precisam de uma análise mais cuidadosa. Como será verificado a seguir.
A segmentação dos clientes é feita com base no RFM score. Por isso, precisamos cuidadosamente de definir e analisar os intervalos dos RFM scores. Assegurando a não duplicação dos clientes em vários segmentos, ou seja cada cliente pertence exclusivamente a um único segmento.
Como podem ter constatado essa abordagem gera \(125\) segmentos se considerarmos cada RFM score individualmente como sendo um segmento. O que tornaria o processo muito dificil de analisar.
De um modo geral, o parâmetro \(MONETARY\) é visto como uma métrica agregadora para sumarizar as transações. Assim, dos \(125\) segmentos (RFM score) podem ser reduzidos para \(25\) segmentos apenas usando os parâmetros \(RECENCY\) e \(FREQUENCY\). Ademais, se fizermos a combinação destes \(25\) podemos alcançar segmentos mais manejáveis e intuitivos, reduzindo deste modo para \(6-8\) segmentos.
Está foi a fase mais delicada de todo o processo. Encontrar um critério para definir a segmentação de clientes que faça sentido para o seu negócio. Alguns artigos sugerem a definição dos segmentos com base no bom senso e na natureza do negócio. Não conformado com essa abordagem, tentei encontrar um critério que fosse geral e independente da natureza do negócio. Com ajuda da Tabela 1 abaixo, defini os segmentos usando o seguinte critério:
# Define Customer Segmentation Groups
rfm_data$segment <- NA
rfm_data$segment[which(rfm_data$RFM == 111)] <- 'Lost'
rfm_data$segment[which(rfm_data$RFM > 111)] <- 'Hibernating'
rfm_data$segment[which(rfm_data$RFM >= 222)] <- 'About to Sleep'
rfm_data$segment[which(rfm_data$RFM >= 333)] <-
'Potential Loyalists'
rfm_data$segment[which(rfm_data$RFM >= 444)] <- 'Champions'
# 2nd round
rfm_data$segment[which(rfm_data$segment == 'Potential Loyalists' &
(rfm_data$F >= 4))] <- 'Loyal Customers'
rfm_data$segment[which(rfm_data$segment == 'About to Sleep' &
(rfm_data$M >= 4))] <-
'Need Attention'
rfm_data$segment[which(rfm_data$segment == 'Hibernating' &
(rfm_data$F >= 4 & rfm_data$M >= 4))] <-
'Can not Lose Them'
rfm_data$segment[which(rfm_data$first_purchase <= 180)] <-
'New Customers'
rfm_data[, -c(3, 6,7,8)] %>%
head(10) %>% pander()
| customer_id | recency | frequency | monetary | RFM | segment |
|---|---|---|---|---|---|
| 10 | 3829 | 1 | 30 | 111 | Lost |
| 80 | 343 | 7 | 500 | 455 | Champions |
| 90 | 758 | 10 | 1158 | 355 | Loyal Customers |
| 120 | 1401 | 1 | 20 | 211 | Hibernating |
| 130 | 2970 | 2 | 100 | 134 | Hibernating |
| 160 | 2963 | 2 | 60 | 133 | Hibernating |
| 190 | 2211 | 5 | 340 | 255 | Need Attention |
| 220 | 2058 | 2 | 50 | 233 | About to Sleep |
| 230 | 3985 | 1 | 50 | 113 | Hibernating |
| 240 | 463 | 4 | 65 | 443 | Loyal Customers |
Note que, um cliente não pode pertencer simultâneamente a mais de um segmento, ou seja deve pertencer exclusivamente a um único segmento.
Para assegurar que os critérios definidos abrangem todos os clientes e que nenhum ficou por definir o seu segmento, em código R validamos de seguinte modo:
# validate the segments
rfm_data[is.na(rfm_data$segment), ]
## [1] customer_id recency first_purchase frequency monetary
## [6] R F M RFM segment
## <0 rows> (or 0-length row.names)
# ou
# rfm_data %>%
# filter(is.na(segment))
O resultado mostra que cada cliente pertence a um determinado segmento.
Tabela 1: Segmentação
Precisamos de avaliar a distribuição dos clientes por segmentos. E ordena-los de uma forma que faça sentido.
# re-order segments in factor in a wat that makes sense ----
rfm_data$segment <-
factor(
x = rfm_data$segment,
levels = c(
'Lost',
'Hibernating',
'Can not Lose Them',
'About to Sleep',
'Need Attention',
'New Customers',
'Potential Loyalists',
'Loyal Customers',
'Champions'
)
)
# table of frequency
freqTable <-
rfm_data %>%
# group_by(group) %>%
count(segment) %>%
# arrange(desc(n)) %>%
rename(Segment = segment, Count = n)
freqTable %>% pander()
| Segment | Count |
|---|---|
| Lost | 1522 |
| Hibernating | 3107 |
| Can not Lose Them | 198 |
| About to Sleep | 3423 |
| Need Attention | 1263 |
| New Customers | 1269 |
| Potential Loyalists | 1469 |
| Loyal Customers | 1771 |
| Champions | 4395 |
O output mostra a quantidade de clientes distribuidos pelos segmentos definidos.
Também podemos mostrar os dados agregados por segmentos.
cust_aggr_dat <-
aggregate(x = rfm_data[, 2:5],
by = list(rfm_data$segment),
mean)
cust_aggr_dat %>% pander()
| Group.1 | recency | first_purchase | frequency | monetary |
|---|---|---|---|---|
| Lost | 3007 | 3007 | 1 | 18.35 |
| Hibernating | 2592 | 2685 | 1.244 | 65.57 |
| Can not Lose Them | 2734 | 3482 | 3.722 | 214.9 |
| About to Sleep | 1378 | 1621 | 1.387 | 39.08 |
| Need Attention | 1665 | 2525 | 3.233 | 255.4 |
| New Customers | 55.18 | 56.2 | 1.015 | 71.57 |
| Potential Loyalists | 568.4 | 953.6 | 1.236 | 88.83 |
| Loyal Customers | 837.1 | 2040 | 3.756 | 198.1 |
| Champions | 159.6 | 2072 | 6.037 | 430.4 |
Os resultados mostram que:
- os clientes Champions \((4395)\), considerados os clientes mais valiosos gastam em média o valor de \(430.00\) u.m, efectuaram compras por mais de \(6\) vezes e a última compra foi em média há sensivelmente \(160\) dias;
- enquanto que, os Lost o caso pior estão há mais de \(08\) anos sem efectuar uma compra;
- o destaque vai para o segmento Need Attention, uma vez que apresentam um consumo elevado, contudo a última actividade foi há bastante tempo, ou seja há aproximadamente \(4.5\) anos. Daí que, requer um trabalho árduo para recuperá-los.
Para visualizar os dados, vamos apresentar 02 tipos de gráficos:
- treemap (recomendado); e
- gráficos de barras.
Treemap é o gráfico recomendado para apresentação desse tipo de dados, ou seja dados que incluem variáveis categóricas e númericas.
hctreemap2(
data = freqTable,
group_vars = "Segment",
size_var = "Count",
color_var = "Count"
)
highchart() %>%
hc_add_series(
data = freqTable,
type = 'column',
hcaes(x = Segment, y = Count),
dataLabels = list(align = "center", enabled = TRUE,
style = list(
fontWeight = "bold",
color = "#f7a35c",
textOutline = NULL
)
),
name = 'Segments'
) %>%
hc_xAxis(categories = unique(freqTable$Segment)) %>%
hc_yAxis(title = list(text = "No of customers"))
## Warning: `parse_quosure()` is deprecated as of rlang 0.2.0.
## Please use `parse_quo()` instead.
## This warning is displayed once per session.
No presente post foi avaliado a segmentação dos clientes com base nas suas transacções históricas de compras usando à técnica RFM score. Foram usados dados simulados (presumo) mas a ideia é mostrar como esse processo pode ser implementado em um ambiente de produção.
Os clientes no segmento Champions são considerados os mais valiosos e os About to Sleep e Need Attention precisam de alguma atenção especial para os resgatar.
Na verdade, o critério para a definição dos segmentos com base nos RFM score é um pouco ambíguo e requer alguma arte. Alguns autores sugerem que a definição dos segmentos depende da natureza do negócio, por isso desdobrei-me em encontrar um critério que fosse aplicável para todos casos. Tive sucesso aplicando esse método em produção relacionado com recarregamentos efectuado pelos subescritores nos últimos \(02\) anos e encorajo sem reservas. Portanto, estou ansioso em ler vossos comentários após a adopção desta técnica.