# clear-up the environment
rm(list = ls())

# chunk options
knitr::opts_chunk$set(
  message = FALSE,
  warning = FALSE,
  fig.align = "center",
  comment = "#>"
)

# scientific notation
options(scipen = 9999)

1 Pendahuluan

Materi ini diproduksi oleh tim dari Algoritma untuk DSS - Customer Segmentation Using PAM Algorithm. Materi berikut hanya ditujukan untuk kalangan terbatas, meliputi individu/personal yang menerima materi ini secara langsung dari lembaga pelatihan. Materi ini dilarang untuk direproduksi, didistribusikan, diterjemahkan, atau diadaptasikan dalam bentuk apapun di luar izin dari individu dan organisasi yang berkepentingan.

Algoritma adalah pusat pendidikan Data Science di Jakarta. Kami mengadakan workshop dan program pelatihan untuk membantu para profesional dan pelajar untuk mendapatkan keahlian dalam berbagai bidang dalam ruang lingkup Data Science: data visualization, machine learning, data modeling, statistical inference, dan lain-lainnya.

Sebelum masuk ke dalam materi dan menjalankan kode-kode di dalam materi ini, silakan anda melihat bagian Library and Setup untuk melihat dan memastikan semua persyaratan dasar untuk mengikuti materi ini sudah terpenuhi termasuk package-package yang diperlukan. Pada bagian Tujuan Pembelajaran anda dapat melihat secara umum apa saja yang akan dipelajari dalam modul materi ini. Kami harap materi ini akan bermanfaat bagi karir ataupun menambah keahlian peserta.

1.1 Apa itu Clustering?

Clustering adalah salah satu teknik Machine Learning, yang memiliki kemampuan untuk mengelompokkan kumpulan data yang tidak berlabel. Setiap data yang tidak berlabel nantinya akan diklasifikasikan ke dalam kelompok tertentu dimana, titik data yang berada dalam kelompok yang sama akan memiliki sifat atau fitur yang serupa, sedangkan titik data dalam kelompok yang berbeda diharapkan memiliki sifat atau fitur yang sangat berbeda.

Dalam ilmu data, kita dapat menggunakan clustering untuk mendapatkan beberapa wawasan berharga dari data kita dengan melihat hasil pengelompokan yang terjadi. Informasi yang berharga tersebut bisa kita bawa untuk membantu kita dalam menentukan segmentasi market, social netwrok analysis, dan masih banyak lagi. Di luar semua manfaat yang bisa kita dapatkan dari metode clustering, masih ada sebuah stigma yang menempel terhadap metode clustering. Pada artikel ini nantinya akan membahas metode clustering yang dapat digunakan untuk mengatasi stigma yang menempel terhadap metode clustering.

1.2 Stigma

Stigma yang menempel pada clustering adalah hanya bisa digunakan untuk data-data numerik. Hal tersebut terjadi karena banyak sekali implementasi atau contoh dari clustering yang hanya menggunakan data numerik.

Sesungguhnya clustering juga bisa digunakan terhadap data-data kategorikal, dan pada kesempatan kali ini kita akan mencoba menggunakan clustering untuk mengelompokan data kategorikal dan data numerik. Tipe algoritma yang akan kita gunakan nantinya bernama Partitioning Around Medoids dan metode perhitungan jarak yang akan digunakan adalah Gower Distance.

1.3 Tujuan Pembelajaran

Tujuan utama dari workshop ini adalah untuk memberikan pengenalan yang komprehensif mengenai tools dan perangkat lunak yang digunakan untuk melakukan sekementasi kustomer dengan clustering, yakni sebuah open-source populer: R. Adapun materi ini akan mencakup:

  • Dasar Bahasa Pemograman R
    • Perkenalan ke bahasa R
    • Perkenalan ke software RStudio
    • Pemprosesan data dan manipulasi data dengan R tidyverse
  • Pendahuluan ke Unsupervised Machine Learning
    • Perbedaan antara Supervised dan Unsupervised
    • Pengelompokan dengan metode clustering
  • Kasus Bisnis: Segmentasi Kustomer Supermarket
    • Perisapan data
    • Eksplorasi data
    • Segmentasi Kustomer
      • Perhitungan jarak dengan Gower Distance
      • Menentukan jumlah cluster yang paling optimum
      • Clustering dengan PAM
      • Interpretasi hasil Clustering

2 Dasar Bahasa Pemograman R

Sebelum itu mari kita coba berkenalan dengan R Studio UI, dengan melihat gambar di bawah ini.

2.1 Perkenalan ke bahasa R

R merupakan bahasa pemrograman di mana seperangkat instruksi akan diterjemahkan ke dalam bahasa komputer, sedangkan RStudio merupakan aplikasi tambahan yang dapat membantu pengguna R melakukan pekerjaannya.

2.1.1 Mengapa mempelajari R?

1. Dibangun oleh ahli statistik, untuk ahli statistik.

R adalah bahasa pemrograman statistik yang dibuat oleh Ross Ihaka dan Robert Gentleman di Departemen Statistik, di University of Auckland (Selandia Baru). R dibuat untuk analisis data, dan dengan demikian, berbeda dari bahasa pemrograman tradisional. R bukan hanya bahasa pemrograman statistik, R juga environment yang lengkap untuk analis data dan perangkat lunak analisis data yang paling banyak digunakan saat ini.

2. Memiliki banyak Library

R menyediakan banyak packages tambahan yang menambahkan fungsionalitas out-of-the-box untuk berbagai kegunaan: uji statistik, analisis deret waktu, visualisasi yang indah, dan berbagai tugas machine learning seperti algoritme regresi, algoritme klasifikasi, dan algoritme clustering. Komunitas R terkenal karena kontribusinya yang aktif dalam hal packages.

3. Sumber Terbuka

Bagian dari alasan komunitasnya yang aktif dan berkembang pesat adalah sifat sumber terbuka (open-source) dari R. Pengguna dapat berkontribusi dalam pembuatan packages, banyak tools statistik dan template kustomisasi untuk visualisasi yang tidak ditemukan dalam aplikasi statistik lain.

4. Digunakan oleh berbagai perusahaan perangkat lunak Terbesar di Dunia

R digunakan oleh Google untuk menghitung Return on Investment (ROI) dari berbagai iklan, dan seringkali digunakan untuk mengestimasi casual effect; seperti estimasi dampak dari sebuah fitur dari suatu aplikasi terhadap jumlah download dari aplikasi tersebut, ataupun peningkatan tingkat penjualan setelah mengeluarkan AdWords. Bahkan, Google merilis package R yang dapat digunakan oleh pengguna R lain untuk melakukan analisis serupa (lihat CausalImpact). Banyak pegawai di Google telah berkontribusi aktif terhadap komunitas pengguna R: mereka seringkali aktif dalam berbagai grup pengguna R; membuat interface untuk Google Prediction; membuat coding style versi Google untuk R, dan telah berkontribusi berbagai package untuk R.

Microsoft juga termasuk sebagai salah satu diantara perusahaan besar yang sangat bergantung pada R. Pada awalnya, Microsoft menggunakan R dalam: platform Azure–tepatnya sebagai capacity planning; sistem matchmaking pada Xbox’s TrueSkill; analisis churn untuk berbagai produk; dan beberapa internal services lain dalam Microsoft’s line of products. Langkah penting yang diambil oleh Microsoft dalam hal ini adalah akuisisi dari Revolution Analytics, yang terkenal atas berbagai produk perkembangan di R; yang sekarang lebih dikenal sebagai Microsoft R Server, Microsoft R Open, Microsoft Data Science Virtual Machine, dll.

5. Ready for Big Data

R dapat terintegrasi dengan tools lain dalam pengolahan big data, library seperti RHadoop, ParallelR, merupakan sebagian dari library yang mampu membantu data engineers untuk melakukan komputasi pararel di R.

2.2 Perkenalan ke R Studio

# Pada R Studio kita juga bisa memasukan gambar dengan menggunakan code di bawah ini
knitr::include_graphics("assets/RStudio_UI.PNG")

Terdapat 4 panel utama yang harus Anda pahami yaitu :

1. Panel Source : Panel ini merupakan fitur utama dari RStudio, panel ini menampilkan file yang sedang dibuka pada RStudio.

2. Panel Console : Panel ini menampilkan console asli dari R yang digunakan untuk berkomunikasi dengan R session. Terdapat beberapa tab lain seperti Terminal yang dapat digunakan untuk mengakses komputer Anda melalui Command Line Interface (CLI).

3. Panel Environment / History : Bagian ini menampilkan seluruh object R yang sudah dibuat selama session yang sama. Terdapat tab History yang berfungsi untuk melihat history dari kode yang sudah dijalankan sebelumnya.

4. Panel Files/Plot/Packages/Help :

  • Tab Files : Daftar dari berkas (file) yang berada dalam working directory.
  • Tab Plot : Menampilkan visualisasi yang terbentuk
  • Tab Packages : Berisi daftar packages yang sudah terinstall
  • Tab Help : Menampilkan dokumentasi resmi dari setiap fungsi

2.3 Library/Packages Preparation

Library/Package adalah sekumpulan fungsi yang digunakan untuk pengolahan data tertentu. Untuk menggunakan fungsi-fungsi pada suatu package, harus dilakukan instalasi package ke komputer terlebih dahulu.

Untuk menginstal packages, klik tab panel Packages pada bagian kanan bawah RStudio, kemudian klik tombol install. Masukan list package berikut ke dalam kolom package yang ingin di-instal (ragam package yang digunakan pada Trial Class). Untuk mendapatkan ilustrasi yang lebih jelas bisa melihat gambar di bawah ini.

knitr::include_graphics("assets/packages_installation.PNG")

Nantinya setelah klik tombol install akan muncul sebuah pop-up baru. Dari pop-up tersebut, kita dapat mengisi library apa saja yang ingin kita isntall pada bagian Packages. Untuk mendapatkan ilustrasi yang lebih jelas bisa melihat gambar di bawah ini.

knitr::include_graphics("assets/packages_installation2.PNG")

Berikut adalah library yang akan kita gunakan dalam melakukan membangun segmentasi kustomer.

  • Library untuk periapan dan eksplorasi data: tidyverse & Hmisc
  • Library untuk clustering: cluster & factorextra

Setelah berhasil melakukan instalasi library yang kita butuhkan, kita harus memanggil library tersebut terlebih dahulu. Untuk memanggil library tersebut kita akan menggunakan fungsi library().

library(tidyverse)

2.4 Laporan R Markdown

Pada materi ini, kita akan menggunakan file Rmarkdown (.Rmd). Rmarkdown merupakan package/tools yang digunakan untuk membuat report dengan kualitas tinggi. Pada folder materi ini terdapat file dengan ekstensi .html yang merupakan hasil knit dari Rmarkdown.

Untuk melakukan transformasi R Markdown menjadi format yang kita inginkan, kita bisa mengklik tombol knit dan memilih format file yang di-inginkan. Untuk ilustrasi yang lebih jelas, bisa melihat gambar di bawah ini.

2.5 Shortcut

Terdapat beberapa key shortcut yang akan memudahkan anda dalam menggunaan R. Beberapa diantaranya yaitu:

knitr::include_graphics("assets/OtherShortcut.PNG")

2.6 Pemprosesan data dan manipulasi data dengan R tidyverse

2.6.1 Membaca data

Kita akan menganalisis data retail.csv yang terdapat pada folder data. Gunakan fungsi read.csv() untuk membaca file CSV ke R, lalu simpanlah ke sebuah object dengan nama retail.

# Please type your code
retail <- read.csv("data/retail.csv")

Deskripsi kolom:

  • Order.ID = Unique ID pemesanan
  • Ship.Mode = Jenis pengiriman yang dipilih customer
  • Segment = Segmentasi/kategori customer
  • Category = Kateogri barang
  • Sub.Category = Sub Kategori barang
  • Product.Name = Nama produk barang
  • Sales = Total sales dari barang yang dibeli oleh customer
  • Quantity = Total barang yang dibeli oleh customer
  • Discount = Total diskon yang diberikan kepada customer
  • Profit = Total keuntungan yang didapatkan oleh perusahaan

Daripada melihat keseluruhan data, lebih baik kita “mengintip” sebagian baris yang dapat merepresentasikan bentuk keseluruhan data.

Fungsi head() untuk melihat beberapa baris teratas pada data (default 6)

# Please type your code
head(retail)

Fungsi tail() untuk melihat beberapa data terakhir.

# Please type your code
tail(retail)

2.6.2 Tipe data

Setelah berhasil memanggil data yang akan diolah, kita harus memerika tipe datanya terlebih dahulu. Tujuannya agar data tersebut bisa diolah lebih lanjut. Untuk menglihat tipe data pada data kita, kita dapat menggunakan fungsi glimpse().

# Please type your code
glimpse(retail)
#> Rows: 9,994
#> Columns: 10
#> $ Order.ID     <chr> "CA-2016-152156", "CA-2016-152156", "CA-2016-138688", "US…
#> $ Ship.Mode    <chr> "Second Class", "Second Class", "Second Class", "Standard…
#> $ Segment      <chr> "Consumer", "Consumer", "Corporate", "Consumer", "Consume…
#> $ Category     <chr> "Furniture", "Furniture", "Office Supplies", "Furniture",…
#> $ Sub.Category <chr> "Bookcases", "Chairs", "Labels", "Tables", "Storage", "Fu…
#> $ Product.Name <chr> "Bush Somerset Collection Bookcase", "Hon Deluxe Fabric U…
#> $ Sales        <dbl> 261.9600, 731.9400, 14.6200, 957.5775, 22.3680, 48.8600, …
#> $ Quantity     <int> 2, 3, 2, 5, 2, 7, 4, 6, 3, 5, 9, 4, 3, 3, 5, 3, 6, 2, 2, …
#> $ Discount     <dbl> 0.00, 0.00, 0.00, 0.45, 0.20, 0.00, 0.00, 0.20, 0.20, 0.0…
#> $ Profit       <dbl> 41.9136, 219.5820, 6.8714, -383.0310, 2.5164, 14.1694, 1.…

Terdapat berbagai macam tipe data di R, berikut intuisi dari setiap tipe data tersebut.

# character
a_char <- c("Algoritma", "Indonesia", "e-Commerce", "marketing")

# factor
a_factor <- factor(c("AB", "O", "B", "A", "B", "AB", "O"))

# numeric
a_num <- c(-1, 1, 2, 3/4, 0.5)

# integer
an_int <- c(1L, 2L)

# date
a_date <- c("24/Jan/2019", "10-12-1994")

# logical
a_log <- c(TRUE, TRUE, FALSE)
  • Tipe data character : Tipe data yang berisi huruf/karakter.
  • Tipe data factor : Factor merupakan bentuk perkembangan dari character, yang membedakan character dan factor adalah objek factor memiliki levels dan pengulangan. Digunakan untuk tipe data kategorikal.
  • Tipe data numeric/dbl: Tipe data berisi angka yang dapat berupa angka kontinu (ada koma/pecahan), maupun diskrit (bilangan bulat tanpa koma).
  • Tipe data integer: Tipe data berisi angka yang berupa angka diskrit (bilangan bulat tanpa koma). Untuk memaksa numeric menjadi integer, dapat gunakan L dibelakang angka.
  • Tipe data logical: Tipe data yang hanya berisi TRUE atau FALSE. Penulisan TRUE/FALSE dapat disingkat menjadi T/F.

Cara untuk mengetahui tipe data dari suatu objek, Anda dapat menggunakan fungsi class()

class(a_char)
#> [1] "character"

Lalu, apa yang akan terjadi jika dalam satu data memiliki beberapa tipe data yang berbeda seperti chunk dibawah ini?

mix <- c("Algoritma", 2022, TRUE)
class(mix)
#> [1] "character"

Bila Anda perhatikan setiap nilai pada object mix memiliki petik dua, artinya nilai tersebut merupakan sebuah objek dengan tipe character. Proses perubahan paksa dari suatu vector bisa disebut sebagai implicit coercion. Ilustrasi terjadinya implicit coercion dapat dilihat pada gambar di bawah ini:

knitr::include_graphics("assets/data_type.png")

2.6.3 Persiapan & Eksplorasi Data

Pada tahapan persiapan dan eksplorasi, kita akan memanfaatkan library(tidyverse) untuk memudahkan.

  • Mengubah tipe data

Persiapan yang akan kita lakukan pada data kita adalah mengubah tipe data yang belum sesuai. Untuk mengubah tipe data, kita akan menggunakan fungsi mutate()

Simbol %>% digunakan untuk menyambungkan proses yang sequential atau berurutan dari setiap fungsi yang kita gunakan.

# Please type your code
retail_clean <- retail %>% 
  mutate_if(is.character, as.factor)

Untuk memastikan apakah tipe dataya sudah berhasil terubah, mari kita coba cek sekali lagi dengan menggunakan fungsi glimpse()

# Please type your code
glimpse(retail_clean)
#> Rows: 9,994
#> Columns: 10
#> $ Order.ID     <fct> CA-2016-152156, CA-2016-152156, CA-2016-138688, US-2015-1…
#> $ Ship.Mode    <fct> Second Class, Second Class, Second Class, Standard Class,…
#> $ Segment      <fct> Consumer, Consumer, Corporate, Consumer, Consumer, Consum…
#> $ Category     <fct> Furniture, Furniture, Office Supplies, Furniture, Office …
#> $ Sub.Category <fct> Bookcases, Chairs, Labels, Tables, Storage, Furnishings, …
#> $ Product.Name <fct> "Bush Somerset Collection Bookcase", "Hon Deluxe Fabric U…
#> $ Sales        <dbl> 261.9600, 731.9400, 14.6200, 957.5775, 22.3680, 48.8600, …
#> $ Quantity     <int> 2, 3, 2, 5, 2, 7, 4, 6, 3, 5, 9, 4, 3, 3, 5, 3, 6, 2, 2, …
#> $ Discount     <dbl> 0.00, 0.00, 0.00, 0.45, 0.20, 0.00, 0.00, 0.20, 0.20, 0.0…
#> $ Profit       <dbl> 41.9136, 219.5820, 6.8714, -383.0310, 2.5164, 14.1694, 1.…
  • Eksplorasi Data

Exploratory Data Analysis bertujuan untuk mendapatkan insight dari data sesuai pertanyaan bisnis yang diajukan. Dari data retail, kita akan coba menjawab pertanyaan bisnis berikut:

Q1: Bagaimanakah performa penjualan (Sales) untuk setiap category (Category) barang yang dijual pada perusahaan retail tersebut?

Berikut adalah beberapa fungsi dari library dplyr yang akan kita gunakan untuk menjawab pertanyaan bisnis tersebut.

  • group_by(): Fungsi ini dapat kita manfaatkan untuk mengelompokkan setiap segment barang pada data kita.
  • summarise(): Fungsi ini dapat kita manfaatkan untuk melakukan perhitungan matematis, untuk melakukan penjumlahan hasil penjualan untuk setiap segment barang yang dimiliki.
  • arrange(desc()): Fungsi ini dapat kita manfaatkan untuk mengurutkan nilai yang ada
# Please type your code
retail %>% 
  group_by(Category) %>% 
  summarise(Total_Sales = sum(Sales)) %>% 
  arrange(desc(Total_Sales))

Q2: Berapa jumlah kustomer yang menggunakan mode pengiriman barang (Ship.Mode) dengan metode First Class?

  • fitler(): Fungsi ini digunakan untuk memberikan sebuah kondisi tertentu
unique(retail_clean$Ship.Mode)
#> [1] Second Class   Standard Class First Class    Same Day      
#> Levels: First Class Same Day Second Class Standard Class
# Please type your code
retail_clean %>% 
  filter(Ship.Mode == "First Class") %>% 
  summarise(Total = n())

3 Topik bisnis: Customer Segmentation

Dalam mempelajari algoritma PAM dan perhitungan jarak Gower Distance, kita akan membuat sebuah segmentasi kustomer berdasarkan data historis pengunjung Supermarket.

Background/Motivational Story:

Dalam mempelajari algoritma dan metode tersebut, kita akan mengangkat sebuah kasus bisnis yaitu membangun segemntasi kustomer. Segmentasi kustomer merupakan sebuah tahapan yang dilakukan oleh peruhsaan untuk mengelompokan dan melihat profile/karakteristik kustomer pada setiap kelompoknya.

Dari setiap informasi karakteristik kustomer pada setiap kelompoknya, akan membantu perusahaan tersebut dalam berkomunikasi lebih baik terhadap semua kustomer. Komunikasi di sini maksudnya adalah bagaimana perusahaan dapat memberikan promosi yang lebih sesuai dengan setiap karakteristik yang terdapat pada setiap kelompoknya.

Jika komunikasi dapat berlajalan dengan baik, perushaan akan meningkatkan kepuasan dari pelanggan, mempersingkat waktu yang dibutuhkan untuk closing penjualan, sampai meningkatkan retensi pelanggan.

3.1 Library

# Data Wrangling
library(tidyverse)
library(Hmisc)

# Machine Learning - Clustering 
library(cluster)
library(factoextra)

# Visualization
library(Rtsne)

3.2 Dataset

Data yang akan kita gunakan adalah data historikal pembelian kustomer pada sebuah toko selama 6 bulan terakhir, dari data tersebut nantinya akan coba kita kelompokan untuk membuat beberapa segmentasi kustomer.

# Please run the code down below
cust_purchase <- read.csv("data/ifood_customer.csv") 

head(cust_purchase)

Deskripsi kolom:

  • ID: unique ID customer
  • Year_Birth: Tahun kelahiran customer
  • Education: Pendidikan terakhir customer (Graduation, PhD, Undergraduate, Master)
  • Marital_Statuts: Status pernikahan customer (Single, Maried, Widow)
  • Income: Penghasilan customer dalam 1 tahun
  • Kidhome: Jumlah anak kecil yang terdapat pada rumah customer
  • Teenhome: Jumalah anak remaja yang terdapat pada rumah customer
  • MntWines: Jumlah pengeluaran untuk produk wine dalam 6 bulan terakhir
  • MntFruits: Jumlah pengeluaran untuk produk buah-buahan dalam 6 bulan terakhir
  • MntMeatProducts: Jumlah pengeluaran untuk product daging merah dalam 6 bulan terakhir
  • MntFishProducts: Jumlah pengeluaran untuk product ikan dalam 6 bulan terakhir
  • NumDealsPurchases: Jumlah transaksi yang dilakukan customer ketika terdapat diskon yang ditawarkan
  • NumWebPurchases: Jumlah transaksi yang dilakukan customer melalui website
  • NumAppPurchases: Jumlah transaksi yang dilakukan customer melalui applikasi
  • NumStorePurchases: Jumlah transaksi yang dilakukan customer secara offline atau data ke store
  • Country: Kebangsaan customer tersebut

Role Play Bisnis Question

Kita selaku tim Data dari Supermarket, diminta tolong oleh tim marketing untuk membuat segmentasi customer dari keseluruhan customer yang terdaftar karena tim marketing merasa khawatir jika promo dibuat secara general atau sama rata antar setiap customer, promo tersebut menjadi sia-sia. Harapan dari tim marketing dengan adanya segmentasi kustomer, promo yang dipersipkan akan membantu meningkatkan penjualan dan awareness customer terhadap product ataupun plaform penjualan yang sudah tersedia.

3.3 Data Wrangling

Wrangling 1: Apakah kolom income perlu kita hilangkan simbol dolarnya, agar dapat kita olang sebagai tipe data numeric?

Jika kita harus mengubahnya maka kita coba manfaatkan fungsi str_replace_all()

# Please run the code down below
cust_purchase$Income <- cust_purchase$Income %>% 
  str_replace_all(pattern = "\\$", 
                  replacement = "") %>% 
  str_replace_all(pattern = ",",
                   replacement = "") 

cust_purchase %>% 
  head()

Wrangling 2: Apakah kita perlu membuat kolom baru yang berisikan umur customer, agar lebih intuitif?

# Please run the code down below
cust_purchase <- cust_purchase %>% 
  mutate(age = 2022 - Year_Birth)

cust_purchase %>% 
  head()

Wrangling 3: Apakah kolom ID dan Year_Birth perlu kita buang?

  • ID: ID perlu dibuang karena nantinya dapat mempengaruhi perhitungan dan pembuatan kelompok yang ada
  • Year_Birth: Karena kita sudah memiliki kolom baru yaitu age yang lebih intuitif, maka kita bisa menghilangkan kolom Year_Birth agar tidak ada informasi yang berulang.

Wrangling 4: Apakah terdapat kolom yang belum sesuai tipe datanya?

cust_purchase %>% 
  glimpse()
#> Rows: 1,000
#> Columns: 19
#> $ ID                <int> 1826, 1, 10476, 1386, 5371, 7348, 4073, 1991, 4047, …
#> $ Year_Birth        <int> 1970, 1961, 1958, 1967, 1989, 1958, 1954, 1967, 1954…
#> $ Education         <chr> "Graduation", "Graduation", "Graduation", "Graduatio…
#> $ Marital_Status    <chr> "Widow", "Single", "Married", "Married", "Single", "…
#> $ Income            <chr> "84835.00 ", "57091.00 ", "67267.00 ", "32474.00 ", …
#> $ Kidhome           <int> 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0…
#> $ Teenhome          <int> 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1…
#> $ MntWines          <int> 189, 464, 134, 10, 6, 336, 769, 78, 384, 384, 450, 1…
#> $ MntFruits         <int> 104, 5, 11, 0, 16, 130, 80, 0, 0, 0, 26, 4, 82, 10, …
#> $ MntMeatProducts   <int> 379, 64, 59, 1, 24, 411, 252, 11, 102, 102, 535, 61,…
#> $ MntFishProducts   <int> 111, 7, 15, 0, 11, 240, 15, 0, 21, 21, 73, 0, 80, 3,…
#> $ MntSweetProducts  <int> 189, 0, 2, 0, 0, 32, 34, 0, 32, 32, 98, 13, 20, 16, …
#> $ MntGoldProds      <int> 218, 37, 30, 0, 34, 43, 65, 7, 5, 5, 26, 4, 102, 32,…
#> $ NumDealsPurchases <int> 1, 1, 1, 1, 2, 1, 1, 1, 3, 3, 1, 2, 1, 1, 0, 4, 4, 1…
#> $ NumWebPurchases   <int> 4, 7, 3, 1, 3, 4, 10, 2, 6, 6, 5, 3, 3, 1, 25, 2, 2,…
#> $ NumAppPurchases   <int> 4, 3, 2, 0, 1, 7, 10, 1, 2, 2, 6, 1, 6, 1, 0, 1, 1, …
#> $ NumStorePurchases <int> 6, 7, 5, 2, 2, 5, 7, 3, 9, 9, 10, 6, 6, 2, 0, 5, 5, …
#> $ Country           <chr> "SP", "CA", "US", "AUS", "SP", "SP", "GER", "SP", "U…
#> $ age               <dbl> 52, 61, 64, 55, 33, 64, 68, 55, 68, 68, 75, 43, 63, …
  • Marital_Status -> Factor
  • Country -> Factor
  • Education -> Factor
  • Income -> Integer
# Please run the code down below
cust_purchase_clean <- cust_purchase %>% 
  select(-ID, -Year_Birth) %>% 
  mutate(Marital_Status = as.factor(Marital_Status),
         Country = as.factor(Country),
         Education = as.factor(Education),
         Income = as.integer(Income))
glimpse(cust_purchase_clean)
#> Rows: 1,000
#> Columns: 17
#> $ Education         <fct> Graduation, Graduation, Graduation, Graduation, Grad…
#> $ Marital_Status    <fct> Widow, Single, Married, Married, Single, Single, Mar…
#> $ Income            <int> 84835, 57091, 67267, 32474, 21474, 71691, 63564, 449…
#> $ Kidhome           <int> 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0…
#> $ Teenhome          <int> 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1…
#> $ MntWines          <int> 189, 464, 134, 10, 6, 336, 769, 78, 384, 384, 450, 1…
#> $ MntFruits         <int> 104, 5, 11, 0, 16, 130, 80, 0, 0, 0, 26, 4, 82, 10, …
#> $ MntMeatProducts   <int> 379, 64, 59, 1, 24, 411, 252, 11, 102, 102, 535, 61,…
#> $ MntFishProducts   <int> 111, 7, 15, 0, 11, 240, 15, 0, 21, 21, 73, 0, 80, 3,…
#> $ MntSweetProducts  <int> 189, 0, 2, 0, 0, 32, 34, 0, 32, 32, 98, 13, 20, 16, …
#> $ MntGoldProds      <int> 218, 37, 30, 0, 34, 43, 65, 7, 5, 5, 26, 4, 102, 32,…
#> $ NumDealsPurchases <int> 1, 1, 1, 1, 2, 1, 1, 1, 3, 3, 1, 2, 1, 1, 0, 4, 4, 1…
#> $ NumWebPurchases   <int> 4, 7, 3, 1, 3, 4, 10, 2, 6, 6, 5, 3, 3, 1, 25, 2, 2,…
#> $ NumAppPurchases   <int> 4, 3, 2, 0, 1, 7, 10, 1, 2, 2, 6, 1, 6, 1, 0, 1, 1, …
#> $ NumStorePurchases <int> 6, 7, 5, 2, 2, 5, 7, 3, 9, 9, 10, 6, 6, 2, 0, 5, 5, …
#> $ Country           <fct> SP, CA, US, AUS, SP, SP, GER, SP, US, IND, US, SP, I…
#> $ age               <dbl> 52, 61, 64, 55, 33, 64, 68, 55, 68, 68, 75, 43, 63, …

3.4 Eksplorasi Data

Pada tahapan eksplorasi data, kita akan mencoba untuk lebih mengenali data yang kita miliki.

EDA 1: Melihat apakah ada missing value

# Please run the code down below
cust_purchase_clean %>% 
  anyNA()
#> [1] FALSE
cust_purchase_clean %>% 
  is.na() %>% 
  colSums()
#>         Education    Marital_Status            Income           Kidhome 
#>                 0                 0                 0                 0 
#>          Teenhome          MntWines         MntFruits   MntMeatProducts 
#>                 0                 0                 0                 0 
#>   MntFishProducts  MntSweetProducts      MntGoldProds NumDealsPurchases 
#>                 0                 0                 0                 0 
#>   NumWebPurchases   NumAppPurchases NumStorePurchases           Country 
#>                 0                 0                 0                 0 
#>               age 
#>                 0

EDA 2: Melakukan 5 number summary

# Please run the code down below
cust_purchase_clean %>% 
  summary()
#>          Education   Marital_Status     Income          Kidhome     
#>  Graduation   :488   Married:647    Min.   :  2447   Min.   :0.000  
#>  Master       :168   Single :214    1st Qu.: 34476   1st Qu.:0.000  
#>  PhD          :219   Widow  :139    Median : 50442   Median :0.000  
#>  Undergraduate:125                  Mean   : 52405   Mean   :0.439  
#>                                     3rd Qu.: 69109   3rd Qu.:1.000  
#>                                     Max.   :666666   Max.   :2.000  
#>                                                                     
#>     Teenhome        MntWines        MntFruits      MntMeatProducts 
#>  Min.   :0.000   Min.   :   0.0   Min.   :  0.00   Min.   :   0.0  
#>  1st Qu.:0.000   1st Qu.:  22.0   1st Qu.:  1.00   1st Qu.:  14.0  
#>  Median :0.000   Median : 167.0   Median :  7.00   Median :  64.0  
#>  Mean   :0.489   Mean   : 296.5   Mean   : 26.78   Mean   : 165.2  
#>  3rd Qu.:1.000   3rd Qu.: 488.0   3rd Qu.: 32.00   3rd Qu.: 223.0  
#>  Max.   :2.000   Max.   :1478.0   Max.   :199.00   Max.   :1725.0  
#>                                                                    
#>  MntFishProducts  MntSweetProducts  MntGoldProds    NumDealsPurchases
#>  Min.   :  0.00   Min.   :  0.00   Min.   :  0.00   Min.   : 0.000   
#>  1st Qu.:  2.75   1st Qu.:  1.00   1st Qu.:  9.00   1st Qu.: 1.000   
#>  Median : 12.00   Median :  8.00   Median : 23.00   Median : 2.000   
#>  Mean   : 37.37   Mean   : 25.49   Mean   : 43.00   Mean   : 2.287   
#>  3rd Qu.: 49.25   3rd Qu.: 29.25   3rd Qu.: 51.25   3rd Qu.: 3.000   
#>  Max.   :259.00   Max.   :262.00   Max.   :321.00   Max.   :15.000   
#>                                                                      
#>  NumWebPurchases  NumAppPurchases  NumStorePurchases    Country   
#>  Min.   : 0.000   Min.   : 0.000   Min.   : 0.000    SP     :479  
#>  1st Qu.: 2.000   1st Qu.: 0.000   1st Qu.: 3.000    SA     :145  
#>  Median : 3.000   Median : 1.000   Median : 5.000    CA     :131  
#>  Mean   : 4.092   Mean   : 2.592   Mean   : 5.791    AUS    : 78  
#>  3rd Qu.: 6.000   3rd Qu.: 4.000   3rd Qu.: 8.000    IND    : 63  
#>  Max.   :27.000   Max.   :28.000   Max.   :13.000    US     : 52  
#>                                                      (Other): 52  
#>       age        
#>  Min.   : 26.00  
#>  1st Qu.: 44.00  
#>  Median : 51.00  
#>  Mean   : 52.65  
#>  3rd Qu.: 62.00  
#>  Max.   :129.00  
#> 

Insight:

  • Salah satu hal yang cukup menarik disini adalah terdapat customer yang umurnya sampai 129 tahun, hal tersebut bisa saja terjadi karena customer “mengasal” mengisi pada bagian kolom Year_Birth. Kita akan mencoba untuk menganalisa lebih medalam lagi pada bagian ini.
  • Nilai maksimal dari gaji customer kita terdapat nilai yang sangat tinggi sampai dengan USD 666.666 dalam satu tahunnya. Kkita juga bisa menelahaah lebih lanjut untuk gaji para customer kita.

Further EDA:

  • Further EDA Age
boxplot_age <- boxplot(cust_purchase_clean$age)

boxplot_age$out
#> [1] 129 123

Dari hasil boxplot, kita dapat melihat bahwa terdapat 2 outlier pada data customer kita. Dikarenakan 2 umur tersebut dianggap sebagai outlier dan dirasa kurang bijak jika kita memanipulasi ketiga data tersebut, salah satu approach yang dapat kita lakukan adalah menghilangkan ketiga data tersebut. Sehinga ketiga data tersebut tidak menjadi noise pada pembuatan cluster nantinya.

  • Further EDA Income
boxplot_income <- boxplot(cust_purchase_clean$Income)

boxplot_income$out
#> [1] 157146 160803 666666 162397 157733

Kita akan melihat karakteristik dari customer yang memiliki income sebesar 666,666 USD

cust_purchase_clean %>% 
  filter(Income == 666666)

Furthen Data Wrangling:

cust_purchase_cleaner <- cust_purchase_clean %>% 
  filter(Income < 666666, age < 122) 

Dari hasil analisa outliers kita dapat melihat bahwa ada sebuah customers yang memiliki gaji yang sangat tinggi, bahkan diantara data outliers customer tersebut memiliki nilai income yang lebih dari 500rb USD. Pada data customer tersebut, kita juga akan menghilangkan data tersebut, karena bisa menjadi noise ketika kita ingin melakukan clustering.

Sebagai tambahan untuk data dengan tipe numerik kita bisa melakukan visualiasi dengan plot histogram untuk melihat jumlah observasinya. Untuk melelihatnya dengan visualisasi histogram, kita tidak perlu membuatnya satu per satu, melainkan kita bisa menggunakan fungsi hist.data.frame() dari library(Hmisc).

# Please run the code down below
cust_purchase_cleaner %>% 
  select_if(is.numeric) %>% 
  hist.data.frame()

cust_purchase_cleaner %>% 
  select_if(is.factor) %>% 
  hist.data.frame()

Additional Insight:

3.5 Clustering

3.5.1 K-Means Vs Partisioning Around Medoids

Algoritma Partitioning Around Medoids(PAM) atau bisa disebut K-Medoids adalah sebuah metode pengelompokan non-hierarki yang masih menjadi keluarga dekat dari metode K-Means. Metode PAM atau K-Medoids ini akan mengelompokan sekumpulan n objek menjadi ke beberapa k cluster. Jika metode pengelompokannya seperti itu, apa bedanya dengan metode K-Means kalau begitu? Perbedaanya terdapat pada bagaimana melakukan pengelompokannya.

Pada metode K-Means, penentuan sebuah objek masuk ke sebuah cluster tertentu berdasarkan perhitungan rata-rata jarak ecluidean. Sedangkan cara kerja metode K-Medoids akan diawali dengan penentuan Medoids (sebuah objek yang letaknya terpusat di dalam suatu cluster). Dari gambar di bawah, kita dapat melihat bagaimana perbedaan antara metode K-Means & K-Medoids dalam menentukan sebuah objek lebih sesuai untuk masuk ke sebuah cluster tertentu.

  • Intuisi penentuan medoids pada PAM/K-Medoids:
knitr::include_graphics("assets/K-Medoids_Clustering.gif")

Penentuan Medoids itu tidak dilakukan hanya sekali, melainkan berulang sampai Medoids itu tidak bisa bergerak lagi. Mengapa tidak hanya sekali, dikarenakan peletakan Medoids belum tentu posisi paling optimal. Untuk mendapatkan posisi yang paling optimal nantinya posisi Medoids awal akan dihitung ulang total simpangannya dengan menggunakan rumus berikut di bawah ini.

\[ S = b - a \] Dimana:

  • a adalah cost jarak terdekat antara objek ke medoid awal
  • b adalah cost jarak terdekat antara objek ke medoid baru

Jika S < 0, maka tukar obyek dengan data untuk membentuk sekumpulan k baru sebagai Medoid

Langkah tersebut akan terus diulang sampai nantinya S > 0, yang berarti tidak adanya pergerakan lagi dari Medoid yang dipilih sebagai titik tengah. Dari titik Medoids yang paling optimum itu, akan dicari lagi objek yang menyerupai dari karakteristik Medoids tersebut. Supaya lebih terbayang dengan cara kerja dari metode PAM, kita bisa melihat contoh penentuan manual di bawah ini

Contoh penentuan dan perhitungan:

Supaya lebih terbayang bagaimana hasil dari metode PAM menentukan medoidsnya, mari kita coba lakukan perhitungan secara manual terlebih dahulu untuk 2 kelompok

df <- data.frame(row = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L),
                 X = c(8L, 3L, 4L, 9L, 8L, 5L, 7L, 8L, 7L, 4L),
                 Y = c(7L, 7L, 9L, 6L, 5L, 8L, 3L, 4L, 5L, 5L))

df

Step 1: Menentukan medoids secara random

Karena ada 2 kelompok yang akan dibuat, maka akan ada 2 random medoids

  • Medoids 1: Observasi data untuk dengan koordinat (4, 5)
  • Medoids 2: Observasi data untuk dengan koordinat (8, 5)

Step 2: Menghitung jarak antara setiap observasi data terhadap titik medoidsnya

\[ Jarak = |Xi - Xm| + |Yi - Ym| \]

Dimana:

  • Xi, Yi: Titik observasi data untuk titik X & Y
  • Xm, Ym: Titik medoids yang ditentukan untuk titik X & Y

\[ JarakMedoids1= |8-4| + |7-5|\\ JarakMedoids1= 4 + 2\\ JarakMedoids1= 6 \]

df <- data.frame(row = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L),
                 X = c(8L, 3L, 4L, 9L, 8L, 5L, 7L, 8L, 7L, 4L),
                 Y = c(7L, 7L, 9L, 6L, 5L, 8L, 3L, 4L, 5L, 5L),
                 Jarak_Medoids_K1 = c(6L, 3L, 4L, 6L, NA, 4L, 5L, 5L, 3L, NA),
                 Jarak_Medoids_K2 = c(2L, 7L, 8L, 2L, NA, 6L, 3L, 1L, 1L, NA))

df

Step 2.1: Menentukan observasi data masuk ke kelompok berapa

Setelah mendapatkan perhitungan jarak, setiap observasi data akan dimasukan ke sebuah kelompok berdasarkan jarak yang paling dekat. Penentuan jarak yang paling dekat, berdasarkan nilai perbandingan dari kolom Jarak_Medoids_K1 dengan Jarak_Medoids_K2.

  • Kelompok 1: Observasi data 2, 3, 6
  • Kelompok 2: Observasi data 1, 4, 7, 8, 9

Dari penentuan setiap observasi data masuk ke kelompok berapa, kita dapat menghitung nilai costnya

\[ Cost Awal: Kelompok 1 + Kelompok 2\\ Cost Awal: (3 + 4 + 4) + (3 + 1 + 1 + 2 + 2)\\ Cost Awal: 20 \]

Step 3: Memilih random medoids kembali

Setelah mendapatkan hasil perhitungan jarak dengan medoids pertama, akan ditentukan medoids lainnya secara random untuk menjadi pembanding.

  • Medoids 1: Masih sama dengan koordinat (4, 5)
  • Medoids 2 Baru: Observasi data untuk dengan koordinat (8, 4)
df <- data.frame(row = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L),
                 X = c(8L, 3L, 4L, 9L, 8L, 5L, 7L, 8L, 7L, 4L),
                 Y = c(7L, 7L, 9L, 6L, 5L, 8L, 3L, 4L, 5L, 5L),
                 Jarak_Medoids_K1 = c(6L, 3L, 4L, 6L, 4L, 4L, 5L, NA, 3L, NA),
                 Jarak_Medoids_K2 = c(3L, 8L, 9L, 3L, 1L, 7L, 2L, NA, 2L, NA))

df
  • Kelompok 1: Observasi data 2, 3, 6
  • Kelompok 2: Observasi data 1, 4, 7, 9

\[ Cost Baru: (3 + 4 + 4) + (2 + 2 + 1 + 3 + 3)\\ Cost Baru: 22 \]

Step 4: Bandingkan hasil cost antara medoids yang sudah ditentukan

Pada tahapan ini kita sudah memiliki 2 hasil perhitungan, untuk medoids yang pertama kali ditentukan dan medoids yang kedua.

\[ S = Cost Baru - Cost Lama\\ S = 22 - 20\\ S = 2 \]

Berdasarkan asumsi yang ada, jika S>0. Maka tidak ada perubahan medoids.

3.5.2 Implementasi Partisioning Around Medoids

Setelah berkenalan dengan data yang kita gunakan, mari kita coba implementasikan metode PAM untuk membuat segmentasi kustomer. Secara garis besar akan ada 3 langkah yang nantinya akan dilakukan, yaitu:

  • Perhitungan jarak antar observasi data
  • Penentuan jumlah cluster
  • Pembuatan cluster

3.5.2.1 Perhitungan Jarak

Dari hasil eksplorasi data, kita mengetahui bahwa total observasi data kita adalah lebih dari 2.000 baris dan setiap observasi data pastinya memiliki nilai yang berbeda. Agar nantinya hasil dari setiap kelompok terdiri dari data-data yang identik, kita harus mengetahui jarak antara satu observasi dengan observasi lainnya berdekatan atau tidak

Pilihan paling populer untuk menghitung jarak adalah Euclidean, namun metode tersebut hanya berlaku untuk data numerik saja, oleh karena itu metode Euclidean tidak berlaku di sini.

Rumus Euclidean:

\[ Euclidean Distance = {\sqrt{(p - q)^2}} \]

Dimana:

  • p: titik observasi 1
  • q: titik obervasi 2

Ada satu metode yang dapat kita gunakan untuk kasus yang memiliki data campuran (data numerik dan data kategorikal), metode tersebut dinamakan Gower Distance.

Metode Gower Distance nantinya akan melakukan perbandingan pada setiap observasi data yang ada terhadap data-data yang lainnya, dengan cara melakukan perhitungan skala antara kedua observasi yang dibandingkan dengan rentan 0 sampai 1.

  • Jika hasil dari perbandingannya mendekati 0 maka bisa dibilang kedua data tersebut identik atau jarak antara kedua observasi tersebut berdekatan
  • Jika rentan perbandingannya mendekati 1 bisa dibilang kedua data tersebut tidak identik atau jarak antara kedua observasi tersebut berjauhan.

Rumus Gower Distance:

\[ Gower Distance = \frac{1}{p}\sum\limits^p_{i=1}(Xnumeric, Xcateogrical) \]

Dimana:

  • p = jumlah kolom
  • Xnumeric: kondisi ketika kolom yang dibandingkan adalah numeric

\[ Xnumeric = \frac{|X_i - {Xj}|}{r} \] * Xi = Data observasi 1 * Xj = Data observasi 2 * r = Range dari kolom observasi tersebut

  • Xcategorical: Nilai yang akan diberikan ketika kolom yang dibandingkan adalah categorical
    • Jika pada kolom kategorikal yang dibandingkan berbeda maka akan diberikan 1
    • Jika pada kolom kategorikal yang dibandingkan tidak berbeda maka akan diberikan 0

Contoh perhitungan:

Supaya lebih terbayang bagaimana hasil dari metode Gower Distance, mari kita coba lakukan perhitungan secara manual terlebih dahulu:

df2 <- data.frame(Age = c(22L, 33L, 52L, 46L),
                 Gender = c("M", "M", "F", "M") %>% as.factor(),
                 IQ = c(111L, 96L, 102L, 100L),
                 Politics = c("Liberal", "Moderate", "Liberal", "Moderate") %>% as.factor())

df2

Menghitung nilai range, untuk kolom numerik

\[ Range Age: 52 - 22 = 30\\ Range IQ: 111 - 96 = 15 \]

Perhitungan jarak untuk observasi 1 dan 2:

df[1:2,]

\[ Gower Distance: \frac{Age + Gender + IQ + Politics}{JumlahKolom}\\ \]

\[ Gower Distance: \frac{\frac{|22 - 30|}{30} + 0 + \frac{|111 - 96|}{15} + 1}4\\ Gower Distance: \frac{0.267 + 0 + 1 + 1}4\\ Gower Distance:0.567 \]

Setelah lebih memahami dari perhitungan manual, mari kita coba implementasikan ke data kita. Di R, metode Gower Distance tersimpan pada library(cluster) dan fungsi yang bisa digunakan adalah daisy(). Pada fungsi tersebut nantinya akan ada 2 parameter yang bisa diisi, yaitu:

  • x: Data frame yang ingin digunakan
  • metric: Metode apa yang ingin digunakan, karena kita akan menggunakan metode Gower Distance, kita bisa isi dengan “gower”.
# Please type your code
cust_purchase_gd <- daisy(x = cust_purchase_cleaner,
                          metric = "gower")
summary(cust_purchase_gd)
#> 496506 dissimilarities, summarized :
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>  0.0000  0.2060  0.2644  0.2648  0.3233  0.6515 
#> Metric :  mixed ;  Types = N, N, I, I, I, I, I, I, I, I, I, I, I, I, I, N, I 
#> Number of objects : 997

Sebagai pembuktian apakah metode Gower Distance memang bisa menempatkan data-data kita berdasarkan kemiripannya, kita bisa mencetak pasangan yang paling mirip dan berbeda dalam data kita dengan menggunakan fungsi di bawah ini.

  • Contoh pasangan data yang paling identik
# Please run the code down below
example <- as.matrix(cust_purchase_gd)

cust_purchase[which(example == min(example[example != min(example)]),
        arr.ind = TRUE)[1, ], ]
  • Contoh pasangan data yang paling tidak identik
# Please run the code down below
cust_purchase[which(example == max(example[example != max(example)]),
        arr.ind = TRUE)[1, ], ]

3.5.2.2 Penentuan Jumlah Cluster

Seperti yang sudah disampaikan pada bagian pendahuluan, tujuan dari melakukan clustering adalah menempatkan setiap observasi data yang ada ke beberapa kelompok yang sesuai dengan karakteristiknya atau bedasarkan kedekatan jarak antar setiap observasi. Pada langkah pertama kita sudah memiliki informasi jarak antar setiap observasi, kita akan memanfaatkan informasi tersebut untuk menentukan berapakah kelompok yang paling optimal. Hasil dari jumlah kelompok nantinya akan digunakan untuk menentukan berapa banyak segmentasi kustomer yang bisa dibuat berdasarkan data historis pembelian kustomer.

Dalam menentukan jumlah kelompok yang paling optimum, kita data memanfaatkan metode Elbow Method. Metode tersebut dapat kita gunakan dengan fungsi fviz_nbclust() dari library(factoextra). Pada fungsi tersebut nantinya akan ada 3 parameter yang bisa diisi, yaitu:

3.5.2.2.1 Elbow Method

Dengan metode pertama ini kita akan memvariasikan jumlah kelompok dengan menghitung Within Cluster Sum of Square (WSS). WSS adalah akan menghasilkan jumlah kuadrat jarak antara setiap observasi terhadap titik Medoids untuk setiap kemungkinan jumlah kelompok.

\[ WCSS = \sum\limits^N_{i=1}d(X_i - Xc)^2 \]

Dimana:

  • N = cluster
  • Xi = Data observation
  • Xc = Titik medoids
  • d(Xi - Xc): Jarak antara setiap observasi data terhadap titik medoids

Fungsi fviz_nbclust() secara otomatis akan menghitung WSS sampai dengan 10 kelompok (jumlah kelompok yang ingin dicoba hitung bisa diatur secara manual sesuai dengan kebutuhan).

Dengan bertambahnya jumlah kelompok, nilai WCSS akan mulai berkurang (nilai WCSS terbesar ketika K = 1). Ketika kita menganalisis grafik kita dapat melihat bahwa grafik akan berubah dengan cepat pada suatu titik dan dengan demikian menciptakan bentuk siku. Dari titik ini, grafik mulai bergerak hampir sejajar dengan sumbu X. Nilai K yang sesuai dengan titik ini adalah nilai K optimal atau jumlah kelompok yang optimal.

Additional: Sampling Data

Fungsi sample() digunakan untuk mengambil beberapa baris secara acak pada data. Biasanya digunakan pada proses machine learning.

# contoh sampling sederhana menggunakan vector
student <- c("A", "B", "C", "D")
sample(student, 3)
#> [1] "D" "C" "A"

Fungsi set.seed() digunakan untuk mengunci random number generator sehingga hasil sampling tidak berubah setiap kali run code. Nilai seed yang digunakan bebas, maksimal 10 digit.

# Please run the code down below
set.seed(100)
sample(student, 3)
#> [1] "B" "C" "D"

Penentual Nilai K

# Please type your code
set.seed(123)

elbow_method <- fviz_nbclust(x = as.matrix(cust_purchase_gd),
             FUNcluster = pam,
             method = "wss", 
             k.max = 5) +
  labs(subtitle = "Elbow Method")
3.5.2.2.2 [Opsional] Silhouette & Gap Statistic Method

Selain menggunakan metode Elbow, terdapat dua metode lagi yang dapat kita manfaatkan yaitu Metode Silhouette & Gap Statistic.

  • Metode Silhoutte

Metode kedua yang akan dicoba untuk menentukan jumlah kelompok yang optimal adalah Silhouette Method. Metode ini akan menghitung seberapa mirip sebuah observasi dengan kelompoknya sendiri jika dibandingkan dengan nilai kelompok lainnya.

Hasil dari perhitungannya akan memiliki rentan koefisien dari -1 sampai 1. Jika nanti hasilnya makin mendekati 1 maka observasi tersebut cocok dengan kelompoknya dan kurang cocok dengan kelompok tetangga, begitu juga sebaliknya, jika hasilnya makin mendekati -1 maka observasi tersebut tidak cocok dengan kelompoknya akan tetapi lebih cocok dengan kelompok tetangganya.

Berbeda dengan elbow method, makin bertambahnya jumlah kelompok, nilai hasil perhitungan metode silhouette method tidak pasti akan berkurang. Penentuan nilai kelompok yang optimal akan ditentukan berdasarkan nilai koefisien paling tinggi pada saat membagi keseluruhan observasi ke berapa banyak kelompok.

# PLEASE DO NOT RUN THE CODE
# set.seed(123)
# 
# fviz_nbclust(x = as.matrix(cust_purchase_gd),
#              FUNcluster = pam,
#              method = "silhouette",
#              k.max = 5) +
#   labs(subtitle = "Silhouette Method")
knitr::include_graphics("assets/sm_result.PNG")

  • Metode Silhoutte

Metode terakhir yang akan digunakan adalah Gap Statistic Method, metode ini akan menghitung seberapa berbeda total variansi antara setiap observasi pada sebuah kelompok terhadap observasi yang terdapat pada kelompok lainnya.

# PLEASE DO NOT RUN THE CODE
# set.seed(123)
# 
# fviz_nbclust(x = as.matrix(cust_purchase_gd),
#              FUNcluster = pam,
#              method = "gap_stat",
#              k.max = 5,) +
#   labs(subtitle = "Gap Statistic Method")
knitr::include_graphics("assets/gs_result.PNG")

2 dari 3 metode menunjukan bahwa K=3 adahlah nilai yang optimum, kita dapat memanfaatkan K=3 untuk mengsegmentasi kustomer kita.

Dislaimer: Penentuan jumlah kelompok tidak wajib menggunakan 3 metode pengujian di atas, penentuan kelompok juga dapat ditentukan berdasarkan pertanyaan bisnis atau kesepakatan bersama.

3.5.2.3 Pembuatan Cluster

Sekarang kita bisa memulai tahapan terakhir yaitu mengelompokan semua data customer ke kelompok yang sudah ditentukan dengan menggunakan fungsi pam(). Pada fungsi tersebut nantinya akan ada 2 parameter yang bisa diisi, yaitu:

  • x = Data frame yang ingin digunakan
  • k = Jumlah kelompok
# Please type your code
pam_fit <- pam(x = cust_purchase_gd, 
               k = 3)

Fungsi di atas akan membantu kita untuk mengelompokan keseluruhan observasi data ke beberapa kelompok berdasarkan kemiripan setiap observasi terhadap titik pusat atau medoids. Supaya kita bisa mendapatkan intuisi awal bagaimana karakteristik customer untuk setiap kelompoknya, kita bisa melihat dari tabel di bawah ini.

# Please type your code
cust_purchase_cleaner[pam_fit$medoids, ]

Seperti yang sudah disampaikan, dengan melihat tabel di atas kita hanya bisa mendapatkan intuisi awal bagaimana karakteristik dari masing-masing kelompok yang dibuat. Supaya kita dapat melakukan interpretasi lebih detail dan melihat apakah pengelompokan yang kita lakukan sudah dapat memisahkan setiap observasi data dengan baik, kita bisa memanfaatkan metode statistik deskriptif dan visualisasi.

3.5.3 Interpretasi

3.5.3.1 Metode Visualisasi

Jika dengan menggunakan metode statistik deskriptif kita bisa memahami karakteristik dari masing-masing kelompok, metode visualisasi dapat membantu kita untuk menilai bagaimana hasil pengelompokan. Metode visualisasi yang akan digunakan adalah metode t-SNE, metode ini merupakan salah satu cara untuk memvisualisasikan data yang tidak hanya memiliki nilai numerik saja melainkan nilai kategorikal juga.

# Please type your code
set.seed(123)

tsne_obj <- Rtsne(X = cust_purchase_gd, 
                  is_distance = TRUE)

tsne_data <- tsne_obj$Y %>%
  data.frame() %>%
  setNames(c("X", "Y")) %>%
  mutate(cluster = factor(pam_fit$clustering))

ggplot(aes(x = X, y = Y), data = tsne_data) +
  geom_point(aes(color = cluster)) +
  labs(title = "Visualisasi Clustering 2D") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5),
        axis.title = element_blank())

Dari hasil plot di atas, kita bisa melihat bahwa setiap observasi sudah berhasil terbagi menjadi beberapa kelompok walaupun memang masih ada beberapa observasi yang tumpang tindih atau berdekatan pada sebuah kelompok yang didominasi oleh kelompok yang berlawanan.

3.5.3.2 Metode Deskriptif

Tujuan melakukan statistik deskriptif adalah untuk memahami karakteristik masing-masing kelompok, dan dalam kasus ini untuk mengetahui karakteristik untuk setiap segmentasi kustomer.

# Please type your code
pam_results <- cust_purchase_cleaner %>%
  mutate(cluster = pam_fit$clustering) %>%
  group_by(cluster) %>%
  do(the_summary = summary(.))
pam_results$the_summary[[1]]
#>          Education   Marital_Status     Income          Kidhome       
#>  Graduation   :186   Married:186    Min.   : 27421   Min.   :0.00000  
#>  Master       : 42   Single : 60    1st Qu.: 65541   1st Qu.:0.00000  
#>  PhD          : 25   Widow  : 40    Median : 73309   Median :0.00000  
#>  Undergraduate: 33                  Mean   : 73011   Mean   :0.05594  
#>                                     3rd Qu.: 80526   3rd Qu.:0.00000  
#>                                     Max.   :105471   Max.   :2.00000  
#>                                                                       
#>     Teenhome         MntWines        MntFruits     MntMeatProducts
#>  Min.   :0.0000   Min.   :  34.0   Min.   :  0.0   Min.   : 38.0  
#>  1st Qu.:0.0000   1st Qu.: 298.2   1st Qu.: 26.0   1st Qu.:204.5  
#>  Median :0.0000   Median : 516.5   Median : 53.5   Median :365.5  
#>  Mean   :0.2692   Mean   : 550.3   Mean   : 69.8   Mean   :403.2  
#>  3rd Qu.:1.0000   3rd Qu.: 769.8   3rd Qu.:107.0   3rd Qu.:557.2  
#>  Max.   :2.0000   Max.   :1302.0   Max.   :199.0   Max.   :981.0  
#>                                                                   
#>  MntFishProducts  MntSweetProducts  MntGoldProds   NumDealsPurchases
#>  Min.   :  0.00   Min.   :  0.00   Min.   :  0.0   Min.   :0.000    
#>  1st Qu.: 43.50   1st Qu.: 24.00   1st Qu.: 33.0   1st Qu.:1.000    
#>  Median : 87.50   Median : 47.50   Median : 56.0   Median :1.000    
#>  Mean   : 98.96   Mean   : 63.64   Mean   : 77.8   Mean   :1.563    
#>  3rd Qu.:141.75   3rd Qu.: 97.75   3rd Qu.:110.5   3rd Qu.:2.000    
#>  Max.   :259.00   Max.   :198.00   Max.   :248.0   Max.   :7.000    
#>                                                                     
#>  NumWebPurchases  NumAppPurchases  NumStorePurchases    Country   
#>  Min.   : 1.000   Min.   : 1.000   Min.   : 4.000    SP     :130  
#>  1st Qu.: 4.000   1st Qu.: 3.000   1st Qu.: 6.000    SA     : 39  
#>  Median : 5.000   Median : 5.000   Median : 8.000    CA     : 36  
#>  Mean   : 5.524   Mean   : 5.294   Mean   : 8.566    IND    : 22  
#>  3rd Qu.: 7.000   3rd Qu.: 7.000   3rd Qu.:11.000    AUS    : 20  
#>  Max.   :11.000   Max.   :11.000   Max.   :13.000    GER    : 19  
#>                                                      (Other): 20  
#>       age           cluster 
#>  Min.   :27.00   Min.   :1  
#>  1st Qu.:43.00   1st Qu.:1  
#>  Median :52.00   Median :1  
#>  Mean   :53.24   Mean   :1  
#>  3rd Qu.:63.00   3rd Qu.:1  
#>  Max.   :78.00   Max.   :1  
#> 
pam_results$the_summary[[2]]
#>          Education   Marital_Status     Income          Kidhome      
#>  Graduation   : 55   Married:183    Min.   :  4023   Min.   :0.0000  
#>  Master       : 52   Single : 53    1st Qu.: 46344   1st Qu.:0.0000  
#>  PhD          :156   Widow  : 49    Median : 54998   Median :0.0000  
#>  Undergraduate: 22                  Mean   : 56257   Mean   :0.2421  
#>                                     3rd Qu.: 65104   3rd Qu.:0.0000  
#>                                     Max.   :162397   Max.   :2.0000  
#>                                                                      
#>     Teenhome         MntWines        MntFruits      MntMeatProducts 
#>  Min.   :0.0000   Min.   :   5.0   Min.   :  0.00   Min.   :   1.0  
#>  1st Qu.:1.0000   1st Qu.: 123.0   1st Qu.:  0.00   1st Qu.:  30.0  
#>  Median :1.0000   Median : 313.0   Median :  6.00   Median :  81.0  
#>  Mean   :0.8912   Mean   : 396.6   Mean   : 14.49   Mean   : 116.5  
#>  3rd Qu.:1.0000   3rd Qu.: 580.0   3rd Qu.: 17.00   3rd Qu.: 145.0  
#>  Max.   :2.0000   Max.   :1478.0   Max.   :162.00   Max.   :1622.0  
#>                                                                     
#>  MntFishProducts  MntSweetProducts  MntGoldProds    NumDealsPurchases
#>  Min.   :  0.00   Min.   :  0.00   Min.   :  0.00   Min.   : 0.000   
#>  1st Qu.:  0.00   1st Qu.:  0.00   1st Qu.: 10.00   1st Qu.: 2.000   
#>  Median :  7.00   Median :  6.00   Median : 24.00   Median : 2.000   
#>  Mean   : 19.24   Mean   : 16.13   Mean   : 41.48   Mean   : 3.098   
#>  3rd Qu.: 24.00   3rd Qu.: 19.00   3rd Qu.: 50.00   3rd Qu.: 4.000   
#>  Max.   :168.00   Max.   :262.00   Max.   :205.00   Max.   :15.000   
#>                                                                      
#>  NumWebPurchases  NumAppPurchases  NumStorePurchases    Country   
#>  Min.   : 0.000   Min.   : 0.000   Min.   : 0.000    SP     :136  
#>  1st Qu.: 3.000   1st Qu.: 1.000   1st Qu.: 4.000    SA     : 50  
#>  Median : 5.000   Median : 2.000   Median : 6.000    CA     : 43  
#>  Mean   : 5.172   Mean   : 2.604   Mean   : 6.667    AUS    : 18  
#>  3rd Qu.: 7.000   3rd Qu.: 4.000   3rd Qu.: 9.000    IND    : 16  
#>  Max.   :27.000   Max.   :28.000   Max.   :13.000    US     : 13  
#>                                                      (Other):  9  
#>       age           cluster 
#>  Min.   :31.00   Min.   :2  
#>  1st Qu.:51.00   1st Qu.:2  
#>  Median :59.00   Median :2  
#>  Mean   :58.87   Mean   :2  
#>  3rd Qu.:67.00   3rd Qu.:2  
#>  Max.   :78.00   Max.   :2  
#> 
pam_results$the_summary[[3]]
#>          Education   Marital_Status     Income          Kidhome      
#>  Graduation   :246   Married:276    Min.   :  2447   Min.   :0.0000  
#>  Master       : 74   Single :100    1st Qu.: 25154   1st Qu.:1.0000  
#>  PhD          : 37   Widow  : 50    Median : 33316   Median :1.0000  
#>  Undergraduate: 69                  Mean   : 34461   Mean   :0.8286  
#>                                     3rd Qu.: 42012   3rd Qu.:1.0000  
#>                                     Max.   :157733   Max.   :2.0000  
#>                                                                      
#>     Teenhome         MntWines         MntFruits      MntMeatProducts 
#>  Min.   :0.0000   Min.   :   0.00   Min.   : 0.000   Min.   :   0.0  
#>  1st Qu.:0.0000   1st Qu.:   7.00   1st Qu.: 1.000   1st Qu.:   7.0  
#>  Median :0.0000   Median :  19.00   Median : 3.000   Median :  14.5  
#>  Mean   :0.3685   Mean   :  59.55   Mean   : 5.939   Mean   :  37.9  
#>  3rd Qu.:1.0000   3rd Qu.:  63.00   3rd Qu.: 7.000   3rd Qu.:  34.5  
#>  Max.   :2.0000   Max.   :1126.00   Max.   :77.000   Max.   :1725.0  
#>                                                                      
#>  MntFishProducts  MntSweetProducts   MntGoldProds    NumDealsPurchases
#>  Min.   : 0.000   Min.   :  0.000   Min.   :  0.00   Min.   : 0.000   
#>  1st Qu.: 2.000   1st Qu.:  1.000   1st Qu.:  5.00   1st Qu.: 1.000   
#>  Median : 4.000   Median :  3.000   Median : 11.00   Median : 2.000   
#>  Mean   : 8.131   Mean   :  6.169   Mean   : 20.40   Mean   : 2.232   
#>  3rd Qu.:12.000   3rd Qu.:  8.000   3rd Qu.: 21.75   3rd Qu.: 3.000   
#>  Max.   :73.000   Max.   :126.000   Max.   :321.00   Max.   :15.000   
#>                                                                       
#>  NumWebPurchases  NumAppPurchases   NumStorePurchases    Country   
#>  Min.   : 0.000   Min.   : 0.0000   Min.   : 0.000    SP     :212  
#>  1st Qu.: 1.000   1st Qu.: 0.0000   1st Qu.: 3.000    SA     : 54  
#>  Median : 2.000   Median : 0.0000   Median : 3.000    CA     : 52  
#>  Mean   : 2.418   Mean   : 0.7723   Mean   : 3.362    AUS    : 40  
#>  3rd Qu.: 3.000   3rd Qu.: 1.0000   3rd Qu.: 4.000    IND    : 25  
#>  Max.   :25.000   Max.   :28.0000   Max.   :12.000    GER    : 23  
#>                                                       (Other): 20  
#>       age           cluster 
#>  Min.   :26.00   Min.   :3  
#>  1st Qu.:41.00   1st Qu.:3  
#>  Median :47.00   Median :3  
#>  Mean   :47.77   Mean   :3  
#>  3rd Qu.:52.00   3rd Qu.:3  
#>  Max.   :76.00   Max.   :3  
#> 

Referensi

  1. Clustering datasets having both numerical and categorical variables
  2. K-Medoids/Partitioning Around Medoids (PAM) — Non Hierarchical Clustering with R
  3. K-Means Cluster Analysis
  4. t-SNE
  5. Example of Calculation Gower Distance
  6. Example of Calcultion Medoids for PAM Algorithm