Email : gabrielerichsonmrp@gmail.com
Linkedin : www.linkedin.com/in/gabrielerichson
Github : www.github.com/gabrielerichsonmrp
# Wrangling
library(tidyverse) #untuk wrangling
library(lubridate) #pengelolaan datetime
library(scales) #scalling variable
#visualization
library(plotly) # visualisasi chart
library(paletti) # warna
library(GGally) # Korelasi
library(glue) # pop up text pada chart
library(gridExtra) # Display 2 chart dalam 1 baris
#Segmentation
library(dbscan) # Clustering menggunakan metode DBScan
library(factoextra) # Elbow method, Shilloute methof dan Clustering menggunakan K-Means# 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
)
}Pada Part 2 ini, kita akan melakukan analisis dan segmentasi customer berdasarkan RFM Value. Project ini terbagi menjadi 3 part yaitu:
Saya sangat menyarankan teman-teman membaca setiap part secara berurutan karena setiap part berhubungan.
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:
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:
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:
Pada gambar di atas, misalkan kita memutuskan untuk membagi data ke dalam 2 cluster, data warna hijau merupakan data asli. Maka stepnya adalah:
Metode K-Means sangat sensitif terhadap jarak, sehingga kita harus berhati-hati ketika mengaplikasikannya. Adapun beberapa permasalahannya yaitu:
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:
df_customer_segmentation <- readRDS("data_clean/df_customer_transaction.rds")
#df_customer_segmentation <- readRDS("data_clean/df_customer_segmentation.rds")
head(df_customer_segmentation,10)Dalam case ini, dataset berisikan data transaksi dari 1 Desember 2010 sampai 30 November 2011, sehingga berikut perlakukan RFM Value untuk case ini:
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. |
Bagaimana distribusi umur customer?
plot_age <- df_customer_rfm %>%
ggplot(aes(age)) +
geom_histogram(fill=my_theme_hex("col4"))+
labs(
title="Customer Age Distribution in Days",
x="Age in Days",
y="Average Frequency")+
my_plot_theme(10)
ggplotly(plot_age) %>%
layout(showlegend=FALSE) %>%
config(displayModeBar = F) 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.
plot_age <- df_customer_rfm %>%
ggplot(aes(avg_return)) +
geom_histogram(fill=my_theme_hex("col5"))+
labs(
title="Average Return in Days",
x="Days of Return",
y="Average Frequency")+
my_plot_theme(10)
ggplotly(plot_age) %>%
layout(showlegend=FALSE) %>%
config(displayModeBar = F) 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.
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:
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:
Hasil dari segmentasi berdasarkan recency bisa digunakan sebagai tolak ukur prioritas campaign terhadap masing-masing fokus campaign.
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.
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.
ggcorr(df_customer_rfm %>% select(recency,frequency,monetary), label = TRUE, label_size = 4, vjust=1, hjust=0.5)+
labs(
title="Correlation Matrix on RFM Value"
)+
my_plot_theme(11)+
scale_fill_gradient(low=my_theme_hex("col2") ,na.value = "#C0C0C0", high=my_theme_hex("col6"))Mari kita cek dahulu sebaran data dan outliernya.
par(mfrow=c(2,2),cex=0.8)
#hist(cust_rfm$log_recency,main="log of recency")
hist(df_customer_rfm$frequency,main="frequency",breaks=200)
hist(df_customer_rfm$monetary,main="monetary", breaks=10000)
boxplot(df_customer_rfm$frequency,main="frequency")
boxplot(df_customer_rfm$monetary, main="monetary")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.
ggplotly(
df_customer_rfm %>%
ggplot(aes(frequency,monetary))+
geom_jitter()+
geom_smooth(method = lm)+
labs(
title = "Frequency vs Monetary of all data"
)+
scale_x_continuous(breaks = seq(from = 0, to = 200, by = 10))+
scale_y_continuous(breaks = seq(from = 0, to = 300000, by = 10000)))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:
frequency_adjust = 18
monetary_adjust = 6000
df_customer_rfm_adjust <- df_customer_rfm %>%
filter(frequency <= frequency_adjust, monetary <=monetary_adjust)
txt_freqmon_range <- paste0("Range Frequency <= ",frequency_adjust, " and Monetary <=", monetary_adjust)
txt_data_total <- paste0("(",round((df_customer_rfm_adjust %>% nrow())/(df_customer_rfm %>% nrow())*100,1),"% Customer)")
ggplotly(
df_customer_rfm_adjust %>%
ggplot(aes(frequency,monetary))+
geom_jitter()+
geom_smooth(method = lm)+
labs(
title = "Frequency vs Monetary"
)) %>%
layout(title = list(text = paste0('Frequency vs Monetary',
'<br>',
'<sup>',
paste0(txt_freqmon_range," ",txt_data_total),
'</sup>')))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.
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:
norm_minmax <- function(x){
return ((x - min(x))/(max(x) - min(x)))
}
df_customer_rfm_adjust$mm_frequency <- norm_minmax(df_customer_rfm_adjust$frequency)
df_customer_rfm_adjust$mm_monetary <- norm_minmax(df_customer_rfm_adjust$monetary)
par(mfrow=c(2,2),cex=0.9)
#hist(cust_rfm$mm_recency,main="1 - minmax of log_recency")
hist(df_customer_rfm_adjust$mm_frequency,main="minmax of frequency")
hist(df_customer_rfm_adjust$mm_monetary,main="minmax of monetary")
#boxplot(cust_rfm$mm_recency, main="1 - minmax of log_recency")
boxplot(df_customer_rfm_adjust$mm_frequency,main="minmax of frequency")
boxplot(df_customer_rfm_adjust$mm_monetary, main="minmax of monetary")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.
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.
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:
fviz_nbclust(df_customer_rfm_adjust[,c("mm_frequency","mm_monetary")], kmeans, method = "wss", k.max = 15) +
labs(subtitle = "Elbow method")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.
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:
fviz_nbclust(df_customer_rfm_adjust[,c("mm_frequency","mm_monetary")], kmeans, "silhouette", k.max = 15) +
labs(subtitle = "Silhouette method")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.
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.
set.seed(2020, sample.kind = "Rounding")
kmeans_mm_k3 <- kmeans(df_customer_rfm_adjust[,c("mm_frequency","mm_monetary")],centers = 3, iter.max = 20)
kmeans_mm_k3$size#> [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)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.
set.seed(2020, sample.kind = "Rounding")
kmeans_mm_k4 <- kmeans(df_customer_rfm_adjust[,c("mm_frequency","mm_monetary")],centers = 4, iter.max = 20)
kmeans_mm_k4$size#> [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
df_segment_result <- df_customer_rfm %>%
left_join(cust_fm_mm_k3 %>% select(customer_id,fm_segment), by=c("customer_id","customer_id")) %>%
mutate(fm_segment = ifelse(is.na(fm_segment),4,fm_segment))df_segment_profile <- df_segment_result %>%
group_by(fm_segment) %>%
summarise(
total_customer = n(),
total_monetary = sum(monetary),
total_frequency = sum(frequency),
min_frequency = min(frequency),
max_frequency = max(frequency),
avg_frequency = as.integer(median(frequency)),
min_monetary = min(monetary),
max_monetary = max(monetary),
avg_monetary = median(monetary)
)cluster_fm_summary <- df_segment_profile %>% mutate(
popup = glue("Total Customer: {total_customer} ({round((total_customer/sum(total_customer))*100,1)})%
Total Monetary: BRL {comma(total_monetary)} ({comma((total_monetary/sum(total_monetary))*100)}%)
Avg. Frequency: {avg_frequency} Order
Avg. Monetary: BRL {avg_monetary}"),
fm_segment = as.factor(fm_segment)
) %>%
ggplot(aes(fm_segment,avg_frequency)) +
geom_bar(aes(fill=fm_segment, text=popup),stat="identity", show.legend = FALSE)+
geom_text(aes(label=paste0('Avg: ',round(avg_frequency,0)),y=avg_frequency+0.8),size=3, color="black")+
#geom_text(aes(label=paste0(round((total_customer/sum(total_customer))*100,1),"% cust"),
# y=total_monetary+220000),size=3, color="black")+
labs(
title="Profiling: Average Frequency per Cluster",
x="Cluster",
y="Average Frequency")+
my_theme_fill()+
my_plot_theme(10)
ggplotly(cluster_fm_summary,tooltip="text") %>%
layout(showlegend=FALSE) cluster_fm_summary <- df_segment_profile %>% mutate(
popup = glue("Total Customer: {total_customer} ({round((total_customer/sum(total_customer))*100,1)})%
Total Monetary: £ {comma(total_monetary)} ({comma((total_monetary/sum(total_monetary))*100)}%)
Avg. Frequency: {avg_frequency} Order
Avg. Monetary: £ {comma(avg_monetary)}"),
fm_segment = as.factor(fm_segment)
) %>%
ggplot(aes(fm_segment,avg_monetary)) +
geom_bar(aes(fill=fm_segment, text=popup),stat="identity", show.legend = FALSE)+
geom_text(aes(label=paste0("£ ",comma(avg_monetary,1)),y=avg_monetary+500),size=3, color="black")+
labs(
title="Profiling: Average Monetary per Cluster",
x="Cluster",
y="Average Monetary"
)+
my_theme_fill()+
my_plot_theme(10)
ggplotly(cluster_fm_summary,tooltip="text") %>%
layout(showlegend=FALSE)Berdasarkan visualisasi profiling cluster diatas, maka setiap cluster bisa artikan sebagai berikut:
Berikut ini data hasil segmentasi:
df_cust_segment <- df_segment_result %>%
mutate(
fm_segment = case_when(fm_segment == "1" ~ "Medium Value",
fm_segment == "2" ~ "Low Value",
fm_segment == "3" ~ "High Value",
fm_segment == "4" ~ "Special Value"),
fm_segment = factor(fm_segment, levels=c("Low Value","Medium Value",
"High Value","Special Value")))
head(df_cust_segment,10)Berikut ini visualisasi hasil segmentasi
df_cust_segment %>% mutate(size_point=as.integer(fm_segment),size_point=size_point*10) %>%
plot_ly( x = ~frequency, y = ~monetary, color = ~as.factor(fm_segment), size=~size_point,
colors=c(my_theme_hex("col3"),my_theme_hex("col4"),my_theme_hex("col5"),my_theme_hex("col7")),
hoverinfo = 'text', text = ~paste("Customer ID: ", customer_id,
"<br>Value Segment: <b>",fm_segment,"</b>",
"<br>Frequency: <b>", frequency,"</b>",
"<br>Monetary: <b>£ ",monetary,"</b>")) %>%
#layout(margin = list(l = 10, r = 10, b = 10, t = 10)) %>%
config(displayModeBar = F) %>%
layout(font=list(size = 12)) %>%
layout(title="Customer Value Segmentation")Sebelumnya kita sudah melakukan segmentasi customer berdasarkan Recency, Frequency dan Monetary. Berikut ini summary hasil RFM Segmentation:
plot_tile_rfm_segmentation <- df_cust_segment %>%
group_by(recency_segment,fm_segment) %>%
summarise(freq = n(),
total_monetary=sum(monetary),
avg_monetary = round(mean(monetary)),
avg_frequency = median(frequency),
avg_age = median(age)) %>%
ungroup() %>%
mutate(
popup=glue("Total Customer: {freq}
RFM Segment : {toupper(recency_segment)} - {toupper(fm_segment)}
Avg. Frequency : {avg_frequency}
Avg. Monetary : £ {comma(avg_monetary)}
Avg. Age : {avg_age}")
) %>%
mutate(percent_freq = round((freq/sum(freq))*100,2)) %>%
ggplot(aes(recency_segment,fm_segment))+
geom_tile(aes(fill = percent_freq, text=popup), colour = "white", show.legend = FALSE) +
geom_text(aes(label=paste0(percent_freq,"%"),text=popup), size=4, color="white", show.legend = FALSE)+
labs(
y= NULL,
x= "Days since last purchase",
fill = "Avg. Monetary"
)+
scale_fill_gradient(low=my_theme_hex("col3") ,na.value = "#C0C0C0", high=my_theme_hex("col6"))+
theme(legend.title=element_text(size=9),
legend.position = "bottom")+
my_plot_theme(8)
ggplotly(plot_tile_rfm_segmentation, tooltip = "text")%>%
#layout(showlegend=FALSE, margin = list(l = 10, r = 10, b = 10, t = 10)) %>%
layout(showlegend=FALSE) %>%
config(displayModeBar = F) %>%
layout(title="RFM Segmentation Result")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:
ks <- function (x) { number_format(accuracy = 1,
scale = 1/1000000,
suffix = "M",
big.mark = ",")(x) }
ks_thousand <- function (x) { number_format(accuracy = 1,
scale = 1/1000,
suffix = "K",
big.mark = ",")(x) }
plot_fm_total_customer <- df_cust_segment %>% group_by(fm_segment) %>%
summarise(total_monetary = sum(monetary),
total_customer = n(),
avg_frequency = median(frequency),
avg_monetary = median(monetary)) %>%
ungroup() %>%
mutate(popup = glue("Total Customer: {total_customer} ({round((total_customer/sum(total_customer))*100,1)})%
Total Monetary: £ {comma(total_monetary)} ({comma((total_monetary/sum(total_monetary))*100)}%)")) %>%
ggplot(aes(fm_segment,total_customer)) +
geom_bar(aes(fill=fm_segment, text=popup),stat="identity", show.legend = FALSE)+
geom_text(aes(label=paste0(round(total_customer/sum(total_customer)*100,1),"%"),
y=total_customer+130),size=3, color="black")+
#geom_text(aes(label=paste0("C: ",round((total_customer/sum(total_customer))*100,1),"%"),
# y=total_monetary+280000), size=3, color="black")+
labs(
#title="Order Value by Freqmon Segment",
title = "Total Customer per Customer Value Segment",
x= NULL,
y="Total Customer"
)+
scale_y_continuous(labels = ks)+
coord_flip()+
my_theme_fill()+
my_plot_theme(8)
ggplotly(plot_fm_total_customer,tooltip="text") %>%
layout(showlegend=FALSE) %>%
config(displayModeBar = F) Chart di atas memberikan informasi total customer pada masing-masing segmen customer. Bisa dilihat bahwa 64.9% customer kita ada disegmen low value customer.
plot_recency_total_amount <- df_cust_segment %>% group_by(recency_segment) %>%
summarise(
total_customer = n(),
total_transaction = sum(frequency),
total_monetary = sum(monetary),
percent_monetary = round((total_monetary/sum(df_cust_segment$monetary))*100,1)
) %>%
ungroup() %>%
mutate(
popup = glue("Recency Segment : {recency_segment}
Total Customer : {comma(total_customer)}
Total Trasanction: {total_transaction}
Total Monetary: £ {comma(total_monetary)} ({percent_monetary}%)"),
recency_segment = factor(recency_segment, levels=c("inactive","cold","warm","active"))
) %>%
ggplot(aes(recency_segment,total_monetary))+
geom_bar(stat = "identity", aes(fill=recency_segment, text=popup), show.legend = FALSE)+
#geom_text(aes(label=paste0(comma(total_customer)," (",round((total_customer/sum(total_customer))*100,1),"%) Cust"),
# y=total_monetary+450000),size=3,color="black")+
geom_text(aes(label=paste0(round((total_monetary/sum(total_monetary))*100,1),"%"),y=total_monetary+260000), size=3)+
scale_y_continuous(labels = ks)+
labs(
title = "Total Monetary by Recency Segment",
x = NULL,
y = "Total Monetary"
)+
coord_flip()+
my_theme_fill()+
my_plot_theme(8)
ggplotly(plot_recency_total_amount,tooltip="text") %>%
layout(showlegend=FALSE) %>%
config(displayModeBar = F) 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.
plot_fm_total_monetary <- df_cust_segment %>% group_by(fm_segment) %>%
summarise(total_monetary = sum(monetary),
total_customer = n(),
avg_frequency = median(frequency),
avg_monetary = median(monetary)) %>%
ungroup() %>%
mutate(popup = glue("Total Customer: {total_customer} ({round((total_customer/sum(total_customer))*100,1)})%
Total Monetary: £ {comma(total_monetary)} ({comma((total_monetary/sum(total_monetary))*100)}%)")) %>%
ggplot(aes(fm_segment,total_monetary)) +
geom_bar(aes(fill=fm_segment, text=popup),stat="identity", show.legend = FALSE)+
geom_text(aes(label=paste0(round(total_monetary/sum(total_monetary)*100,1),"%"),
y=total_monetary+200000),size=3, color="black")+
#geom_text(aes(label=paste0("C: ",round((total_customer/sum(total_customer))*100,1),"%"),
# y=total_monetary+280000), size=3, color="black")+
labs(
#title="Order Value by Freqmon Segment",
title = "Total Monetary per Customer Value Segmen",
x= NULL,
y="Total Monetary"
)+
scale_y_continuous(labels = ks)+
coord_flip()+
my_theme_fill()+
my_plot_theme(8)
ggplotly(plot_fm_total_monetary,tooltip="text") %>%
layout(showlegend=FALSE) %>%
config(displayModeBar = F) 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.
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.
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:
Segmen Active : Fokus untuk meningkatkan pembelian customer sehingga perlu membentuk cross/Up Selling Strategy.
Segmen Warm : Fokus supaya customer melakukan pembelian kembali sehingga perlu membentuk Retention Strategy, Cross/Up Selling Strategy.
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.
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:
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.
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.
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.
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:
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.
Seorang customer mendapatkan diskon 35% terhadap produk yang sedang tren, sedangkan customer ini tidak tertarik dengan produk tersebut. Alhasil, diskon ini juga terabaikan.
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!