Latar Belakang

Dalam suatu industri yang memiliki produk yang dijual, transaksi jual beli sering terjadi. Transaksi tersebut terjadi baik diantara produsen dengan konsumen secara langsung, maupun produsen dengan distributor. Para distributor maupun konsumen tersebut akan dianggap sebagai customer. Customer merupakan hal yang esensial dalam suatu transaksi dan tentunya untuk kemajuan pada suatu industri tersebut. Adanya customer akan membuat industri dapat menjalankan roda bisnisnya dengan baik dan dapat menciptakan sebuah produk sesuai dengan kebutuhan customer.

Dalam mengolah hubungan antara customer dengan industri sendiri tentunya perlu dibuat sebuah strategi agar customer selalu loyal dan tetap membeli produk yang dijual. Oleh karenanya banyak sekali strategi yang digunakan mulai dari pemberian diskon produk, bonus produk setiap membeli berapa banyak produk, dan lain sebagainya. Agar strategi marketing untuk mempertahankan customer tidak salah sasaran, tentunya pemberian promosi ini perlu dipetakan berdasarkan value pada customer.

Deskripsi Data

Untuk melakukan analisa segmentasi customer berdasarkan ‘nilai’ yang mereka miliki, maka disini akan digunakan data sebuah transaksi perusahaan retail yang berbasis di United Kingdom. Data dapat diunduh melalui laman UCI Machine Learning. Data yang akan digunakan adalah data transaksi yang terjadi dari 01/12/2010 hingga 30/11/2011.

Read Data

Berikut ini gambaran dari dataset yang digunakan.

online_df <- read.csv("data_input/online_retail_II_2010-2011.csv", stringsAsFactors = F)
head(online_df, 10)

Data terdiri dari 541,910 observasi dan 8 variabel. Berikut ini gambaran jumlah baris dan kolomnya.

Note: Jumlah baris dan kolom ini merupakan jumlah baris data yang masih raw dan masih perlu dibersihkan terlebih dahulu menjadi data yang siap dianalisis.

Agar lebih seragam dalam penulisan nama kolom, maka akan diubah terlebih format penulisan masing-masing setiap kolom kedalam huruf kecil dan setiap kata dipisahkan oleh underscore.

colnames(online_df) <- tolower(colnames(online_df))
online_df <-   online_df %>% 
    rename("invoice_no" = invoice,
           "stock_code" = stockcode,
           "invoice_date" = invoicedate,
           "price" = price,
           "customer_id" = customer.id)
colnames(online_df)
#> [1] "invoice_no"   "stock_code"   "description"  "quantity"     "invoice_date"
#> [6] "price"        "customer_id"  "country"

Berikut penjelasan untuk masing-masing variabel yang ada:

Variable Description
invoice_no Merupakan nomor transaksi. Terdiri dari 6 digit angka yang bersifat unik setiap transaksi. Kemudian, jika nomor transaksi diawali huruf C maka artinya transaksi tersebut dicancel.
stock_code Merupakan kode produk yang terdiri dari 5 digit angka yang bersifat unik.
description Nama produk.
quantity Jumlah per produk yang dibeli setiap transaksi.
invoice_date Waktu transaksi.
price Harga setiap produk persetiap transaksi.
customer_id ID customer yang terdiri dari 5 digit angka yang bersifat unik setiap customer.
country Negara asal customer ketika melakukan transaksi.

Data Cleansing

Berdasarkan informasi yang terkandung dalam variabel tersebut, maka sebelumnya akan dipastikan semua data yang akan digunakan merupakan data transaksi yang valid. Oleh karena itu, akan coba dibersihkan terlebih dahulu datanya.

Tansaksi yang Tidak Valid

Berdasarkan informasi variabel invoice_no, quantity, unit_price, stock_code, dan customer_id dapat diketahui transaksi mana yang valid dan tidak. Berikut transaksi yang akan kita hapus berdasarkan transaksi yang tidak valid:

  • transaksi yang dibatalkan diawali oleh huruf C pada invoice_no
  • panjang karakter pada invoice yang valid terdiri dari 6 digit angka
  • transaksi yang memiliki quantity = 0
  • transaksi yang memiliki unit_price = 0, karena tidak ada keterangan lebih lanjut mengenai transaksi ini, maka akan dihapus
  • panjang karakter pada stock_code yang valid terdiri dari 5 digit angka
  • panjang karakter pada customer_id yang valid terdiri dari 5 digit angka
online_df_clean <- online_df %>% 
  filter(!str_detect(invoice_no, "C"), !nchar(invoice_no)>6, quantity > 0, price > 0) %>% 
  mutate(stock_code = toupper(stock_code),
         stock_code_nchar = nchar(stock_code)) %>% 
  filter(!stock_code_nchar %in% c(1:4), !stock_code %in% c("AMAZONFEE", "BANK CHARGES", "GIFT_0001_10",
                                                           "GIFT_0001_20", "GIFT_0001_20", "GIFT_0001_30",
                                                           "GIFT_0001_40","GIFT_0001_50")) %>% 
  select(-stock_code_nchar)

data.frame(n_observations = format(nrow(online_df_clean), big.mark = ","), n_columns = ncol(online_df_clean))

Transaksi Tidak Valid Berdasarkan Deskripsi Produk

Berdasarkan suatu artikel yang pernah dipublish oleh Diego Usai mengenai Market Basket Analysis berdasarkan suatu transaksi online, ditemukan 50 transaksi dengan deskripsi produk yang diinput secara manual. Produk tersebut dapat diindikasikan tidak valid.

descr <- c( "check", "check?", "?", "??", "damaged", "found", 
            "adjustment", "Amazon", "AMAZON", "amazon adjust", 
            "Amazon Adjustment", "amazon sales", "Found", "FOUND",
            "found box", "Found by jackie ","Found in w/hse","dotcom", 
            "dotcom adjust", "allocate stock for dotcom orders ta", "FBA", 
            "Dotcomgiftshop Gift Voucher £100.00", "on cargo order",
            "wrongly sold (22719) barcode", "wrongly marked 23343",
            "dotcomstock", "rcvd be air temp fix for dotcom sit", 
            "Manual", "John Lewis", "had been put aside", 
            "for online retail orders", "taig adjust", "amazon", 
            "incorrectly credited C550456 see 47", "returned", 
            "wrongly coded 20713", "came coded as 20713", 
            "add stock to allocate online orders", "Adjust bad debt", 
            "alan hodge cant mamage this section", "website fixed",
            "did  a credit  and did not tick ret", "michel oops",
            "incorrectly credited C550456 see 47", "mailout", "test",
            "Sale error",  "Lighthouse Trading zero invc incorr", "SAMPLES",
            "Marked as 23343", "wrongly coded 23343","Adjustment", 
            "rcvd be air temp fix for dotcom sit", "Had been put aside." )

online_df_clean %>% 
  filter(description %in% descr) %>% 
  arrange(stock_code)

Apabila diperhatikan berdasarkan deskripsi produk, produk dengan invoice_id 562113 merupakan sebuah gift voucher. Oleh karena itu akan dihapus.

Data yang Terduplikasi

Berdasarkan informasi variabel, stock_code bersifat unik. Oleh karena itu apabila terdapat stock_code dengan deskripsi yang sama, maka akan dihapus.

df_products <- online_df_clean %>% 
  arrange(desc(invoice_date)) %>% #to get the last named of stock code and description
  select(stock_code, description) %>% 
  distinct()

df_products %>% 
  group_by(stock_code) %>% 
  summarise(description_count=n()) %>% 
  ungroup() %>% 
  filter(description_count>1)

Terdapat 215 stock_code yang terduplikasi, maka akan dihapus. Pada case ini, akan disamakan terlebih dahulu deskripsi produk dengan transaksi terakhirnya.

df_products <- df_products %>% 
  group_by(stock_code) %>% 
  slice(1)

online_df_clean <- online_df_clean %>% left_join(df_products, by = c("stock_code" = "stock_code")) %>% 
  mutate(description=description.y) %>%
  select(-description.x,-description.y)

data_frame(
  stock_code_unique = online_df_clean$stock_code %>% unique() %>% length(),
  description_unique = online_df_clean$description %>% unique() %>% length(),
  stock_description_unique = online_df_clean %>% select(stock_code,description) %>% distinct() %>% nrow()
)

Selanjutnya, akan disamakan juga stock_code berdasarkan transaksi terakhir yang dilakukan.

df_description <- online_df_clean %>% 
  arrange(desc(invoice_date)) %>% #to get the last named of stock code and description
  select(stock_code,description) %>% 
  distinct()

df_description <- df_description %>% 
  group_by(description) %>% 
  slice(1)

online_df_clean <- online_df_clean %>% left_join(df_description, by = c("description" = "description")) %>% 
  mutate(stock_code=stock_code.y) %>% 
  select(-stock_code.x,-stock_code.y)

data_frame(
  stock_code_unique = online_df_clean$stock_code %>% unique() %>% length(),
  description_unique = online_df_clean$description %>% unique() %>% length(),
  stock_description_unique = online_df_clean %>% select(stock_code,description) %>% distinct() %>% nrow()
)

Selain itu, apabila ditinjau lebih lanjut, setiap customer seharusnya memiliki 1 negara. Apabila terdapat 1 customer memiliki lebih dari 1 negara, maka kemungkinan terdapat customer yang sudah pindah negara, maka data akan kita sesuaikan dengan transaksi di negara terakhir customer tersebut.

data_frame(
  customer_unik = online_df_clean %>% select(customer_id) %>% distinct() %>% nrow(),
  country_unik = online_df_clean %>% select(customer_id,country) %>% distinct() %>% nrow()
)
df_country <- online_df_clean %>% 
  arrange(desc(invoice_date,customer_id)) %>% #country that used at the last transaction.
  select(customer_id, country) %>% 
  group_by(customer_id) %>% 
  slice(1)

online_df_clean <- online_df_clean %>% select(-country) %>% 
  left_join(df_country, by = c("customer_id" = "customer_id"))


data_frame(
  customer_unik = online_df_clean %>% select(customer_id) %>% distinct() %>% nrow(),
  country_unik = online_df_clean %>% select(customer_id,country) %>% distinct() %>% nrow()
)
online_df_clean <- online_df_clean %>% distinct()

Missing Value

Untuk memastikan data memang benar-benar siap untuk digunakan analisis, maka perlu dipastikan juga adakah missing value pada data. Apabila dicek, terdapat 131,420 missing data pada kolom customer_id.

colSums(is.na(online_df_clean))
#>   invoice_no     quantity invoice_date        price  customer_id  description 
#>            0            0            0            0       131352            0 
#>   stock_code      country 
#>            0            0

Missing data tersebut akan dihapus agar tidak mengganggu proses analisa data nantinya.

online_df_clean <- online_df_clean %>% 
  filter(complete.cases(customer_id))

Penyesuaian Tipe Data dan Feature Engineering

Tipe data pada setiap variabel harus tepat agar proses pengambilan insight pada data juga tepat.

glimpse(online_df_clean)
#> Rows: 391,113
#> Columns: 8
#> $ invoice_no   <chr> "536365", "536365", "536365", "536365", "536365", "536365…
#> $ quantity     <int> 6, 6, 8, 6, 6, 2, 6, 6, 6, 6, 3, 3, 3, 32, 6, 6, 8, 6, 6,…
#> $ invoice_date <chr> "12/1/2010 8:26", "12/1/2010 8:26", "12/1/2010 8:26", "12…
#> $ price        <dbl> 2.55, 3.39, 2.75, 3.39, 3.39, 7.65, 4.25, 1.85, 1.85, 4.2…
#> $ customer_id  <int> 17850, 17850, 17850, 17850, 17850, 17850, 17850, 17850, 1…
#> $ description  <chr> "WHITE HANGING HEART T-LIGHT HOLDER", "WHITE METAL LANTER…
#> $ stock_code   <chr> "85123A", "71053", "84406B", "84029G", "84029E", "22752",…
#> $ country      <chr> "United Kingdom", "United Kingdom", "United Kingdom", "Un…

Pada hasil cek tipe data diatas, akan diubah beberapa kolom berikut sesuai dengan tipe data yang tepat:

  • invoice_date -> date
  • country -> factor

Selain itu akan dibuat kolom baru dengan nama total_price yang diperoleh dari perkalian anatara kolom quantity dan price.

online_df_clean <- online_df_clean %>% 
  mutate(invoice_date = as.Date(invoice_date, "%m/%d/%Y %H:%M"),
         country = as.factor(country),
         total_price = quantity*price)
head(online_df_clean, 10)

Terakhir, apabila diperhatikan pada data yang ada, transaksi dilakukan pada tanggal 01 Januari 2010 hingga 09 Desember 2011. Apabila memiliki kebutuhan untuk mengetahui rata-rata penjualan perbulan, sepertinya akan kurang adil jika data pada bulan Desember hanya 9 hari saja. Oleh karena itu, pada analisis ini, data pada bulan Desember akan dihapus terlebih dahulu. Selain itu, pada analisis ini, akan diambil customer yang melakukan pembelian hanya di United Kingdom saja.

range(online_df_clean$invoice_date)
#> [1] "2010-12-01" "2011-12-09"
df_customer <- online_df_clean %>% 
  filter(invoice_date < "2011-12-01", country == "United Kingdom")

Berikut hasil akhir data yang akan digunakan dalam analisis recency, frequency, dan monetary (RFM) berdasarkan transaksi pelanggan.

head(df_customer)

Exploratory Data Analysis

RFM Analysis

RFM Analysis merupakan salah satu metode analisis yang digunakan untuk segmentasi customer berdasarkan informasi kebiasaan dari customer itu sendiri. Adapun metrik yang akan dianalisa adalah sebagai berikut:

  1. Recency : berapa lama sejak customer melakukan pembelian.
  2. Frequency : seberapa sering customer melakukan transaksi.
  3. Monetary : jumlah pengeluaran customer dalam transaksi.

Metrik-metrik dalam RFM biasanya akan digunakan untuk mengetahui karakteristik dari suatu customer. Selain itu bisa digunakan juga untuk mengetahui value dari suatu customer. Apakaoh customer A termasuk customer yang loyal? Apakah customer A termasuk customer yang selalu melakukan transaksi yang cukup besar? dan lain sebagainya. Menggunakan metrik tersebut, akan dibuat suatu segmen customer berdasarkan nilainya yaitu high-value, medium-value, dan low-value.

Recency

Seperti yang sudah dijelaskan pada bagian sebelumnya, recency akan melihat berapa lama waktu customer dalam melakukan transaksi terakhirnya. Oleh karena itu untuk mendapatkan nilai recency, perlu diketahui terlebih dahulu tanggal terakhir customer melakukan transaksi. Selanjutnya akan diselisihkan hari terakhir customer melakukan transaksi dengan hari melakukan analisis. Sebagai asumsi, hari analisis diperoleh dari hari terakhir transaksi + 1.

  1. Mengetahui hari analisis
# hari analisis
date_analysis <- date(max(df_customer$invoice_date))+days(1)
date_analysis
#> [1] "2011-12-01"
  1. Mengetahui hari terakhir transaksi masing-masing customer
# hari terakhir transaksi customer
df_recency <- df_customer %>% 
  group_by(customer_id) %>% 
  summarise(first_order = min(invoice_date),
            last_order = max(invoice_date)) %>% 
  ungroup()
head(df_recency)
  1. Mengambil informasi recency customer
# recency

df_recency <- df_recency %>% 
  group_by(customer_id) %>% 
  summarise(recency = as.integer(date_analysis - last_order),
            age_cust = as.integer(date_analysis - first_order))
head(df_recency)

Apabila dilihat dari hasil tabel diatas, diketahui bahwa customer dengan customer_id 12346 sudah selama 317 hari yang lalu. Artinya customer 12346 sudah cukup lama tidak berbelanja pada toko tersebut. Sedangkan customer 12748 baru 2 hari yang lalu melakukan transaksi terakhir, artinya customer ini masih baru sekali berbelanja di toko tersebut.

Frequency

Untuk mengetahui seberapa sering customer melakukan transaksi, dapat dianalisa dari nilai frekuensi pembelanjaan dari masing-masing customer.

df_customer_rfm <- df_customer %>% 
  group_by(customer_id, invoice_no) %>% 
  summarise(freq = n()) %>% 
  ungroup() %>% 
  group_by(customer_id) %>% 
  summarise(avg_basket = round(median(freq))) %>% 
  ungroup()


df_frequency <- df_customer %>% 
  select(customer_id, invoice_no) %>% 
  distinct() %>% # mengambil informasi transaksi yang unik
  group_by(customer_id) %>% 
  summarise(frequency = n()) %>% 
  ungroup() %>% 
  left_join(df_customer_rfm, by = c("customer_id" = "customer_id"))


head(df_frequency)

Melihat customer 12346 yang sudah cukup lama berbelanja, jika dilihat dari seberapa sering berbelanja di toko tersebut juga hanya 1 kali, sangat tidak sering. Berbeda dengan customer 12748 sangat cukup sering berbelanja terlihat sudah sebanyak 197 kali berbelanja di toko tersebut.

Monetary

Selanjutnya yaitu monetary, seberapa banyak pengeluaran customer dalam setiap transaksi. Nilai monetary dapat diperoleh dari perkalian harga barang yang dibeli dengan jumlah barang yang dibeli dalam satu kali transaksi. Sebelumnya pernah dibuat kolom total_price yang menunjukkan hasil perkalian antraa price dan quantity. Langkah selanjutnya adalah menjumlahkan total_price dari masing-masing customer.

df_monetary <- df_customer %>% 
  group_by(customer_id) %>% 
  summarise(monetary = sum(total_price),
            avg_spend = median(total_price))
head(df_monetary)

Kembali lagi pada customer 12346 jika dilihat pernah melakukan satu kali transaksi dan sudah lama sekali tidak melakukan transaksi kembali namun dalam satu kali transaksi tersebut termasuk transaksi yang cukup besar dengan besaran pengeluarannya yaitu £77,183.60. Sedangkan jika melihat customer 12748 besaran pengeluaran yang dilakukan dalam 197 transaksi tersebut yaitu £30,652.63 dengan rata-rata pengeluarannya yaitu £3.75

Selanjutnya setiap informasi yang sudah diperoleh baik, recency, frequency dan monetary akan digabungkan dalam 1 dataframe agar mudah dilakukan analisis untuk segmentasi customer.

customer_rfm <- df_recency %>% 
  left_join(df_frequency) %>% 
  left_join(df_monetary) %>% 
  select(customer_id, age_cust, recency, frequency, monetary, avg_basket, avg_spend)
head(customer_rfm, 10)

Berikut penjelasan dari masing-masing variabel diatas:

Variable Description
customer_id ID customer
age_cust Usia customer, diperoleh dari selisih hari pertama bertransaksi dengan hari analisis
recency Selisih hari terakhir bertransaksi dengan hari analisis
frequency Jumlah transaksi yang dilakukan oleh customer
monetary Besar pengeluaran yang dilakukan oleh customer
avg_basket Rata-rata jumlah produk yang dibeli oleh customer per transaksi
avg_spend Rata-rata besar pengeluaran yang dilakukan customer per transaksi

Visualisasi RFM Analysis

Distribusi Usia Customer

Berdasarkan distribusi usia customee tersebut terlihat bahwa kebanyakan customer memiliki usiang 364 hari atau bisa dianggap 1 tahun. Hal ini cukup masuk akal karena data yang digunakan merupakan data transaksi satu tahun saja, sehingga usia customer terlama pun juga pada 364 hari.

Rata-rata Transaksi per Bulan

Berdasarkan transaksi per bulannya, diketahui bahwa transaksi paling rendah terjadi pada bulan Januari 2011 sedangkan transaksi tertinggi terjadi pada bulan November 2011.

Jenis Produk yang Sering Dibeli

Segmentasi Customer

Segmentasi customer kali ini akan membagi beberapa customer kedalam segmen high-value, medium-value, dan low-value.

Segmentasi Berdasarkan Recency

Recency merupakan salah satu metrik yang bisa digunakan untuk mengetahui nilai dari suatu customer. Tujuannya adalah agar ketika ingin memberikan suatu promosi terhadap customer tidak salah target dan tidak memperbesar biaya campaign. Berdasarkan nilai recency bisa diketahui customer yang masih aktif bertransaksi dan yang sudah tidak aktif. Pada kasus ini, akan dibuat sebuah nilai kuartil yang membagi customer berdasarkan recency dengan ketentuan sebagai berikut:

  1. Active : recency <= 16
  2. Slight Active : 16 < recency <= 49
  3. Slight Inactive : 49 < recency <= 147
  4. Inactive : recency > 147

Berdasarkan informasi diatas digambarkan proporsi customer berdasarkan transaksi terakhirnya. Misal perusahaan ingin memberikan campaign pada segmen customer diatas seperti:

  • Segmen Active merupakan segmen customer yang masih sering berbelanja, oleh karena itu bisa diarahkan campaign untuk meningkatkan volume pembelian seperti cross-selling atau up-selling.
  • Segmen Slight Active merupakan segmen customer yang sedikit sering berbelanja sehingga ada kemungkinan customer bisa churn, maka bisa diarahkan campaign untuk retention campaign atau bisa ditawarkan cross-selling.
  • Segmen Slight Inactive merupakan segmen customer yang sudah mulai tidak berbelanja dan akan sangat besar resikonya untuk churn, sehingga bisa diarahkan campaign untuk menarik hati konsumen tersebut kembali dengan memberikan promosi atau campaign untuk re-activation.
  • Segmen Inactive merupakan segmen customer yang sudah bisa disebut churn karena sudah sangat lama sebelum terakhi melakukan pembelian, maka untuk campaign yang dapat dilakukan adalah re-activation campaign.

Segmentasi Berdasarkan Frequency dan Monetary Menggunakan Clustering

Dua metrik frequency dan monetary memiliki jenis penilaian yang sama, yaitu customer yang bisa dianggap loyal adalah customer yang sering membeli dan pengeluaran setiap membeli produk cukup besar. Untuk dapat melakukan segmentasi customer yang bisa disebut high-value, medium-value, dan low-value, maka akan digunakan kedua metrik tersebut dengan cara melakukan clustering.

customer_rfm %>% 
  select(frequency, monetary) %>% 
  ggplot(aes(x = frequency, y = monetary))+
  geom_point()+
  geom_smooth(method = "lm", col = "firebrick")+
  labs(x = "Frequency", y = "Monetary", title = "Correlation between Frequency and Monetary")+
  scale_y_continuous(labels = label_number(prefix = "£ ", big.mark = ","))+
  theme_minimal()

Berdasarkan informasi pada plot tersebut diketahui bahwa kebanyakan customer yang sering melakukan transaksi, besaran transaksi yang dilakukan memiliki nilai yang cukup kecil. Berdasarkan informasi pada sebaran antara frequency dan monetary masing-masing customer diketahui bahwa terdapat banyak sekali beberapa nilai outlier. Artinya beberapa customer memiliki pengeluaran yang cukup besar sekali dan secara frekuensi berbelanja juga sangat sering sekali. Outlier pada data tersebut perlu dikeluarkan terlebih dahulu agar tidak mengganggu proses clustering.

library(FactoMineR)
library(factoextra)

fm_cust <- customer_rfm %>% select(customer_id, frequency, monetary) %>% 
  column_to_rownames(var = "customer_id")
fm_pca <- PCA(fm_cust, scale.unit = T, graph = F)
plot.PCA(fm_pca, select = "contrib30")

Diperoleh 20 data customer yang termasuk outlier yaitu sebagai berikut:

outlier_cust <- c("12346", "17511", "17450", "18102", "16029", "15311", "12971", "13089", "14606",
             "17841", "12748", "16684", "15769", "14298", "17949", "13694", "15061", "13408", 
             "16422", "13798")
             #, "15749", "15098", "12931", "16013", "15039", "14527", "14088", 
             #"17389", "15189", "14096")

Perlu dibuang terlebih dahulu dan bisa dijadikan satu kelompok sendiri dengan kriteria yang bisa juga berbeda.

customer_rfm_cluster <- customer_rfm %>% 
  filter(!customer_id %in% outlier_cust)
head(customer_rfm_cluster)

Selanjutnya dibuat clustering berdasarkan nilai frequency dan monetary. Metode clustering yang digunakan yaitu K-Means, dimana k-means akan mengelompokkan setiap data kedalam beberapa kelompok berdasarkan kedekatan jarak antar observasi. Semakin dekat jarak antar observasi dinilai semakin mirip karakteristiknya. Semakin jauh jarak antar observasi dinilai semakin berbeda karakteristiknya. K-means menggunakan perhitungan jarak euclidean distance yang mana secara formula mirip sekali dengan formula pytaghoras.

Pada kasus ini, karena setiap customer ingin dikelompokkan dalam 3 segmen, high, medium, dan low maka jumlah cluster yang akan dibentuk sebanyak 3.

set.seed(100)
cust_rfm_scale <- customer_rfm_cluster %>% 
  column_to_rownames(var = "customer_id") %>%
  select(-recency_segment) %>% 
  mutate_if(.predicate = is.numeric, scale)

cust_kmeans <- kmeans(cust_rfm_scale %>% select(frequency, monetary), centers = 3)
fviz_cluster(cust_kmeans, cust_rfm_scale %>% select(frequency, monetary), palette = "Set2")+
  labs(x = "Frequency", y = "Monetary")+
  theme_minimal()

Diperoleh hasil clustering berdasarkan variabel frequency dan monetary dimana cluster 1 digambarkan dengan warna hijau, cluster 2 digambarkan dengan warna biru, dan cluster 3 digambarkan dengan warna merah. Masing-masing cluster memiliki karakteristik sebagai berikut:

# data customer outlier
customer_outlier <- customer_rfm %>% 
  filter(customer_id %in% outlier_cust) %>% 
  mutate(fm_segment = 4)

# menggabungkan data customer outlier dengan hasil cluster
customer_rfm <- customer_rfm_cluster %>% 
  mutate(fm_segment = cust_kmeans$cluster) %>% 
  rbind.data.frame(customer_outlier) %>% 
  mutate(fm_segment = as.factor(fm_segment))

profiling_cluster <- customer_rfm %>% 
  group_by(fm_segment) %>% 
  summarise(min_monetary = min(monetary),
            max_monetary = max(monetary),
            avg_monetary = mean(monetary),
            med_monetary = median(monetary),
            min_freq = min(frequency),
            max_freq = max(frequency),
            avg_freq = mean(frequency),
            med_freq = median(frequency),
            min_recency = min(recency),
            max_recency = max(recency),
            avg_recency = mean(recency),
            med_recency = median(recency),
            avg_basket = mean(avg_basket),
            avg_spend = mean(avg_spend)
            ) %>% 
  ungroup()
profiling_cluster

Berdasarkan hasil profiling cluster diatas dapat diketahui bahwa cluster 4 memiliki nilai rata-rata recency paling rendah, nilai rata-rata frequency serta monetary paling tinggi karena cluster ini merupakan data-data outlier. Cluster 1 memiliki rata-rata recency paling rendah berdasarkan hasil clustering, dan frequency serta monetary paling tinggi. Sedangkan cluster 3 memiliki recency tidak terlalu rendah, nilai rata-rata frequency serta monetary tidak terlalu tinggi. Sedangkan cluster 2 memiliki rata-rata recency paling tinggi, namun frequency dan monetary paling rendah. Oleh karena itu, dapat disimpulkan bahwa hasil profiling cluster yang diperoleh adalah sebagai berikut:

  1. Cluster 1: High frequency-monetary, low recency -> High Value
  2. Cluster 2: Low frequency-monetary, very high recency -> Low Value
  3. Cluster 3: Medium frequency-monetary, medium recency -> Medium Value
  4. Cluster 4: Very high frequency-monetary, very low recency -> Special Value

Berikut gambaran sebaran masing-masing segmen customer berdasarkan sebaran frequency dan monetarynya.

customer_rfm <- customer_rfm %>% 
  mutate(fm_segment = factor(fm_segment, levels = c("4", "1", "3", "2"),
                             labels = c("Special Value", "High Value", 
                                        "Medium Value", "Low Value")))
plot_segment <- customer_rfm %>% 
  ggplot(aes(x = frequency, y = monetary, text = glue("Customer ID : {customer_id}
                                                      Customer Value : {fm_segment}
                                                      Frequency : {frequency}
                                                      Monetary : £ {comma(monetary)}")))+
  geom_point(aes(col = fm_segment), size = 3, alpha = 0.7)+
  scale_color_manual(values = c("#D68F91", "#B63639", "#ae2024", "#561011"))+
  labs(title = "Customer Value Segmentation", x = "Frequency", y = "Monetary")+
  theme_minimal()

ggplotly(plot_segment, tooltip = "text")

Customer Segmentation Berdasarkan RFM Analysis

Setelah dilakukan segmentasi berdasarkan informasi dari RFM, maka berikut hasil summary segmentasi customernya.

Dapat diketahui bahwa berdasarkan beberapa customer yang sudah melakukan transaksi, lebih banyak customer yang tergolong low-value dan sudah tidak aktif bertransaksi lagi. Selain itu juga customer yang terdapat pada United Kingdom kebanyakan merupakan low-value customer. Hal ini cukup riskan bagi perusahaan apabila memiliki customer yang lebih banyak masuk dalam segmentasi low-value dan inactive karena akan menurunkan performa penjualan dari perusahaan.

Apabila ditelusuri lebih lanjut besaran pendapatan yang diperoleh dalam hal ini akan menggunakan nilai monetary dari masing-masing segmen customer adalah sebagai berikut.

plot_total_monetary <- customer_rfm %>% 
  group_by(recency_segment) %>% 
  summarise(freq = n(),
            percentage_cust = round((freq/nrow(customer_rfm))*100,2),
            total_monetary = sum(monetary),
            percentage_monetary = round((total_monetary/sum(customer_rfm$monetary))*100,2)) %>%
  ungroup() %>%
  mutate(recency_segment = factor(recency_segment, levels = c("Inactive","Slight Inactive",
                                                              "Slight Active","Active"))) %>% 
  ggplot(aes(x = total_monetary, y = recency_segment, text = glue("Total Customer: {freq} ({percentage_cust})%
                                                                  Total Monetary: £ {comma(total_monetary)}"), label = glue("{percentage_monetary}%")))+
  geom_col(aes(fill = recency_segment))+
  scale_fill_manual(values = c("#D68F91", "#B63639", "#ae2024", "#561011"))+
  scale_x_continuous(labels = label_number(prefix = "£ ", big.mark = ","))+
  labs(title = "Total Monetary based on Recency Segment", y = NULL, x = "Total Monetary")+
  geom_text(aes(x = total_monetary+250000), hjust = -0.5)+
  theme_minimal()

ggplotly(plot_total_monetary, tooltip = "text")%>% 
  layout(showlegend=FALSE) %>% 
  config(displayModeBar = F)

Berdasarkan plot diatas dapat diketahui bahwa customer yang masih aktif memberikan besaran pembelian yang paling banyak, hal ini dapat diasumsikan bahwa customer yang aktif masih memberikan penjualan terbesar.

plot_total_monetary <- customer_rfm %>% 
  group_by(fm_segment) %>% 
  summarise(freq = n(),
            percentage_cust = round((freq/nrow(customer_rfm))*100,2),
            total_monetary = sum(monetary),
            percentage_monetary = round((total_monetary/sum(customer_rfm$monetary))*100,2)) %>%
  ungroup() %>%
  mutate(fm_segment = factor(fm_segment, levels = c("Low Value","Medium Value",
                                                         "High Value","Special Value"))) %>%
  ggplot(aes(x = total_monetary, y = fm_segment, text = glue("Total Customer: {freq} ({percentage_cust}%)
                                                                  Total Monetary: £ {comma(total_monetary)}"), label = glue("{percentage_monetary}%")))+
  geom_col(aes(fill = fm_segment))+
  scale_fill_manual(values = c("#D68F91", "#B63639", "#ae2024", "#561011"))+
  scale_x_continuous(labels = label_number(prefix = "£ ", big.mark = ","))+
  labs(title = "Total Monetary based on Customer Value", y = NULL, x = "Total Monetary")+
  geom_text(aes(x = total_monetary+250000), hjust = -0.5)+
  theme_minimal()

ggplotly(plot_total_monetary, tooltip = "text")%>% 
  layout(showlegend=FALSE) %>% 
  config(displayModeBar = F)

Sedangkan apabila dilihat dari value pada suatu customer, lebih banyak customer dengan segmen Low Value yang cenderung memberikan penjualan terbesar yaitu sebesar 35.75%. Sedangkan customer dengan segmen High Value memberikan penjualan terendah yaitu hanya sebesar 14.61% dari total penjualan yang ada.

Rekomendasi

Berdasarkan hasil segmentasi customer dapat diberikan rekomendasi dalam memastikan pelanggan tetap selalu loyal diantaranya:

  1. Sebesar ~15% dari customer yang tergolong Special-High-Medium menyumbang penjualan kurang lebih 64.25%, oleh karena itu agar tidak menyebabkan berkurangnya penjualan yang ada, proporsi resource dalam melakukan promosi lebih diperhatikan kembali agar loyalitas customer tetap terjaga.
  2. Pemberian program customer loyal dapat diberikan kepada customer yang tergolong Active-Slight Active serta yang masuk dalam Medium Value. Hal ini dapat me-maintain penjualan yang ada agar customer merasa bahwa keloyalitasan mereka diperhatikan oleh perusahaan.
  3. Para customer yang tergolong dalam Low apabila dilihat dari segi penjualan menjadi penyumbang terbanyak diantara segmen yang lain. Dalam hal ini, dapat diberikan promosi berupa diskon atau cashback ketika melakukan pembelian yang cukup banyak. Sehingga mereka akan memiliki kemungkinan untuk berpikir berbelanja kembali walau dari segi pembelian yang mereka lakukan tidak sering, namun pembelian mereka langsung banyak.
  4. Dalam menjalankan campaign nantinya akan lebih baik ketika memberikan promosi terhadap produk tertentu adalah produk yang memang sedang diinginkan oleh customer. Kebanyakan customer memiliki keinginan untuk membeli ketika promosi yang diberikan adalah promosi terhadap barang yang sedang mereka inginkan. Oleh karena itu, berikanlah promosi sesuai dengan produk yang diinginkan customer.