1.0 Introdução

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.

2.0 Contextualização

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.

2.1 Metodologia

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.

3.0 Caso de Estudo

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.

3.1 Análise Descritiva de Dados

É 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)

3.1.1 Determinar Percentil

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.

3.2 Determinar RFM score

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.

3.3 Definir a segmentação dos clientes

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

Tabela 1: Segmentação

3.4 Distribuição dos clientes por segmentos

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.

4.0 Visualização dos dados

Para visualizar os dados, vamos apresentar 02 tipos de gráficos:
- treemap (recomendado); e
- gráficos de barras.

4.1 Apresentação dos dados usando Treemap

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"
    )

4.2 Apresentação dos dados usando gráfico de barras

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.

5.0 Conclusão

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.

6.0 Referências

  1. https://www.r-bloggers.com/customer-segmentation-using-rfm-analysis/
  2. https://www.r-bloggers.com/customer-segmentation-part-1-k-means-clustering/
  3. https://www.putler.com/rfm-analysis/
  4. https://blog.rsquaredacademy.com/customer-segmentation-using-rfm-analysis/
  5. https://earlconf.com/2017/downloads/london/presentations/EARL2017_-_London_-_Alexander_Campbell_-_Customer_segmentation.pdf