Email :
Linkedin : www.linkedin.com/in/gabrielerichson
Github : www.github.com/gabrielerichsonmrp

"Demand Forecasting
# Kustomisasi Warna dan Visualisasi chart
my_color = c(
  col1="#d3f2a3",
  col2="#97e196",
  col3="#6cc08b",
  col4="#4c9b82",
  col5="#217a79",
  col6="#105965",
  col7="#074050"
)

my_theme_fill  <- get_scale_fill(get_pal(my_color))
my_theme_color <- get_scale_color(get_pal(my_color))
my_theme_hex <- get_hex(my_color)


color_dark_text = "#222629"

# MY PLOT THEME
my_plot_theme <- function (base_size, base_family="Segoe UI Semibold"){ 
  dark_color="#222629"
  facet_header = "#78767647"
  dark_text = "#222629"
  
  half_line <- base_size/2
  theme_algoritma <- theme(
    
    plot.background = element_rect(fill=NA,colour = NA), #background plot
    plot.title = element_text(size = rel(1.2), margin = margin(b = half_line * 1.2), 
                              color= dark_text, hjust = 0, family=base_family, face = "bold"),
    plot.subtitle = element_text(size = rel(1.0), margin = margin(b = half_line * 1.2), color= dark_text, hjust=0),
    plot.margin=unit(c(0.5,0.5,0.5,0.5),"cm"),
    #plot.margin=unit(c(0.5,r=5,1,0.5),"cm"),
    
    panel.background = element_rect(fill="#18181800",colour = "#e8e8e8"), #background chart
    panel.border = element_rect(fill=NA,color = NA),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.x = element_blank(),
    panel.grid.major.y = element_line(color="#e8e8e8", linetype=2),
    panel.grid.minor.y = element_blank(),
    #panel.margin = unit(0.8*half_line, "mm"), 
    panel.margin.x = NULL, 
    panel.margin.y = NULL, 
    panel.ontop = FALSE,
    panel.spacing = unit(1.2,"lines"),
    
    legend.background = element_rect(fill="#18181800",colour = NA),
    legend.text = element_text(size = rel(0.7),color=dark_text),
    legend.title =  element_text(colour = dark_text, size = base_size, lineheight = 0.8),
    legend.box = NULL, 
    
    # text = element_text(colour = "white", size = base_size, lineheight = 0.9, 
    #                    angle = 0, margin = margin(), debug = FALSE),
    axis.text = element_text(size = rel(0.8), color=dark_text),
    axis.text.x = element_text(colour = dark_text, size = base_size, margin = margin(t = 0.8 * half_line/2)),
    axis.text.y = element_text(colour = dark_text, size = base_size, margin = margin(r = 0.8 * half_line/2)),
    axis.title.x = element_text(colour = dark_text, size = base_size, lineheight = 0.8,
                                margin = margin(t = 0.8 * half_line, b = 0.8 * half_line/2)), 
    axis.title.y = element_text(colour = dark_text, size = base_size, lineheight = 0.8,
                                angle = 90, margin = margin(r = 0.8 * half_line, l = 0.8 * half_line/2)),
    axis.ticks = element_blank(),
    
    strip.background = element_rect(fill=facet_header,colour = NA),
    strip.text = element_text(colour = dark_text, size = rel(0.8)), 
    strip.text.x = element_text(margin = margin(t = half_line*0.8, b = half_line*0.8)), 
    strip.text.y = element_text(angle = -90, margin = margin(l = half_line, r = half_line)),
    strip.switch.pad.grid = unit(0.1, "cm"), 
    strip.switch.pad.wrap = unit(0.1, "cm"),
    complete = TRUE
    
  )
}

1 Project Description

Pada Part 2 ini, kita akan melakukan analisis dan segmentasi customer berdasarkan RFM Value. Project ini terbagi menjadi 3 part yaitu:

  1. Part 1 : Data Preparation and Exploratory Analysis
  2. Part 2 : Customer Analysis and Segmentation
  3. Part 3 : Product Personalization
  4. Shiny Dashboard : CUSTMARKETS


Saya sangat menyarankan teman-teman membaca setiap part secara berurutan karena setiap part berhubungan.

2 Theory

2.1 RFM Analysis

Metode analisis Recency, Frequency, Monetary Value (RFM) adalah salah satu metode analisis dan segmentasi pelanggan berdasarkan kebiasan customer. Variabel yang digunakan untuk melakukan RFM analisis yaitu:

  1. Recency : Seberapa baru pelanggan melakukan transaksi.
  2. Frequency : Seberapa sering pelanggan melaukan transaksi
  3. Monetary : Seberapa besar transaksi yang sudah dilakukan pelanggan

Variabel RFM bisa dianalisa sebagai single parameter ataupun dikombinasikan. Misalkan berdasarkan nilai monetary, maka kita bisa mengetahui siapa customer yang paling banyak menghabiskan uang untuk berbelanja. Namun, bagaimana jika customer tersebut hanya berbelanja satu kali? atau transaksi terakhirnya sudah lama? lantas bagaimana jika ternyata customer tersebut sudah tidak menggunakan produk kita lagi? Menilai pelanggan hanya dari 1 aspek akan memberikan insight yang kurang akurat, maka dari itu kita perlu mengkombinasikan nilai RFM ini.

Sesuai artikel yang dipublish oleh www.marketeers.com, GO-JEK adalah salah satu perusahaan yang menggunakan metode analisis RFM dalam menentukan segmentasi pelanggan. GO-JEK membagi segmentasi pelanggan ke dalam empat kelas, yaitu Gold, Silver, Bronze, dan Non-Profit. Segmentasi Gold memiliki kualifikasi konsumen dengan high monetary, high frequency, dan high recency. Silver memiliki kualifikasi konsumen dengan tingkat monetary yang tinggi, frequency yang rendah, dan recency yang tinggi. Kategori Bronze terdiri dari konsumen dengan tingkat monetary rendah, frequency, dan recency yang tinggi. Sementara segmentasi Non-Profit memiliki kualifikasi konsumen dengan kualifikasi monetary, frequency, dan recency yang rendah.


Mengutip dari artikel Customer Segmentation — RFM Analysis, beberapa pertanyaan yang bisa dijawab oleh RFM analisis antara lain:

  1. Siapa customer terbaik?
  2. Siapa customer yang berpotensi untuk churn atau hilang/tidak menggunakan produk lagi?
  3. Siapa customer yang berpotensi untuk dikonversi menjadi pelanggan yang lebih menguntungkan?
  4. Siapa customer yang churn dan tidak perlu diberikan perhatian lebih?
  5. Siapa customer yang harus dipertahankan?
  6. Siapa customer yang loyal?
  7. Siapa customer yang berpotensi besar merespon campaign yang dilakukan?


2.2 RFM Segmentation

Kita bisa melakukan segmentasi customer berdasarkan nilai RFM untuk melakukan approach yang berbeda dan optimal. Proses segmentasi RFM ini bisa dilakukan dengan cara scoring atau nilai aslinya menggunakan teknik:

1. Quantile
Penggunaan teknik quantile yaitu membagi sekumpulan data berdasarkan sebarannya. Teknik ini powerfull dan mudah diimplementasikan, namun kita perlu menetapkan batasan-batasan nilainya. Lantas bagaimana jika kita “buta” terhadap batasannya, maka kita bisa menggunakan teknik machine learning.


2. Machine Learning
Metode machine learning bisa digunakan untuk segmentasi dan akrabnya dikenal Unsupervised Machine Learning. Pendekatan Unsupervised Machine Learning akan mempelajari pola data dan kemudian melakukan groupping setiap data kedalam cluster-cluster unik. Sangat banyak algoritma yang dapat digunakan untuk segmentasi, contohnya K-Means. Metode K-Means merupakan algoritma centroid-based clustering yang dimana proses clustering dilakukan berdasarkan jarak sehingga variabel-variabelnya harus bersifat numerik atau kuantitatif. Sebelum menggunakan teknik K-Means, kita harus menentukan jumlah cluster yang diingikan (K = Cluster). Secara sederhana, konsep K-Means clustering sebagai berikut:

Pada gambar di atas, misalkan kita memutuskan untuk membagi data ke dalam 2 cluster, data warna hijau merupakan data asli. Maka stepnya adalah:

  1. Data diatas bersifat 2 dimensi (2 variabel)
  2. Iterasi 1 - Melatakan 2 titik random sebagai inisialisasi centroid atau pusat cluster. Simbol X merah dan biru merupakan centroid.
  3. Iterasi 1 - Assign anggota cluster berdasarkan jarak ke centroid.
  4. Iterasi 2 - Pindahkan centroid ke titik tengah masing-masing cluster.
  5. Iterasi 2 - Re-assign anggota cluster berdasarkan jarak centroid pada iterasi 2.
  6. Iterasi 3 - Pindahkan centroid ke titik tengah masing-masing cluster.
  7. Iterasi 3 - Tidak terdapat perubahan anggota cluster ketika re-assign, sehingga proses berhenti disini.(makanya gambar tidak ada)


Metode K-Means sangat sensitif terhadap jarak, sehingga kita harus berhati-hati ketika mengaplikasikannya. Adapun beberapa permasalahannya yaitu:

  1. Ditemukannya beberapa model clustering yang berbeda
  2. Pemilihan jumlah cluster yang paling tepat
  3. Kegagalan untuk converge
  4. Outliers
  5. Bentuk cluster
  6. Overlapping

3 Data Preparation

Proses data preparation dan EDA secara general sudah dilakukan pada Part 1: Data Preparation and Exploratory Analysis, sehingga kita bisa langsung melakukan inti dari Part 2 yaitu fokus pada customer. Berikut 10 data teratas:

4 Feature Engineering

Dalam case ini, dataset berisikan data transaksi dari 1 Desember 2010 sampai 30 November 2011, sehingga berikut perlakukan RFM Value untuk case ini:

  1. Recency : Selisih antara hari terakhir pelanggan melakukan transaksi dan hari melakukan analisis. Dalam case ini, hari analisis menggunakan data hari terakhir transaksi + 1.
  2. Frequency : Jumlah transaksi yang dilakukan oleh pelanggan dari 1 Desember 2010 sampai 30 November 2011.
  3. Monetary : Jumlah total order amount yang sudah dikeluarkan pelanggan dari 1 Desember 2010 sampai 30 November 2011.

Berikut proses dan sample 10 data teratas:

analysis_date = date(max(df_customer_segmentation$invoice_date))+days(1)

#first_order, last_order, avg_amount_order, age, recency, monetary
df_customer_rfm <- df_customer_segmentation %>% 
  group_by(customer_id,country) %>% 
  summarise(
         first_order = as.Date(min(invoice_date)),
         last_order = as.Date(max(invoice_date)),
         #min_amount_order = min(total_amount),
         avg_amount_order = median(total_amount),
         #max_amount_order = max(total_amount),
         age = as.integer(analysis_date-date(first_order)),
         recency =  as.integer(analysis_date-date(last_order)),
         monetary = sum(total_amount)) %>% 
  ungroup()

#frequency
df_customer_rfm <- df_customer_segmentation %>% select(customer_id,invoice_no) %>% 
  distinct() %>% 
  group_by(customer_id) %>% 
  summarise(frequency = n()) %>% 
  ungroup() %>% 
  left_join(df_customer_rfm,by=c("customer_id","customer_id"))


# Baskets size
df_customer_rfm <- df_customer_segmentation %>% group_by(customer_id,invoice_no) %>% 
  summarise(freq = n()) %>% ungroup() %>% 
  group_by(customer_id) %>% 
  summarise(
  #  min_baskets = min(freq),
  #  max_baskets = max(freq),
    avg_baskets = round(median(freq),0)
  ) %>% ungroup() %>% 
  left_join(df_customer_rfm, by = c("customer_id","customer_id")) 




# Rata-rata jarak transaksi per-customer. Jika hanya transaksi 1 kali maka nilainya 0
df_return <- df_customer_segmentation %>% select(customer_id,invoice_date) %>% distinct() %>% arrange(customer_id,invoice_date) %>% 
  group_by(customer_id) %>% 
  group_nest(.key = "invoice_date_list") %>% 
  ungroup()

# Fungsi untuk mendapatkan jarak transaksi. Menggunakan nilai median.
get_date_distance <- function(data_value){
  data_value <- list(data_value[[1]]$invoice_date)
  i = 1
  df = data.frame(start=NA, end =NA, result = NA)
  while (i<=length(data_value[[1]])) {
    if(i!=1){
       start = data_value[[1]][i-1]
       end = data_value[[1]][i]
       result = end-start
       df <- df %>% add_row(start = start, end = end, result = result)   
    }
    i=i+1
  }
  df <- drop_na(df)
  floor(median(df$result))
}


df_return <- df_return %>% group_by(customer_id) %>%
  mutate(avg_return = get_date_distance(invoice_date_list)) %>% 
  ungroup() %>% 
  select(customer_id,avg_return) %>% 
  mutate(avg_return = replace_na(avg_return,0))


df_customer_rfm <- df_customer_rfm %>%
  left_join(df_return, by=c("customer_id","customer_id"))%>% 
  select(customer_id, country, first_order,last_order, avg_amount_order, avg_baskets, avg_return, age, recency, frequency, monetary)



head(df_customer_rfm,10)

Berikut penjelasan variabel diatas:

Variable Description
customer_id Merupakan id customer
country Merupakan negara customer
first_order Pertama kali customer melakukan transaksi
last_order Terakhir kali customer melakukan transaksi
avg_amount_order Rata-rata biaya yang dikeluarkan customer per-transaksi
avg_baskets Rata-rata jumlah produk yang dibeli customer per-transaksi
avg_return Rata-rata jarak customer melakukan transaksi. Jika nilainya 0 maka hanya terdapat 1 transaksi
age Usia customer, didapatkan dari hari pertama customer bertransaksi dan hari melakukan analisis. Dalam case ini, hari analisis menggunakan data hari terakhir transaksi + 1.
recency Selisih antara hari terakhir pelanggan melakukan transaksi dan hari melakukan analisis. Dalam case ini, hari analisis menggunakan data hari terakhir transaksi + 1.
frequency Jumlah transaksi yang dilakukan oleh pelanggan.
monetary Jumlah total order amount yang sudah dikeluarkan pelanggan.



5 Exploratory Data Analysis

Bagaimana distribusi umur customer?

Chart diatas menunjukan distribusi umur customer. Kebanyakan customer berumur 364 hari, hal ini make sense dan sesuai dengan chart jumlah new customer pada part 1. Dari chart ini kita mendapat insight bahwa jumlah customer baru terus menurun, sehingga akan sangat penting menjaga customer yang ada untuk tetap aktif.


Chart di atas menggambarkan rentang waktu rata-rata customer melakukan pembelian kembali. Jika dilihat avg return = 0 paling banyak, pada data ini avg return = 0 artinya tidak melakukan pembelian kembali. Dari chart ini kita bisa menyimpulkan bahwa retention rate pada online retail ini kurang baik. Insight yang kita dapat adalah perusahaan harus melakukan strategi retention campaign untuk meningkatkan retention rate. Untuk itu, mari kita analisa dan segmentasikan customer-customer yang ada.



6 RFM Segmentation

6.1 Recency Segmentation

Recency menunjukan kapan customer terakhir kali melakukan transaksi. Data ini merupakan histori transksi selama 1 tahun dan pada case ini kita bisa mengsegmentasikan customer terhadap nilai Recency menggunakan teknik Quantile dengan ketentuan berikut:

  1. Active : Recency <= 30
  2. Warm : 30 < Recency <= 90
  3. Cold : 90 < Recency <= 180
  4. Inactive : Recency > 180
recency_adjust <- data.frame(
  active = 30,
  warm = 90,
  cold = 180,
  inactive = Inf
)


df_customer_rfm <- df_customer_rfm %>% mutate(
  recency_segment = case_when(recency <= recency_adjust$active ~ "active",
                              recency > recency_adjust$active & recency <= recency_adjust$warm ~ "warm",
                              recency > recency_adjust$warm & recency <= recency_adjust$cold ~ "cold",
                              TRUE ~ "inactive"),
  recency_segment = factor(recency_segment, levels=c(names(recency_adjust)))
)

recency_count <- data.frame(table(df_customer_rfm$recency_segment))
recency_segmentation <- df_customer_rfm %>% 
  group_by(recency) %>% summarise(freq=n()) %>% ungroup() %>% 
            mutate(
                popup=glue("Recency: {recency}
                           Total Customer: {freq}")
            )

plot_recency_segmentation <- ggplot(recency_segmentation,aes(recency,freq))+
            geom_area(fill=my_color[5])+
            geom_point(aes(text=popup),size=0.2,alpha=0.05)+
            geom_vline(xintercept = recency_adjust$active,linetype="dotted",color = "black", size=1,alpha=0.2)+
            geom_vline(xintercept = recency_adjust$warm,linetype="dotted",color = "black", size=1,alpha=0.2)+
            geom_vline(xintercept = recency_adjust$cold,linetype="dotted",color = "black", size=1, alpha=0.2)+
            
            geom_point(aes(x=recency_adjust$active, y=10+50), colour="black", size=1)+
            annotate("text", x = recency_adjust$active, y = 10+60, 
                     color = "black", size=3, label=paste0(recency_adjust$active," days"))+
            geom_point(aes(x=recency_adjust$warm, y=10+10), colour="black", size=1)+
            annotate("text", x = recency_adjust$warm, y = 10+20, 
                     color = "black", size=3, label=paste0(recency_adjust$warm," days"))+
            geom_point(aes(x=recency_adjust$cold, y=15), colour="black", size=1)+
            annotate("text", x = recency_adjust$cold, y = 10+15, 
                     color = "black", size=3, label=paste0(recency_adjust$cold," days"))+

            annotate("text", x = (0+recency_adjust$active)/2, y = max(recency_segmentation$freq)+10, 
                     color = "black", size=3, label="Active")+
            annotate("text", x = (0+recency_adjust$active)/2, y = max(recency_segmentation$freq)+20, 
                     color = "black", size=3.2, label=prettyNum(recency_count %>% filter(Var1=="active") %>% .$Freq, big.mark=","))+
            annotate("text", x = (0+recency_adjust$active)/2, y = max(recency_segmentation$freq)+2, 
                   color = "#676767", size=2.2, label=paste0(round(((recency_count %>% filter(Var1=="active") %>%
                                                                       .$Freq)/sum(recency_count$Freq))*100,1),"%"))+
          
            annotate("text", x = median(recency_adjust$active:recency_adjust$warm), y = max(recency_segmentation$freq)+10, 
                     color = "black", size=3, label="Warm")+
            annotate("text", x = median(recency_adjust$active:recency_adjust$warm), y = max(recency_segmentation$freq)+20, 
                     color = "black", size=3.2, label=prettyNum(recency_count %>% filter(Var1=="warm") %>% .$Freq, big.mark=","))+
            annotate("text", x = median(recency_adjust$active:recency_adjust$warm), y = max(recency_segmentation$freq)+2, 
                     color = "#676767", size=2.2, label=paste0(round(((recency_count %>% filter(Var1=="warm") %>%
                                                                         .$Freq)/sum(recency_count$Freq))*100,1),"%"))+
            
            annotate("text", x = median(recency_adjust$warm:recency_adjust$cold), y = max(recency_segmentation$freq)+10, 
                     color = "black", size=3, label="Cold")+
            annotate("text", x = median(recency_adjust$warm:recency_adjust$cold), y = max(recency_segmentation$freq)+20, 
                     color = "black", size=3.2, label=prettyNum(recency_count %>% filter(Var1=="cold") %>% .$Freq, big.mark=","))+
            annotate("text", x = median(recency_adjust$warm:recency_adjust$cold), y = max(recency_segmentation$freq)+2, 
                      color = "#676767", size=2.2, label=paste0(round(((recency_count %>% filter(Var1=="cold") %>%
                                                                          .$Freq)/sum(recency_count$Freq))*100,1),"%"))+
          
            annotate("text", x = median(recency_adjust$cold:max(recency_segmentation$recency)), y = max(recency_segmentation$freq)+10, 
                     color = "black", size=3, label="Inactive")+
            annotate("text", x = median(recency_adjust$cold:max(recency_segmentation$recency)), y = max(recency_segmentation$freq)+20, 
                     color = "black", size=3.2, label=prettyNum(recency_count %>% filter(Var1=="inactive") %>% .$Freq, big.mark=","))+
            annotate("text", x = median(recency_adjust$cold:max(recency_segmentation$recency)), y = max(recency_segmentation$freq)+2, 
                     color = "#676767", size=2.2, label=paste0(round(((recency_count %>% filter(Var1=="inactive") %>%
                                                                         .$Freq)/sum(recency_count$Freq))*100,1),"%"))+
            labs(
                #title= "Order Value by Recency",
                title = "Recency Segmentation using Quantile Method",
                x = "Recency (days since last transaction)",
                y = "Total Customer"
            )+
            my_plot_theme(10)
        
ggplotly(plot_recency_segmentation, tooltip = NULL)%>% 
    layout(showlegend=FALSE) %>%  
    config(displayModeBar = F)

Chart diatas menggambarkan proporsi customer berdasarkan transaksi terkahirnya. Kita bisa melakukan fokus campaign yang berbeda terhadap segmen diatas, misalkan:

  1. Segmen Active : Fokus campaign yaitu untuk meningkatkan pembelian customer (X/Up Sell Campaign).
  2. Segmen Warm : Customer pada segmen ini beresiko untuk Churn, sehinga fokus campaign yaitu supaya customer melakukan pembelian kembali (Retention Campaign, X/Up Sell Campaign).
  3. Segmen Cold : Customer pada segmen ini sangat beresiko untuk churn. Sehingga fokus campaign untuk mengaktifkan customer dan melakukan pembelian kembali (Reactivation Campaign, Retention Campaign).
  4. Segmen Inactive : Customer pada segmen ini sudah churn/hilang. Sehingga fokus campaign untuk mengaktifkan customer kembali (Reactivation Campaign).

Hasil dari segmentasi berdasarkan recency bisa digunakan sebagai tolak ukur prioritas campaign terhadap masing-masing fokus campaign.


6.2 Customer Value Segmentation

Setelah kita mendapatkan prioritas dan fokus campaign. Kita juga harus mengukur nilai customer, akan kurang optimal apabila customer yang bernilai rendah diberikan prioritas tinggi dan diberikan campaign seperti promosi yang besar-besaran. Hal ini dapat menyebabkan overcost budget. Kita bisa menggunakan nilai Monetary dan Frequency untuk menentukan nilai customer. Dalam hal ini, saya tidak mengetahui batasan-batasan nilai untuk Frequency dan Monetary sehingga saya memutuskan untuk menggunakan teknik K-Means Clustering. Berikut ini seluruh prosesnya.

6.2.1 Correlaction Checking

Jika dilihat frequency dan monetary memiliki korelasi yang cukup kuat, dalam artian semakin tinggi frequency maka semakin besar nilai monetary. Sehingga, seharusnya jika penyebaran data x=frequency dan y=monetary divisualisasikan akan terlihat linier.


6.2.2 Outlier Checking

Mari kita cek dahulu sebaran data dan outliernya.

Dari visualisasi diatas, nilai frequency dan monetary tidak berdistribusi normal dan juga memiliki outlier yang cukup banyak. Mari kita cek lebih detail lewat scatter plot.

Terdapat customer-customer yang jauh dari pusat penyebaran data atau dapat kita sebut outlier/nilai eksrem. Interpretasi outlier disini yaitu terdapat customer yang sangat sering berbelanja ataupun sekalinya belanja langsung spending money sangat tinggi. Perlu kita ketahui bahwa teknik clustering sangat sensitif terhadap jarak, maka kita bisa mengexclude outlier kemudian kita kategorikan customer pada outlier ini sebagai special customer. Saya coba mengkexplore datanya dan mencoba exlude 5% customer yang memiliki nilai ekstrem, berikut ini hasilnya:

95% customer berada pada rentang frequency <= 18 dan monetary <=6000. Maka keputusannya, 95% customer ini akan kita clustering mengunakan K-Means dan 5% customer yang termasuk outlier tadi akan kita buatkan segmen Special Customer.

6.2.3 Scalling

Meskipun nilai ekstrem sudah kita exclude, tetap perlu diingat bahwa teknik clustering sangat sensitif terhadap jarak, maka perlu kita lakukan scalling dahulu. Disini saya melakukan scalling menggunakan min-max normalization untuk menjaga pola datanya tetap linear. Berikut hasil scalling nya:

Bisa dilihat, distribusinya masih tidak normal dan masih terdapat outlier. Meski begitu, tidak masalah karena memang datanya seperti itu. Namun jika hendak melakukan pemodelan yang bersifat supervised learning, maka hal ini perlu diperhatikan dengan baik.

6.2.4 Optimal K For Clustering

Pada dasarnya jumlah cluster harus kita tentukan sendiri, namun terdapat teknik untuk mengetahui jumlah cluster yang optimal seperti menggunakan Elbow Method, Silhouette Method, Gap Statistic atau pendekatan lainnya. Untuk case ini kita akan mencoba Elbow Method dan Silhouette Method sebagai referensi penentuan jumlah cluster.

6.2.4.1 Elbow Method

Elbow method mengukur jumlah cluster optimal berdasarkan nilai SSE (Sum of Square Error). Secara visualisasi, jumlah cluster optimal bisa dilihat dari titik yang cenderung membentuk siku pertama kali atau titik yang memiliki jumlah perubahan tinggi/signifikan dari titk sebelumnya. Berikut visualisasinya:

Berdasarkan visualisasi Elbow Method diatas, perubahan signifikan terjadi pada K=2 dan terjadi perubahan yang cukup tinggi lagi pada cluster 3. Mari kita lihat hasil silhouette method.

6.2.4.2 Silhouette Method

Silhouette Method mengukur koefisien Silhouette dengan menghitung rata-rata jarak setiap data terhadap semua data pada cluster yang sama, kemudian menghitung rata-rata jarak setiap data dengan semua data pada cluster lain. Setelah itu, data akan dikelompokan kembali berdasarkan jarak minimum. Jika dalam perulangan/iterasi di dapati posisi centorid berubah, maka data akan dikelompokan ulang. Sama halnya dengan teknik K-Means.

Berdasarkan visualisasi Silhouette Method, nilai K optimal dapat dilihat pada titik tertinggi. Berikut visualisasinya:

Berdasarkan visualisasi diatas, jumlah cluster paling optimum yaitu K=2 dan dilanjutkan dengan K=3. Namun, mengigat rentang penyebaran data sebelumnya, maka terkesan kurang bijak apabila kita hanya menggunakan 2 cluster karena karakter frekuensi dan monetary-nya kurang terlihat. Maka saya putuskan untuk mencoba menggunakan 3 cluster dan 4 cluster kemudian kita bandingkan hasilnya.

notes: Jumlah cluster tidak tergantung penuh pada teknik-teknik penentuan K-Optimal. Jumlah cluster bisa kita tentukan sendiri sesuai dengan kebutuhan.


6.2.5 K-Means Clustering

6.2.5.1 Clustering using K=3

Hasil K-Means dengan 3 cluster memiliki proporsi jumlah cluster yaitu 952:2788:339 dengan nilai between_SS / total_SS = 75.9 %. Jika dilihat dari proporsinya, data berpusat pada cluster ke-2 dengan jumlah 2788 customer.

#> [1]  952 2788  339
#> [1] 75.9
# K-Means
cust_fm_mm_k3 <- cbind(df_customer_rfm_adjust[,c("customer_id","recency","recency_segment","frequency","monetary")],
                     fm_segment=kmeans_mm_k3$cluster) %>%
   mutate(
    fm_segment=as.factor(fm_segment))

perfom <- round((kmeans_mm_k3$betweenss/kmeans_mm_k3$totss)*100,1)

cluster_rfm_mm_profile_k3 <- cust_fm_mm_k3 %>% group_by(fm_segment) %>% 
  summarise(
    min_recency = min(recency),
    max_recency = max(recency),
    avg_recency = round(mean(recency),2),
    min_frequency = min(frequency),
    max_frequency = max(frequency),
    avg_frequency = round(mean(frequency),2),
    med_frequency = round(median(frequency),2),
    min_monetary = min(monetary),
    max_monetary = max(monetary),
    avg_monetary = round(mean(monetary),2),
    med_monetary = round(median(monetary),2),
    total_monetary = sum(monetary),
    total_customer = n()
  ) %>% ungroup()


cluster_rfm_mm_profile_k3 %>% 
  gather("features","values",max_frequency,min_frequency,avg_frequency,med_frequency,
         min_monetary,max_monetary,avg_monetary,med_monetary) %>% 
  ggplot(aes(x = fm_segment, y = features)) +
  #scale_x_continuous(breaks = seq(min(cluster), max(cluster), by = 1)) +
  geom_tile(aes(fill = values), show.legend = FALSE) +
  geom_text(aes(label=round(values,2)), size=4, color="white")+
  coord_equal() +
  labs(
    title = "K-Means Clustering for 3 Segment",
    subtitle = paste("betweenss/totts:",perfom,"%"),
    x = "Cluster"
  )+
  scale_fill_gradient(low=my_theme_hex("col3") ,na.value = "#C0C0C0", high=my_theme_hex("col6"))+
   theme(text = element_text(size=14),
          plot.title =element_text(size=16),
          legend.position = "bottom",
          axis.text.y = element_text(size=14),
          axis.title.x = element_text(size=14))-> plot_kmeans_k3


grid.arrange(plot_kmeans_k3, nrow = 1)

6.2.5.2 Clustering using K=4

Hasil K-Means dengan 4 cluster memiliki proporsi jumlah cluster yaitu 504:2332:222:2021 dengan nilai between_SS / total_SS = 81.9 %. Jika dilihat dari proporsinya, data berpusat pada cluster ke-2 dan ke-3.

#> [1]  504 2332  222 1021
#> [1] 81.5
# K-Means
cust_fm_mm_k4 <- cbind(df_customer_rfm_adjust[,c("customer_id","recency","recency_segment","frequency","monetary")],
                     fm_segment=kmeans_mm_k4$cluster) %>%
   mutate(
    fm_segment=as.factor(fm_segment))

perfom <- round((kmeans_mm_k4$betweenss/kmeans_mm_k4$totss)*100,1)

cluster_rfm_mm_profile_k4 <- cust_fm_mm_k4 %>% group_by(fm_segment) %>% 
  summarise(
    min_recency = min(recency),
    max_recency = max(recency),
    avg_recency = round(mean(recency),2),
    min_frequency = min(frequency),
    max_frequency = max(frequency),
    avg_frequency = round(mean(frequency),2),
    med_frequency = round(median(frequency),2),
    min_monetary = min(monetary),
    max_monetary = max(monetary),
    avg_monetary = round(mean(monetary),2),
    med_monetary = round(median(monetary),2),
    total_monetary = sum(monetary),
    total_customer = n()
  ) %>% ungroup()


cluster_rfm_mm_profile_k4 %>% 
  gather("features","values",max_frequency,min_frequency,avg_frequency,med_frequency,
         min_monetary,max_monetary,avg_monetary,med_monetary) %>% 
  ggplot(aes(x = fm_segment, y = features)) +
  #scale_x_continuous(breaks = seq(min(cluster), max(cluster), by = 1)) +
  geom_tile(aes(fill = values), show.legend = FALSE) +
  geom_text(aes(label=round(values,2)), size=4, color="white")+
  coord_equal() +
  labs(
    title = "K-Means Clustering for 4 Segment",
    subtitle = paste("betweenss/totts:",perfom,"%"),
    x = "Cluster"
  )+
  scale_fill_gradient(low=my_theme_hex("col3") ,na.value = "#C0C0C0", high=my_theme_hex("col6"))+
    theme(text = element_text(size=14),
          plot.title =element_text(size=16),
          legend.position = "bottom",
          axis.text.y = element_text(size=14),
          axis.title.x = element_text(size=14))-> plot_kmeans_k4


grid.arrange(plot_kmeans_k4, nrow = 1)

Berdasarkan proses clustering diatas, hasil cluster menggunakan metode K-Means dengan K=3 sudah cukup menunjukan hasil yang dimana setiap cluster memiliki indentitas yang berbeda. Maka dari itu dapat kita putuskan, hasil segmentasi ini terdiri dari 4 kelompok customer yang terdiri dari 3 cluster customer berdasarkan hasil hasil k-means dan 1 cluster berisikan 5% customer dengan nilai tertinggi yang kita exclude sebelumnya


6.2.6 Frequency/Monetary Segmentation Profiling


Berdasarkan visualisasi profiling cluster diatas, maka setiap cluster bisa artikan sebagai berikut:

  1. Cluster 1 : Medium Frequency - Medium Monetary. Kita sebut segmen Medium Value Customer.
  2. Cluster 2 : Low Frequency, Low Monetary. Kita sebut segmen Low Value Customer.
  3. Cluster 3 : High Frequency, High Monetary. Kita sebut segmen High Value Customer.
  4. Cluster 4 : Very High Frequency, Very High Monetary. Kita sebut segmen Special Value Customer.


Berikut ini data hasil segmentasi:


Berikut ini visualisasi hasil segmentasi



6.3 RFM Segmentation Result

Sebelumnya kita sudah melakukan segmentasi customer berdasarkan Recency, Frequency dan Monetary. Berikut ini summary hasil RFM Segmentation:

Chart di atas menunjukan distribusi penyebaran setiap customer berdasarkan hasil RFM Segmentation. Jika dilihat terdapat 16 kombinasi dengan perspektif yang berbeda-beda. Contohnya, Customer pada segmen Low Value dan Active bisa kita simpulkan customer baru karena mereka baru mengorder 1-2 kali dalam waktu 30 hari. Berikut ini detail informasi dari hasil segmentasi:

Chart di atas memberikan informasi total customer pada masing-masing segmen customer. Bisa dilihat bahwa 64.9% customer kita ada disegmen low value customer.

Chart di atas memberikan informasi total monetary per recency segment. Dalam 1 tahun, 70.7% transaksi terjadi pada 1 bulan atau 30 terakhir. Pertanyaannya, kemana aja selama 11 bulan? Dari hasil ini, maka perlu ada evaluasi terkait penjualan selama 11 bulan awal.

Chart di atas memberikan informasi total monetary yang dihasilkan setiap segmen customer. Bisa dilihat bahwa 35% customer (Special, High, Medium) menghasilkan penjualan sebesar 85% dalam satu tahun. Berdasarkan hal ini, kita bisa memfokuskan strategi cross selling dan up selling pada customer ini. Kemudian pada segmen Low Value customer kita bisa fokus ke strategi retention.



7 Summary

Kita sudah berhasil melakukan analisa dan segmentasi customer menggunakan RFM value. Tentunya masih banyak hal yang bisa diolah menggunakan nilai RFM dan bisa juga dikombinasikan dengan Average Return, Age, Transaction Quantity. Namun fokus kali ini untuk mengetahui nilai customer kita, contohnya apabila kamu semakin sering berbelanja pada sebuah toko maka bisa disimpulkan kamu semakin nyaman berbelanja disitu (Emotional Value) dan apabila kamu mengeluarkan uang semakin besar pada sebuah toko maka kamu semakin butuh produk yang dijual toko tersebut (functional value), namun jika kamu sudah tidak pernah berbelanja lagi di toko tersebut maka perlu dipertanyakan persoalnnya apa.

Hasil analisa RFM dapat membantu perusahaan untuk menentukan strategi-strategi yang bisa meningkatkan loyalitas customer yang berujung meningkatnya customer value. Meng-segmentasikan customer berdasarkan nilai Recency memberikan insight pada bisnis untuk menentukan strategi dalam memprioritaskan customer. Kemudian meng-segmentasikan customer berdasarkan nilai Frequency dan Monetary akan membantu bisnis untuk mengoptimalkan resources, budget dan waktu karena bisa fokus pada segmen tertentu. Namun kedua hasil ini harus dikombinasikan, misalnya akan kurang baik apabila perusahaan mengeluarkan banyak resources, budget dan waktu terhadap customer yang bernilai rendah dan sudah tidak aktif.



8 Suggestion

Berdasarkan hasil recency segmentation, sekitar 70% transaksi terjadi pada November 2011 (1 bulan terakhir) dan hanya 30% terjadi selama 11 bulan pertama. Padahal customer yang aktif bertransaksi pada November 2011 hanya sebesar 38.7%. Sehingga perlu dilakukan evaluasi kembali apa yang terjadi pada 11 bulan pertama. Rekomendasi yang bisa diberikan berdasarkan recency segementation yaitu:

  1. Segmen Active : Fokus untuk meningkatkan pembelian customer sehingga perlu membentuk cross/Up Selling Strategy.

  2. Segmen Warm : Fokus supaya customer melakukan pembelian kembali sehingga perlu membentuk Retention Strategy, Cross/Up Selling Strategy.

  3. Segmen Cold : Customer pada segmen ini sangat beresiko untuk churn, maka fokus untuk mengaktifkan customer dan melakukan pembelian kembali dengan membentuk Reactivation Strategy, Retention Strategy.

  4. Segmen Inactive : Customer pada segmen ini sudah churn, maka fokus campaign untuk mengaktifkan customer kembali dengan membentuk Reactivation strategy.


Berdarkan hasil Customer Value Segmentation, maka rekomendasi yang bisa diberikan yaitu:

  1. Total 35% customer (Special, High, Medium) menghasilkan penjualan sebesar 85%, sehingga proporsi resources, budget dan waktu campaign harus dioptimalkan terhadap customer di segmen ini demi menjaga loyalitas mereka dan bisa meningkatkan value mereka.

  2. Total 65% customer merupakan low value customer dan hanya menghasilkan 15% penjualan. Customer ini hanya berbelanja 1-2 kali, maka proporsi resources, budget dan waktu pada customer ini tidak perlu besar, tapi kita tidak boleh mengabaikan mereka, terutama pada low customer yang berda disegmen active karena mereka adalah customer baru dan sangat berpotensi.

  3. Salah satu cara efektif untuk meningkatkan loyalitas dan customer value yaitu membuat customer loyality program. Sebagai contoh, setiap customer akan mendapatkan 1 poin setiap melakukan pembelian sebesar £ 3.0 dan setiap kelipatan poin tertentu bisa ditukarkan dengan cashback, diskon, special gift dan lainnya. Dengan begini, kemungkinan frekuensi belanja customer akan meningkat dan perusahaan tidak perlu mengeluarkan banyak budget untuk mengoptimalkan customer.



9 What’s Most Important

Strategi untuk meningkatkan loyalitas customer yang paling sering dijumpai yaitu program promosi, diskon atau potongan harga. Namun, perlu disadari bahwa seorang customer akan membeli produk karena dia sudah nyaman berbelanja disuatu penjual dan penjual tersebut menyediakan produk yang sesuai dengan ketertarikannya. Jadi, semua bentuk campaign atau program loyalitas yang seharusnya bisa meningkatkan customer value akan jadi percuma jika penjual tidak melakukannya sesuai dengan keinginan atau ketertarikan customer. Misalnya:

  1. Seorang customer sering mendapat email promosi produk-produk dengan harapan customer melakukan pembelian kembali, namun karena produk-produk yang dipromosikan tidak sesuai dengan keinginannya, yang ada customer ini menjadi risih dan meninggalkan penjual.

  2. Seorang customer mendapatkan diskon 35% terhadap produk yang sedang tren, sedangkan customer ini tidak tertarik dengan produk tersebut. Alhasil, diskon ini juga terabaikan.

  3. Seorang customer mendapatkan diskon 20% atas produk apapun dengan harapan customer melakukan pembelian kembali. Namun, customer ini bingung mau membeli produk apa karena banyaknya produk yang dijual yang menyebabkan customer kesulitan mencari produk. Alhasil, diskon tersebut terabaikan. Kasus ini sesuai dengan artikel yang dipublish oleh www.xendit.co yang menginfokan hasil riset Accenture mengatakan bahwa 40% konsumen meninggalkan suatu website bisnis dan melakukan pembelian di website atau toko online lain karena merasa kebingungan akibat terlalu banyaknya pilihan saat akan membuat keputusan pembelian.


Solusi dari permasalahan diatas yaitu Product Personalization. Coba kita bayangkan, kita bisa mengetahui strategi prioritas dan alokasi resources, budget serta waktu melalui RFM Segmentation dan kita juga mengetahui produk apa yang disukai oleh masing-masing customer. Maka kita bisa memberikan diskon atau promosi yang berbeda-beda sesuai nilai customer terhadap produk-produk yang disukai oleh masing-masing customer.


Menurut kalian, bagaimana jika kalian mendapat diskon terhadap produk yang kalian suka? pastinya kalian akan lebih tertarik untuk membelinya kan?

Permasalahannya, bagaimana cara mengetahui produk yang disukai oleh masing-masing customer? Dalam dunia machine learning, salah satu penerapannya yaitu Recommender System. Beberapa perusahaan sudah sukses meningkatkan retention rate menggunakan recommender system, diantaranya yaitu Youtube, Spotify dan Amazon. Coba jujur, pasti kalian sering membuka video yang direkomendasikan youtube ketika kalian sedang menonton video tertentu atau ketika kalian pertama kali membuka youtube maka video yang tampil di list halaman utama kalian entah mengapa cocok dengan ketertarikan kalian. Untuk lebih detail, recommender system akan kita bahas di Part 3 yaa. Cheers!