Kelompok 14 :


Dataset

Dataset: https://www.kaggle.com/datasets/valakhorasani/gym-members-exercise-dataset

Notebook ini membandingkan 5 algoritma clustering pada dataset gym members: K-means, K-medians, DBSCAN, Mean Shift, dan Fuzzy C-means. Data diproses menggunakan PCA sebelum clustering untuk mereduksi dimensi dan menghilangkan multikolinearitas antar fitur.

library(psych)
library(tidyverse)
library(flexclust)
library(dbscan)
library(meanShiftR)
library(e1071)
library(cluster)
library(fpc)
library(mclust)
library(factoextra)
library(gridExtra)

Load dan Eksplorasi Data

# Import Data
data <- read.csv("gym_members_exercise_tracking.csv")
knitr::kable(head(data))
Age Gender Weight..kg. Height..m. Max_BPM Avg_BPM Resting_BPM Session_Duration..hours. Calories_Burned Workout_Type Fat_Percentage Water_Intake..liters. Workout_Frequency..days.week. Experience_Level BMI
56 Male 88.3 1.71 180 157 60 1.69 1313 Yoga 12.6 3.5 4 3 30.20
46 Female 74.9 1.53 179 151 66 1.30 883 HIIT 33.9 2.1 4 2 32.00
32 Female 68.1 1.66 167 122 54 1.11 677 Cardio 33.4 2.3 4 2 24.71
25 Male 53.2 1.70 190 164 56 0.59 532 Strength 28.8 2.1 3 1 18.41
38 Male 46.1 1.79 188 158 68 0.64 556 Strength 29.2 2.8 3 1 14.39
56 Female 58.0 1.68 168 156 74 1.59 1116 HIIT 15.5 2.7 5 3 20.55
# Dimensi Data
cat("Dimensi:", nrow(data), "x", ncol(data), "\n")
## Dimensi: 973 x 15
# Struktur dan Tipe Data
str(data)
## 'data.frame':    973 obs. of  15 variables:
##  $ Age                          : int  56 46 32 25 38 56 36 40 28 28 ...
##  $ Gender                       : chr  "Male" "Female" "Female" "Male" ...
##  $ Weight..kg.                  : num  88.3 74.9 68.1 53.2 46.1 ...
##  $ Height..m.                   : num  1.71 1.53 1.66 1.7 1.79 1.68 1.72 1.51 1.94 1.84 ...
##  $ Max_BPM                      : int  180 179 167 190 188 168 174 189 185 169 ...
##  $ Avg_BPM                      : int  157 151 122 164 158 156 169 141 127 136 ...
##  $ Resting_BPM                  : int  60 66 54 56 68 74 73 64 52 64 ...
##  $ Session_Duration..hours.     : num  1.69 1.3 1.11 0.59 0.64 1.59 1.49 1.27 1.03 1.08 ...
##  $ Calories_Burned              : num  1313 883 677 532 556 ...
##  $ Workout_Type                 : chr  "Yoga" "HIIT" "Cardio" "Strength" ...
##  $ Fat_Percentage               : num  12.6 33.9 33.4 28.8 29.2 15.5 21.3 30.6 28.9 29.7 ...
##  $ Water_Intake..liters.        : num  3.5 2.1 2.3 2.1 2.8 2.7 2.3 1.9 2.6 2.7 ...
##  $ Workout_Frequency..days.week.: int  4 4 4 3 3 5 3 3 4 3 ...
##  $ Experience_Level             : int  3 2 2 1 1 3 2 2 2 1 ...
##  $ BMI                          : num  30.2 32 24.7 18.4 14.4 ...
summary(data)
##       Age           Gender           Weight..kg.       Height..m.   
##  Min.   :18.00   Length:973         Min.   : 40.00   Min.   :1.500  
##  1st Qu.:28.00   Class :character   1st Qu.: 58.10   1st Qu.:1.620  
##  Median :40.00   Mode  :character   Median : 70.00   Median :1.710  
##  Mean   :38.68                      Mean   : 73.85   Mean   :1.723  
##  3rd Qu.:49.00                      3rd Qu.: 86.00   3rd Qu.:1.800  
##  Max.   :59.00                      Max.   :129.90   Max.   :2.000  
##     Max_BPM         Avg_BPM       Resting_BPM    Session_Duration..hours.
##  Min.   :160.0   Min.   :120.0   Min.   :50.00   Min.   :0.500           
##  1st Qu.:170.0   1st Qu.:131.0   1st Qu.:56.00   1st Qu.:1.040           
##  Median :180.0   Median :143.0   Median :62.00   Median :1.260           
##  Mean   :179.9   Mean   :143.8   Mean   :62.22   Mean   :1.256           
##  3rd Qu.:190.0   3rd Qu.:156.0   3rd Qu.:68.00   3rd Qu.:1.460           
##  Max.   :199.0   Max.   :169.0   Max.   :74.00   Max.   :2.000           
##  Calories_Burned  Workout_Type       Fat_Percentage  Water_Intake..liters.
##  Min.   : 303.0   Length:973         Min.   :10.00   Min.   :1.500        
##  1st Qu.: 720.0   Class :character   1st Qu.:21.30   1st Qu.:2.200        
##  Median : 893.0   Mode  :character   Median :26.20   Median :2.600        
##  Mean   : 905.4                      Mean   :24.98   Mean   :2.627        
##  3rd Qu.:1076.0                      3rd Qu.:29.30   3rd Qu.:3.100        
##  Max.   :1783.0                      Max.   :35.00   Max.   :3.700        
##  Workout_Frequency..days.week. Experience_Level      BMI       
##  Min.   :2.000                 Min.   :1.00     Min.   :12.32  
##  1st Qu.:3.000                 1st Qu.:1.00     1st Qu.:20.11  
##  Median :3.000                 Median :2.00     Median :24.16  
##  Mean   :3.322                 Mean   :1.81     Mean   :24.91  
##  3rd Qu.:4.000                 3rd Qu.:2.00     3rd Qu.:28.56  
##  Max.   :5.000                 Max.   :3.00     Max.   :49.84

Interpretasi:

Dataset terdiri dari 973 anggota gym dengan 15 variabel (13 numerik, 2 kategorikal: Gender dan Workout_Type).

Fitur numerik yang tersedia mencakup data fisiologis (Age, Weight, Height, BMI, Fat_Percentage, BPM) dan perilaku latihan (Session_Duration, Calories_Burned, Water_Intake, Workout_Frequency, Experience_Level).

Preprocessing

1. Hitung Missing Value

colSums(is.na(data))
##                           Age                        Gender 
##                             0                             0 
##                   Weight..kg.                    Height..m. 
##                             0                             0 
##                       Max_BPM                       Avg_BPM 
##                             0                             0 
##                   Resting_BPM      Session_Duration..hours. 
##                             0                             0 
##               Calories_Burned                  Workout_Type 
##                             0                             0 
##                Fat_Percentage         Water_Intake..liters. 
##                             0                             0 
## Workout_Frequency..days.week.              Experience_Level 
##                             0                             0 
##                           BMI 
##                             0

2. Hanya Menggunakan Fitur Numerik

data_num   <- data[, sapply(data, is.numeric)]
cat("Fitur numerik yang digunakan:", ncol(data_num), "\n")
## Fitur numerik yang digunakan: 13

3. Mengganti Nama Fitur

colnames(data_num) <- paste0("X", 1:ncol(data_num))
knitr::kable(head(data_num))
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13
56 88.3 1.71 180 157 60 1.69 1313 12.6 3.5 4 3 30.20
46 74.9 1.53 179 151 66 1.30 883 33.9 2.1 4 2 32.00
32 68.1 1.66 167 122 54 1.11 677 33.4 2.3 4 2 24.71
25 53.2 1.70 190 164 56 0.59 532 28.8 2.1 3 1 18.41
38 46.1 1.79 188 158 68 0.64 556 29.2 2.8 3 1 14.39
56 58.0 1.68 168 156 74 1.59 1116 15.5 2.7 5 3 20.55

Interpretasi:

Terdapat 13 fitur numerik yang digunakan dan tidak ditemukan missing values pada seluruh kolom, sehingga data siap diproses tanpa perlu imputasi.

Nama-nama fitur diganti menjadi X1 - X13 agar lebih mudah ketika melakukan proses lebih lanjut.

Assumptions

1. Corrrelation

Masing-masing variabel memiliki korelasi dan tidak bernilai 0

cor(data_num)
##               X1           X2           X3            X4            X5
## X1   1.000000000 -0.036339635 -0.027837495 -0.0170725970  0.0359691433
## X2  -0.036339635  1.000000000  0.365321203  0.0570611305  0.0097174780
## X3  -0.027837495  0.365321203  1.000000000 -0.0176598843 -0.0147762881
## X4  -0.017072597  0.057061130 -0.017659884  1.0000000000 -0.0397514432
## X5   0.035969143  0.009717478 -0.014776288 -0.0397514432  1.0000000000
## X6   0.004353714 -0.032138091 -0.005089864  0.0366474807  0.0596355022
## X7  -0.019911904 -0.013665561 -0.010205897  0.0100509814  0.0160144382
## X8  -0.154678760  0.095443473  0.086348051  0.0020900159  0.3396586672
## X9   0.002370051 -0.225511640 -0.235520936 -0.0090557315 -0.0073016551
## X10  0.041528359  0.394275710  0.393532902  0.0316206428 -0.0029106374
## X11  0.008055163 -0.011769328 -0.011269883 -0.0290990657 -0.0106807977
## X12 -0.018675927  0.003378528 -0.010266611  0.0005448337 -0.0008881572
## X13 -0.013691370  0.853157690 -0.159468750  0.0671052310  0.0216054995
##               X6           X7           X8           X9          X10
## X1   0.004353714 -0.019911904 -0.154678760  0.002370051  0.041528359
## X2  -0.032138091 -0.013665561  0.095443473 -0.225511640  0.394275710
## X3  -0.005089864 -0.010205897  0.086348051 -0.235520936  0.393532902
## X4   0.036647481  0.010050981  0.002090016 -0.009055731  0.031620643
## X5   0.059635502  0.016014438  0.339658667 -0.007301655 -0.002910637
## X6   1.000000000 -0.016648808  0.016517951 -0.016834389  0.007725998
## X7  -0.016648808  1.000000000  0.908140376 -0.581519771  0.283410977
## X8   0.016517951  0.908140376  1.000000000 -0.597615248  0.356930683
## X9  -0.016834389 -0.581519771 -0.597615248  1.000000000 -0.588682834
## X10  0.007725998  0.283410977  0.356930683 -0.588682834  1.000000000
## X11 -0.007966891  0.644140366  0.576150125 -0.537059548  0.238562571
## X12  0.001757585  0.764768119  0.694129448 -0.654362613  0.304103549
## X13 -0.032542632 -0.006492647  0.059760826 -0.119257760  0.213696572
##              X11           X12          X13
## X1   0.008055163 -0.0186759269 -0.013691370
## X2  -0.011769328  0.0033785279  0.853157690
## X3  -0.011269883 -0.0102666112 -0.159468750
## X4  -0.029099066  0.0005448337  0.067105231
## X5  -0.010680798 -0.0008881572  0.021605500
## X6  -0.007966891  0.0017575852 -0.032542632
## X7   0.644140366  0.7647681189 -0.006492647
## X8   0.576150125  0.6941294479  0.059760826
## X9  -0.537059548 -0.6543626129 -0.119257760
## X10  0.238562571  0.3041035494  0.213696572
## X11  1.000000000  0.8370787094  0.001644974
## X12  0.837078709  1.0000000000  0.016031073
## X13  0.001644974  0.0160310726  1.000000000
corrplot::corrplot(cor(data_num), tl.col = "black", tl.srt = 45, tl.cex = 0.5)

2. MSA

KMO/MSA memilki nilai > 0.5

# Check MSA
r <- cor(data_num)
KMO(r)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = r)
## Overall MSA =  0.47
## MSA for each item = 
##   X1   X2   X3   X4   X5   X6   X7   X8   X9  X10  X11  X12  X13 
## 0.02 0.35 0.16 0.63 0.06 0.25 0.50 0.49 0.86 0.75 0.80 0.79 0.29
# Delete X1
data <- data_num[-1]
r <- cor(data)
KMO(r)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = r)
## Overall MSA =  0.54
## MSA for each item = 
##   X2   X3   X4   X5   X6   X7   X8   X9  X10  X11  X12  X13 
## 0.36 0.16 0.60 0.09 0.40 0.59 0.58 0.87 0.82 0.80 0.80 0.30
# Delete X5
data <- data[-4]
r <- cor(data)
KMO(r)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = r)
## Overall MSA =  0.61
## MSA for each item = 
##   X2   X3   X4   X6   X7   X8   X9  X10  X11  X12  X13 
## 0.36 0.16 0.54 0.19 0.74 0.75 0.87 0.83 0.80 0.79 0.30
# Delete X3
data <- data[-2]
r <- cor(data)
KMO(r)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = r)
## Overall MSA =  0.72
## MSA for each item = 
##   X2   X4   X6   X7   X8   X9  X10  X11  X12  X13 
## 0.51 0.53 0.19 0.74 0.75 0.85 0.73 0.80 0.78 0.50
# Delete X6
data <- data[-3]
r <- cor(data)
KMO(r)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = r)
## Overall MSA =  0.72
## MSA for each item = 
##   X2   X4   X7   X8   X9  X10  X11  X12  X13 
## 0.51 0.56 0.74 0.75 0.85 0.73 0.80 0.79 0.50

3. Bartlett Test

Dengan syarat hasil p-value < 0.05

bartlett.test(data)
## 
##  Bartlett test of homogeneity of variances
## 
## data:  data
## Bartlett's K-squared = 53948, df = 8, p-value < 2.2e-16

Principal Component Analysis

Metode principal component analysis atau PCA merupakan suatu teknik multivariat yang bertujuan untuk mereduksi faktor atau variabel dalam jumlah besar menjadi beberapa faktor yang lebih sedikit.

1. Standarisasi

Scaling Z-score diperlukan karena rentang antar fitur sangat berbeda — misalnya Calories_Burned bisa mencapai ribuan, sementara Height (m) hanya berkisar 1.5–2.0. Tanpa scaling, fitur dengan rentang besar akan mendominasi perhitungan jarak dan membuat clustering menjadi bias.

data_scale <- scale(data)
r = cov(data_scale)

2. Eigen Value dan Vector

pc <- eigen(r)
eigen_value <- pc$values
eigen_vector <- pc$vectors
print(eigen_value)
## [1] 3.97241779 2.01436367 0.99301511 0.82277961 0.55528999 0.30782414 0.14308312
## [8] 0.11605509 0.07517148
print(eigen_vector)
##               [,1]        [,2]        [,3]        [,4]         [,5]
##  [1,] -0.099617240 -0.65940372 -0.06538785 -0.14384144 -0.008399245
##  [2,] -0.005696882 -0.09097688  0.99424603  0.02099783  0.044865893
##  [3,] -0.440116953  0.15513574  0.04407065 -0.18577363 -0.440966682
##  [4,] -0.435887810  0.07399296  0.02110284 -0.12288184 -0.571382876
##  [5,]  0.408796215  0.08320719  0.02566220 -0.36646008 -0.141992160
##  [6,] -0.271758072 -0.29072163 -0.04086506  0.73988720  0.001629442
##  [7,] -0.400789286  0.15495238 -0.02254232 -0.23976481  0.589730005
##  [8,] -0.446212227  0.14249754  0.01358778 -0.16818811  0.329125078
##  [9,] -0.075090869 -0.62578718 -0.04231333 -0.40218961  0.026782003
##               [,6]        [,7]          [,8]         [,9]
##  [1,] -0.002632990  0.35104856  0.6091448359  0.189544735
##  [2,] -0.001620776  0.02530538  0.0008635083 -0.008245426
##  [3,] -0.067321913 -0.10161042 -0.1160223900  0.723191661
##  [4,] -0.073596872  0.24264945  0.0301603762 -0.630316021
##  [5,] -0.796334024 -0.12359334  0.1450612238 -0.019569357
##  [6,] -0.509513725 -0.14481995 -0.1091713262  0.005232390
##  [7,] -0.308731214  0.50252965 -0.2472519228  0.024420993
##  [8,] -0.008113606 -0.63480224  0.4634801799 -0.161059984
##  [9,]  0.030146211 -0.33935681 -0.5528674818 -0.129434537

3. Varians dan Kumulatif

sumvar <- sum(eigen_value)

propvar <- eigen_value / sumvar
cumvar  <- cumsum(propvar)

# Tampilkan tabel
pca_table <- data.frame(
  PC = paste0("PC", 1:length(eigen_value)),
  eigen_value = eigen_value,
  propvar = propvar * 100,
  cumulative = cumvar * 100
)

print(pca_table)
##    PC eigen_value    propvar cumulative
## 1 PC1  3.97241779 44.1379754   44.13798
## 2 PC2  2.01436367 22.3818185   66.51979
## 3 PC3  0.99301511 11.0335012   77.55330
## 4 PC4  0.82277961  9.1419956   86.69529
## 5 PC5  0.55528999  6.1698887   92.86518
## 6 PC6  0.30782414  3.4202683   96.28545
## 7 PC7  0.14308312  1.5898125   97.87526
## 8 PC8  0.11605509  1.2895010   99.16476
## 9 PC9  0.07517148  0.8352387  100.00000

4. Melihat Total PCA >= 80%

n_pc <- which(cumvar >= 0.80)[1]
# Visualisasi
par(mfrow = c(1, 2), mar = c(5, 4, 4, 2))

# Scree Plot 
plot(propvar * 100, type = "b", pch = 19, col = "steelblue", lwd = 2,
     xlab = "Principal Component", ylab = "Varians (%)",
     main = "Scree Plot", frame = FALSE)

abline(v = n_pc, col = "red", lty = 2)
legend("topright", legend = paste("PC ke-", n_pc),
       col = "red", lty = 2, bty = "n")

# Kumulatif Varians 
plot(cumvar * 100, type = "b", pch = 19, col = "darkorange", lwd = 2,
     xlab = "Jumlah PC", ylab = "Kumulatif Varians (%)",
     main = "Kumulatif Varians", frame = FALSE, ylim = c(0, 100))

abline(h = 80, col = "red", lty = 2)
legend("bottomright", legend = "80% threshold",
       col = "red", lty = 2, bty = "n")

Interpretasi:

Berdasarkan scree plot, terlihat bahwa terjadi penurunan variansi yang signifikan dari PC1 hingga PC3, kemudian kurva mulai melandai setelah PC ke-4. Hal ini menunjukkan adanya titik elbow di sekitar PC ke-3 hingga PC ke-4.

Pada grafik kumulatif varians, terlihat bahwa total variansi yang dijelaskan mencapai lebih dari 80% pada PC ke-4. Namun, dalam analisis clustering ini digunakan seluruh komponen utama.

Hal ini dilakukan karena jumlah variabel awal tidak terlalu besar, sehingga reduksi dimensi tidak menjadi kebutuhan utama. PCA tetap digunakan untuk mentransformasi data ke ruang yang bebas korelasi (orthogonal), sehingga dapat meningkatkan kinerja algoritma clustering yang sensitif terhadap korelasi antar variabel.

5. Hasil PCA

scores <- data_scale %*% eigen_vector

# Ambil sesuai n_pc
scores_PC <- scores

head(scores_PC)
##            [,1]       [,2]        [,3]         [,4]       [,5]       [,6]
## [1,] -3.5548957 -0.8833618 -0.08595574  0.517010543 -0.1447401  0.4177351
## [2,]  0.3043330 -0.1523520 -0.06144534 -1.843146738  0.3344867 -0.8900295
## [3,]  0.8742913  0.5933339 -1.08535378 -0.907137050  0.9490050 -0.9337736
## [4,]  2.7352589  0.8658108  0.90771702  0.475913884  1.0044088  0.2816337
## [5,]  2.4215596  1.1754000  0.74487711  1.564798773  0.8615264 -0.3968399
## [6,] -2.7419137  1.5571772 -0.95281782  0.007667743  0.9010904  0.4243947
##             [,7]         [,8]        [,9]
## [1,] -0.41012131 -0.009206248 -0.19661338
## [2,] -0.21756741 -0.337534476 -0.03979506
## [3,] -0.15051135  0.064842298  0.12866605
## [4,]  0.44619488 -0.104668701 -0.45596013
## [5,]  0.35883015 -0.107400094 -0.38509153
## [6,]  0.09407731 -0.125485492 -0.01599479

6. Visualisasi

pca_res <- prcomp(data_scale, center = FALSE, scale. = FALSE)

fviz_pca_var(pca_res,
             axes = c(1, 2),
             col.var = "contrib",
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
             repel = TRUE) +
  labs(title = "PCA — Kontribusi Variabel ke PC1 & PC2") +
  theme_minimal(base_size = 11)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the ggpubr package.
##   Please report the issue at <https://github.com/kassambara/ggpubr/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the factoextra package.
##   Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Interpretasi:

Berdasarkan plot kontribusi variabel terhadap PC1 dan PC2, terlihat bahwa variabel X2, X13, dan X9 memiliki kontribusi terbesar, ditunjukkan oleh panjang panah dan warna yang lebih intens.

Pada PC1 (44.1%), variabel X9 memiliki arah berlawanan dengan kelompok variabel X7, X11, X12, dan X8, yang menunjukkan adanya korelasi negatif di antara variabel-variabel tersebut. Hal ini mengindikasikan bahwa PC1 berperan dalam membedakan dua kelompok karakteristik yang berlawanan.

Pada PC2 (22.4%), variabel X2 dan X13 mendominasi kontribusi, menunjukkan bahwa dimensi ini terutama merepresentasikan variasi dari kedua variabel tersebut.

Selain itu, variabel yang memiliki arah panah yang searah, seperti X2 dan X13, menunjukkan korelasi positif yang kuat, sedangkan variabel yang berlawanan arah menunjukkan korelasi negatif.

Penentuan Jumlah Cluster Optimal

# Elbow Method (WSS)
wss <- sapply(1:10, function(k) {
  kmeans(scores_PC, centers = k, nstart = 20, iter.max = 100)$tot.withinss
})

# Silhouette Method 
k_values <- 2:10
avg_sil_values <- sapply(k_values, function(k) {
  km <- kmeans(scores_PC, centers = k, nstart = 25, iter.max = 100)
  mean(silhouette(km$cluster, dist(scores_PC))[, 3])
})

# Best K 
best_k <- k_values[which.max(avg_sil_values)]
cat("K optimal (Silhouette):", best_k, "\n")
## K optimal (Silhouette): 2
par(mfrow = c(1, 2), mar = c(5, 4, 4, 2))

plot(1:10, wss, type = "b", pch = 19, col = "steelblue", lwd = 2,
     xlab = "K", ylab = "Within-Cluster SS", main = "Elbow Method", frame = FALSE)
abline(v = 3, col = "red", lty = 2)
legend("topright", legend = "K = 3", col = "red", lty = 2, bty = "n")

plot(k_values, avg_sil_values, type = "b", pch = 19, col = "darkorange", lwd = 2,
     xlab = "K", ylab = "Avg Silhouette Width", main = "Silhouette Analysis", frame = FALSE)
abline(v = best_k, col = "red", lty = 2)
legend("topright", legend = paste("K =", best_k), col = "red", lty = 2, bty = "n")

Interpretasi:

  • Elbow Method: Terjadi penurunan nilai Within-Cluster Sum of Squares (WSS) yang cukup tajam dari K=1 hingga K=3, setelah itu penurunannya mulai melandai. Pola ini menunjukkan adanya titik “siku” (elbow) pada K=3, sehingga jumlah cluster optimal yang disarankan adalah 3.

  • Silhouette Analysis: Nilai rata-rata silhouette tertinggi diperoleh pada K=2 (sekitar 0.33), kemudian menurun pada K=3 (sekitar 0.29) dan terus menurun untuk K yang lebih besar. Hal ini menunjukkan bahwa secara kualitas pemisahan cluster, K=2 memberikan struktur yang paling kompak dan terpisah.

  • Pemilihan jumlah cluster: Meskipun K=2 memiliki nilai silhouette tertinggi, K=3 tetap dipilih karena:

    1. didukung oleh hasil Elbow Method, dan
    2. memberikan segmentasi yang lebih kaya serta informatif untuk analisis lanjutan.
  • Kualitas cluster: Nilai silhouette yang relatif rendah hingga sedang (< 0.35) mengindikasikan bahwa pemisahan antar cluster tidak terlalu kuat. Hal ini menunjukkan bahwa data memiliki pola yang cenderung kontinu, sehingga batas antar kelompok tidak terbentuk secara sangat jelas.

Clustering (K = 3)

6.1 K-Means

# 1. K-means
km_res <- kmeans(scores_PC, centers = 3)
km_res
## K-means clustering with 3 clusters of sizes 560, 195, 218
## 
## Cluster means:
##         [,1]       [,2]         [,3]        [,4]        [,5]        [,6]
## 1  1.0705090  0.6820788  0.005338244 -0.05584093 -0.02869078 -0.03795239
## 2 -3.3165870  0.4047151  0.052234680  0.20374952  0.13942444  0.24119045
## 3  0.2167406 -2.1141447 -0.060436601 -0.03880843 -0.05101343 -0.11825139
##          [,7]        [,8]        [,9]
## 1  0.01311168 -0.01544317 -0.02032968
## 2 -0.08690642 -0.03652687  0.03078970
## 3  0.04405603  0.07234365  0.02468180
## 
## Clustering vector:
##   [1] 2 1 1 1 1 2 1 1 3 3 3 1 3 3 3 1 1 1 1 1 3 1 1 3 1 1 1 1 2 1 3 1 1 1 2 3 1
##  [38] 3 1 1 1 1 3 1 3 1 1 3 1 3 1 2 3 3 1 3 1 1 2 1 1 1 2 1 2 1 2 1 2 3 2 1 1 1
##  [75] 3 1 1 1 3 2 1 2 1 1 1 1 3 1 3 1 2 2 3 1 3 1 3 3 1 2 3 1 1 3 1 2 1 2 1 1 1
## [112] 1 2 2 1 1 1 2 3 1 1 3 3 3 2 3 1 2 3 2 1 1 2 3 1 3 1 1 1 2 3 1 3 1 1 2 1 1
## [149] 3 1 1 3 2 1 3 1 1 1 1 1 3 3 3 2 2 1 2 1 1 1 3 1 2 1 3 1 2 1 1 1 3 2 2 3 2
## [186] 1 2 3 1 1 1 1 1 3 1 1 1 2 1 1 1 2 1 1 2 2 3 1 1 2 1 1 2 1 1 1 1 1 1 3 1 2
## [223] 1 2 3 2 3 1 1 2 1 1 1 2 3 1 3 2 1 1 3 3 1 1 3 1 1 1 1 1 3 3 1 1 1 1 2 3 1
## [260] 3 1 3 1 1 3 1 1 2 1 2 1 3 1 1 2 3 2 1 2 3 1 1 1 3 1 2 3 3 3 3 1 3 3 1 1 1
## [297] 1 1 3 3 3 2 3 2 1 1 1 2 1 2 2 3 3 1 1 2 2 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1
## [334] 1 1 3 1 1 3 1 3 2 1 1 3 3 1 1 2 1 1 1 1 1 1 3 2 3 1 3 1 3 1 1 2 1 1 2 1 3
## [371] 3 1 1 2 1 3 3 1 1 3 2 3 3 1 3 1 1 1 3 1 1 2 1 1 1 2 2 1 2 1 1 1 1 1 2 3 3
## [408] 1 3 1 1 1 2 1 3 1 1 1 1 2 3 2 2 3 2 1 2 3 2 2 2 1 1 1 1 1 3 1 2 1 2 1 2 2
## [445] 1 3 3 1 3 1 1 1 1 1 1 2 3 2 3 1 1 1 1 3 1 3 1 3 1 1 3 1 2 1 1 2 3 1 1 1 3
## [482] 3 1 1 2 2 3 1 1 1 1 1 2 1 3 3 3 2 1 1 1 1 1 1 1 1 1 3 1 1 1 2 2 2 1 1 1 3
## [519] 2 1 1 1 1 1 3 1 3 1 3 2 2 1 1 1 3 1 2 1 2 3 1 1 2 1 1 1 1 3 1 1 1 3 1 1 3
## [556] 1 1 2 1 1 1 3 3 1 3 1 1 1 1 1 1 3 2 1 3 1 1 1 1 1 1 1 1 1 2 1 1 2 3 3 1 2
## [593] 1 3 3 2 1 1 1 1 1 2 1 3 1 2 1 1 2 2 2 2 1 1 3 2 1 1 3 1 1 2 2 1 1 1 1 2 1
## [630] 1 2 3 1 1 3 1 1 1 2 3 3 1 1 3 3 3 2 3 3 2 1 1 1 1 1 1 1 1 1 1 1 3 1 2 1 1
## [667] 1 1 2 2 1 1 2 2 1 1 3 1 3 1 1 3 1 1 1 1 1 3 1 1 1 1 2 1 2 1 3 3 1 1 1 1 1
## [704] 1 1 1 3 1 1 1 3 2 2 1 3 3 1 1 3 2 1 3 1 1 3 1 1 1 2 3 1 1 2 3 3 2 2 3 2 3
## [741] 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 3 1 1 3 1 1 1 2 1 1 1 3 2 3 1
## [778] 2 1 2 1 1 1 1 2 3 1 1 2 1 2 1 1 1 1 1 1 2 1 1 1 2 1 1 2 1 3 1 1 1 2 2 3 1
## [815] 1 3 1 1 1 2 1 1 1 1 1 1 3 1 1 1 2 2 2 3 1 2 1 1 3 3 2 1 1 1 3 2 3 1 2 3 1
## [852] 1 3 1 3 3 1 2 1 1 1 1 1 2 2 3 2 1 3 1 2 1 1 1 1 3 2 2 1 2 1 2 1 1 3 2 1 1
## [889] 1 3 1 3 2 1 1 1 2 2 3 1 3 1 2 1 1 3 2 3 1 1 2 3 3 3 3 1 3 1 1 1 2 1 2 1 3
## [926] 1 3 2 1 1 2 1 2 3 1 1 3 3 1 1 1 1 2 2 1 3 1 1 2 3 1 1 1 1 2 3 1 2 1 3 1 1
## [963] 2 1 2 1 2 1 2 1 2 3 3
## 
## Within cluster sum of squares by cluster:
## [1] 2805.7844  654.9323 1187.8856
##  (between_SS / total_SS =  46.9 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"
# Tambahin cluster ke data asli
data_clustered <- as.data.frame(scores_PC) # Convert matrix to data frame
data_clustered$cluster <- km_res$cluster

# Distribusi cluster
data_clustered %>%
  dplyr::count(cluster) %>%
  knitr::kable(col.names = c("Cluster", "Jumlah"),
        caption = "Distribusi Cluster (K-Means)")
Distribusi Cluster (K-Means)
Cluster Jumlah
1 560
2 195
3 218
cluster1 <- subset(data_clustered, cluster == 1)
cluster2 <- subset(data_clustered, cluster == 2)
cluster3 <- subset(data_clustered, cluster == 3)

knitr::kable(head(cluster1, 10), caption = "Data Cluster 1")
Data Cluster 1
V1 V2 V3 V4 V5 V6 V7 V8 V9 cluster
2 0.3043330 -0.1523520 -0.0614453 -1.8431467 0.3344867 -0.8900295 -0.2175674 -0.3375345 -0.0397951 1
3 0.8742913 0.5933339 -1.0853538 -0.9071370 0.9490050 -0.9337736 -0.1505114 0.0648423 0.1286660 1
4 2.7352589 0.8658108 0.9077170 0.4759139 1.0044088 0.2816337 0.4461949 -0.1046687 -0.4559601 1
5 2.4215596 1.1754000 0.7448771 1.5647988 0.8615264 -0.3968399 0.3588302 -0.1074001 -0.3850915 1
7 -1.0995397 0.5923718 -0.4035556 -0.4058413 -1.3721097 0.6724907 0.1557596 0.1475879 -0.6628256 1
8 0.6732408 -0.0622624 0.8481589 -1.4832362 -0.1884369 0.0331401 -0.6262349 -0.1252764 -0.1747995 1
12 -0.0720606 0.8687009 -0.3788614 1.8397674 0.0934775 -0.4561576 -0.5479242 -0.0068691 -0.2270595 1
16 2.0848219 1.7915295 0.2926179 0.8342424 -0.7615915 0.7705099 0.0062253 0.0142321 0.1818240 1
17 0.0921168 0.4813353 -1.1038968 -0.8813410 -0.7992718 -0.2854199 -0.5127807 -0.1103230 -0.5134098 1
18 1.9295951 2.2877142 0.4258162 -0.4888902 -0.6214933 0.0811903 0.6937203 -0.0087689 -0.0110665 1
knitr::kable(head(cluster2, 10), caption = "Data Cluster 2")
Data Cluster 2
V1 V2 V3 V4 V5 V6 V7 V8 V9 cluster
1 -3.554896 -0.8833618 -0.0859557 0.5170105 -0.1447401 0.4177351 -0.4101213 -0.0092062 -0.1966134 2
6 -2.741914 1.5571772 -0.9528178 0.0076677 0.9010904 0.4243947 0.0940773 -0.1254855 -0.0159948 2
29 -3.414256 -0.1816066 0.4373335 0.6577629 0.9690840 -0.0837603 0.1827329 0.1234493 0.2782091 2
35 -3.755421 -0.4620553 0.4919035 0.7232916 0.9538967 0.4280043 0.1917607 -0.2883135 -0.3658760 2
52 -3.606637 -0.2392797 -1.3884533 0.5174930 0.8306471 0.0956225 0.0351037 -0.1567395 0.2362544 2
59 -2.333756 1.2384319 0.2044168 0.0615809 0.0795927 0.4442417 -0.4327536 0.2367311 0.0516105 2
63 -4.057361 -0.9416178 0.1411246 0.0567164 0.4695292 0.0454726 -0.0416330 -0.5442443 -0.0742905 2
65 -3.640339 1.5365387 -1.5940738 -0.6020632 -0.2878593 0.0474571 0.2149893 -0.1392534 0.1106202 2
67 -4.717246 -0.6868947 -0.4020405 -0.3360717 -0.5029764 -0.2789369 0.0385609 -0.6544218 -0.2604822 2
69 -2.785245 1.4096767 0.9388239 -0.0720908 -0.5279174 0.2525443 -0.2276002 0.3290233 -0.1192882 2
knitr::kable(head(cluster3, 10), caption = "Data Cluster 3")
Data Cluster 3
V1 V2 V3 V4 V5 V6 V7 V8 V9 cluster
9 0.1333676 -2.1620845 0.2077551 -1.0409090 1.1462374 -0.5862504 0.4650611 0.8452888 0.1975062 3
10 1.1030672 -1.5571670 -1.0805812 -0.2987946 -0.2768027 -0.4632111 0.5484607 0.0984137 0.1642210 3
11 1.2347599 -4.1550594 0.3322366 0.2162707 0.1912985 0.9546327 -0.3253190 -0.3861987 0.0167115 3
13 -0.7429353 -3.1278271 1.0682344 -1.0903531 -0.1441856 0.4613117 -0.4695494 -0.2797030 -0.3483750 3
14 0.5092599 -1.2740350 -0.2193996 1.4662842 -0.3097752 -1.0905298 0.5362785 0.1589099 0.4756315 3
15 -1.1066001 -2.7299374 1.1909131 -0.3264263 -0.7911292 -0.7004210 -0.1009853 0.4631988 -0.3545284 3
21 -1.2908732 -0.9281818 -0.5181428 0.3532140 -0.1862299 -1.7134619 -0.0097926 -0.0911675 0.2286129 3
24 1.4498074 -3.8214515 1.1497064 0.5550940 -0.1825644 -0.4031192 -0.1623373 0.1758668 -0.2892818 3
31 0.0936604 -2.3692291 -0.8859936 0.3513297 -0.7937854 -1.0425243 0.4065206 -0.2165840 0.2055142 3
36 -0.2799133 -3.4477563 -1.3776670 -1.1382704 -1.2397253 -0.4164053 0.5743305 -0.3372011 -0.1510106 3

Interpretasi K-Means:

Metode K-Means membagi data menjadi 3 cluster berdasarkan kedekatan jarak terhadap centroid (mean) pada ruang Principal Component.

Berdasarkan hasil clustering, distribusi anggota tiap cluster adalah: - Cluster 1: 195 data - Cluster 2: 560 data - Cluster 3: 218 data

Terlihat bahwa distribusi cluster tidak seimbang, dimana Cluster 2 mendominasi dengan jumlah anggota yang jauh lebih besar dibandingkan cluster lainnya. Hal ini menunjukkan bahwa sebagian besar data memiliki karakteristik yang relatif mirip dan terkonsentrasi dalam satu kelompok utama.

Selain itu, nilai between_SS / total_SS sebesar 46.9% menunjukkan bahwa sekitar setengah dari total variasi data dapat dijelaskan oleh perbedaan antar cluster. Nilai ini tergolong cukup baik (kategori sedang), yang mengindikasikan bahwa K-Means mampu memisahkan data dengan cukup jelas, meskipun belum optimal.

Dengan demikian, dapat disimpulkan bahwa K-Means sudah berhasil membentuk struktur cluster yang cukup representatif, namun masih terdapat kemungkinan tumpang tindih antar cluster, terutama karena dominasi satu cluster besar dalam dataset.

6.2 K-Medians

# 2. K-Medians
kmed_res <- kcca(scores_PC, k = 3, family = kccaFamily("kmedians"))
## Found more than one class "kcca" in cache; using the first, from namespace 'flexclust'
## Also defined by 'kernlab'
## Found more than one class "kcca" in cache; using the first, from namespace 'flexclust'
## Also defined by 'kernlab'
# Distribusi cluster
data.frame(cluster = clusters(kmed_res)) %>%
  dplyr::count(cluster) %>%
  knitr::kable(col.names = c("Cluster", "Jumlah"),
        caption = "Distribusi Cluster (K-Medians)")
Distribusi Cluster (K-Medians)
Cluster Jumlah
1 313
2 465
3 195
# Tambahin ke data asli
data_clustered <- as.data.frame(scores_PC) # Convert matrix to data frame
data_clustered$cluster <- clusters(kmed_res)

# Pisahin per cluster
cluster1 <- subset(data_clustered, cluster == 1)
cluster2 <- subset(data_clustered, cluster == 2)
cluster3 <- subset(data_clustered, cluster == 3)

knitr::kable(head(cluster1, 10), caption = "Data Cluster 1")
Data Cluster 1
V1 V2 V3 V4 V5 V6 V7 V8 V9 cluster
4 2.7352589 0.8658108 0.9077170 0.4759139 1.0044088 0.2816337 0.4461949 -0.1046687 -0.4559601 1
5 2.4215596 1.1754000 0.7448771 1.5647988 0.8615264 -0.3968399 0.3588302 -0.1074001 -0.3850915 1
11 1.2347599 -4.1550594 0.3322366 0.2162707 0.1912985 0.9546327 -0.3253190 -0.3861987 0.0167115 1
16 2.0848219 1.7915295 0.2926179 0.8342424 -0.7615915 0.7705099 0.0062253 0.0142321 0.1818240 1
18 1.9295951 2.2877142 0.4258162 -0.4888902 -0.6214933 0.0811903 0.6937203 -0.0087689 -0.0110665 1
20 0.8981528 -0.3838146 0.7549208 -0.2434322 -1.5880251 0.7416073 0.0791051 -0.2221252 0.0544307 1
22 1.1481069 -0.5599490 1.4635949 1.3229643 -0.9304952 -0.0141554 -0.0423959 -0.0681114 -0.0660102 1
23 1.8663454 -0.2563679 0.6652213 -0.0104114 -1.1373642 -0.4372326 -0.2320201 -0.0174101 0.0296210 1
24 1.4498074 -3.8214515 1.1497064 0.5550940 -0.1825644 -0.4031192 -0.1623373 0.1758668 -0.2892818 1
27 2.5897498 -0.1101889 1.0253227 1.3285645 0.2892521 0.3005442 -0.1696413 0.1721331 -0.0353738 1
knitr::kable(head(cluster2, 10), caption = "Data Cluster 2")
Data Cluster 2
V1 V2 V3 V4 V5 V6 V7 V8 V9 cluster
2 0.3043330 -0.1523520 -0.0614453 -1.8431467 0.3344867 -0.8900295 -0.2175674 -0.3375345 -0.0397951 2
3 0.8742913 0.5933339 -1.0853538 -0.9071370 0.9490050 -0.9337736 -0.1505114 0.0648423 0.1286660 2
7 -1.0995397 0.5923718 -0.4035556 -0.4058413 -1.3721097 0.6724907 0.1557596 0.1475879 -0.6628256 2
8 0.6732408 -0.0622624 0.8481589 -1.4832362 -0.1884369 0.0331401 -0.6262349 -0.1252764 -0.1747995 2
9 0.1333676 -2.1620845 0.2077551 -1.0409090 1.1462374 -0.5862504 0.4650611 0.8452888 0.1975062 2
10 1.1030672 -1.5571670 -1.0805812 -0.2987946 -0.2768027 -0.4632111 0.5484607 0.0984137 0.1642210 2
12 -0.0720606 0.8687009 -0.3788614 1.8397674 0.0934775 -0.4561576 -0.5479242 -0.0068691 -0.2270595 2
13 -0.7429353 -3.1278271 1.0682344 -1.0903531 -0.1441856 0.4613117 -0.4695494 -0.2797030 -0.3483750 2
14 0.5092599 -1.2740350 -0.2193996 1.4662842 -0.3097752 -1.0905298 0.5362785 0.1589099 0.4756315 2
15 -1.1066001 -2.7299374 1.1909131 -0.3264263 -0.7911292 -0.7004210 -0.1009853 0.4631988 -0.3545284 2
knitr::kable(head(cluster3, 10), caption = "Data Cluster 3")
Data Cluster 3
V1 V2 V3 V4 V5 V6 V7 V8 V9 cluster
1 -3.554896 -0.8833618 -0.0859557 0.5170105 -0.1447401 0.4177351 -0.4101213 -0.0092062 -0.1966134 3
6 -2.741914 1.5571772 -0.9528178 0.0076677 0.9010904 0.4243947 0.0940773 -0.1254855 -0.0159948 3
29 -3.414256 -0.1816066 0.4373335 0.6577629 0.9690840 -0.0837603 0.1827329 0.1234493 0.2782091 3
35 -3.755421 -0.4620553 0.4919035 0.7232916 0.9538967 0.4280043 0.1917607 -0.2883135 -0.3658760 3
52 -3.606637 -0.2392797 -1.3884533 0.5174930 0.8306471 0.0956225 0.0351037 -0.1567395 0.2362544 3
56 -2.011343 -2.8808307 -0.0426030 -1.7177908 -0.4698060 0.0546570 0.2282208 -0.6582573 -0.5387367 3
59 -2.333756 1.2384319 0.2044168 0.0615809 0.0795927 0.4442417 -0.4327536 0.2367311 0.0516105 3
63 -4.057361 -0.9416178 0.1411246 0.0567164 0.4695292 0.0454726 -0.0416330 -0.5442443 -0.0742905 3
65 -3.640339 1.5365387 -1.5940738 -0.6020632 -0.2878593 0.0474571 0.2149893 -0.1392534 0.1106202 3
67 -4.717246 -0.6868947 -0.4020405 -0.3360717 -0.5029764 -0.2789369 0.0385609 -0.6544218 -0.2604822 3

Interpretasi K-Medians:

Metode K-Medians membagi data menjadi 3 cluster dengan menggunakan median sebagai pusat cluster, sehingga lebih robust terhadap outlier dibandingkan K-Means.

Berdasarkan hasil clustering, distribusi anggota tiap cluster adalah: - Cluster 1: 195 data - Cluster 2: 475 data - Cluster 3: 303 data

Distribusi ini menunjukkan bahwa data masih tidak seimbang, namun tidak sedominan hasil K-Means. Jika dibandingkan dengan K-Means, pola clustering yang dihasilkan relatif serupa, yang mengindikasikan bahwa dataset tidak mengandung outlier ekstrem yang signifikan.

Selain itu, dari contoh data pada masing-masing cluster: - Cluster 1 cenderung memiliki nilai PC1 negatif (lebih rendah dari rata-rata), yang menunjukkan kelompok dengan karakteristik tertentu yang berbeda secara signifikan dari pusat data. - Cluster 2 berada di sekitar nilai tengah (dekat nol), sehingga dapat dianggap sebagai kelompok dengan karakteristik “rata-rata”. - Cluster 3 cenderung memiliki nilai PC1 positif, yang menunjukkan kelompok dengan karakteristik yang berlawanan dengan Cluster 1.

Secara keseluruhan, K-Medians menghasilkan pembagian cluster yang lebih stabil dan sedikit lebih seimbang dibandingkan K-Means, namun tidak memberikan perbedaan yang signifikan dalam struktur cluster.

6.3 DBSCAN

# eps=2.0 dipilih agar DBSCAN menghasilkan cluster yang bermakna
db_res <- dbscan(scores_PC, eps = 2.0, MinPts = 5)

# Tabel distribusi
data.frame(cluster = db_res$cluster) %>%
  dplyr::count(cluster) %>%
  knitr::kable(col.names = c("Cluster", "Jumlah"),
        caption = "Distribusi Cluster (DBSCAN)")
Distribusi Cluster (DBSCAN)
Cluster Jumlah
0 1
1 972
# Jumlah noise
cat("Jumlah noise:", sum(db_res$cluster == 0))
## Jumlah noise: 1

Interpretasi DBSCAN:

Dengan parameter yang digunakan, DBSCAN menghasilkan 1 cluster utama dengan 972 data dan hanya 1 data sebagai noise (outlier).

Jumlah noise yang sangat kecil (hanya 1 dari 973 data) menunjukkan bahwa hampir seluruh data dianggap berada dalam satu kepadatan yang sama. Artinya, algoritma tidak mampu menemukan pemisahan cluster berbasis densitas dalam dataset ini.

Akibatnya, DBSCAN tidak efektif dalam mengelompokkan data ini karena hanya menghasilkan satu cluster besar tanpa segmentasi yang berarti. Hal ini juga menyebabkan metrik evaluasi seperti Silhouette tidak dapat dihitung (NA), karena tidak ada lebih dari satu cluster yang terbentuk.

6.4 Mean Shift

ms_res <- meanShift(scores_PC)

# jumlah cluster
cat("Jumlah cluster yang terbentuk:", length(unique(ms_res$assignment)), "\n")
## Jumlah cluster yang terbentuk: 213
# tabel distribusi
data.frame(cluster = ms_res$assignment) %>%
  dplyr::count(cluster) %>%
  knitr::kable(col.names = c("Cluster", "Jumlah"),
        caption = "Distribusi Cluster (Mean Shift)")
Distribusi Cluster (Mean Shift)
Cluster Jumlah
1 101
2 50
3 68
4 8
5 4
6 14
7 66
8 2
9 1
10 1
11 1
12 1
13 4
14 11
15 31
16 9
17 8
18 2
19 11
20 1
21 4
22 19
23 1
24 9
25 10
26 1
27 3
28 3
29 1
30 3
31 18
32 32
33 1
34 1
35 31
36 3
37 17
38 3
39 5
40 1
41 5
42 6
43 2
44 1
45 8
46 4
47 3
48 6
49 7
50 6
51 5
52 1
53 1
54 1
55 15
56 3
57 3
58 1
59 2
60 1
61 1
62 2
63 1
64 2
65 4
66 1
67 19
68 1
69 9
70 1
71 6
72 6
73 9
74 1
75 1
76 2
77 1
78 11
79 1
80 1
81 2
82 8
83 6
84 4
85 2
86 1
87 1
88 1
89 5
90 1
91 1
92 1
93 5
94 1
95 5
96 1
97 1
98 6
99 1
100 4
101 1
102 1
103 1
104 1
105 10
106 5
107 6
108 9
109 6
110 1
111 2
112 2
113 1
114 2
115 15
116 1
117 1
118 1
119 1
120 2
121 3
122 1
123 8
124 1
125 2
126 1
127 4
128 1
129 1
130 1
131 1
132 1
133 1
134 2
135 1
136 1
137 1
138 1
139 1
140 1
141 3
142 1
143 1
144 1
145 1
146 2
147 2
148 1
149 1
150 2
151 1
152 1
153 1
154 1
155 1
156 1
157 1
158 1
159 1
160 1
161 1
162 1
163 1
164 2
165 2
166 2
167 2
168 1
169 1
170 3
171 1
172 1
173 4
174 1
175 2
176 2
177 2
178 1
179 2
180 1
181 1
182 1
183 2
184 1
185 1
186 1
187 1
188 1
189 1
190 1
191 1
192 1
193 1
194 1
195 1
196 1
197 1
198 1
199 1
200 1
201 1
202 1
203 1
204 1
205 1
206 1
207 1
208 1
209 1
210 1
211 1
212 1
213 1

Interpretasi Mean Shift: Mean Shift secara otomatis menentukan jumlah cluster berdasarkan kepadatan data. Pada dataset ini, algoritma cenderung menghasilkan cluster yang sangat banyak dan tidak seimbang, mencerminkan distribusi data yang menyebar dan tidak memiliki puncak kepadatan (modus) yang tegas.

Sama seperti DBSCAN, hasil ini menegaskan bahwa data gym members tidak memiliki struktur yang cocok untuk algoritma berbasis kepadatan. Mean Shift lebih efektif pada data dengan distribusi multimodal yang jelas, misalnya data gambar atau sensor.

6.5 Fuzzy C-means

fcm_res <- cmeans(scores_PC, centers = 3, m = 2, iter.max = 100)
# Distribusi cluster
data.frame(cluster = fcm_res$cluster) %>%
  dplyr::count(cluster) %>%
  knitr::kable(col.names = c("Cluster", "Jumlah"),
        caption = "Distribusi Cluster (FCM - Hard Assignment)")
Distribusi Cluster (FCM - Hard Assignment)
Cluster Jumlah
1 323
2 437
3 213
# Membership degree
knitr::kable(round(head(fcm_res$membership, 5), 3),
      caption = "Contoh Membership Degree (5 Data Pertama)")
Contoh Membership Degree (5 Data Pertama)
1 2 3
0.085 0.079 0.837
0.443 0.424 0.133
0.429 0.469 0.101
0.424 0.484 0.093
0.417 0.471 0.112

Interpretasi Fuzzy C-Means:

Fuzzy C-Means menghasilkan 3 cluster dengan distribusi: - Cluster 1: 323 data - Cluster 2: 213 data - Cluster 3: 437 data

Distribusi ini mirip dengan hasil K-Means, yang mengindikasikan bahwa struktur dasar cluster dalam data cukup konsisten antar metode. Namun, keunikan utama Fuzzy C-Means terletak pada membership degree, yaitu setiap data memiliki derajat keanggotaan pada semua cluster.

Berdasarkan contoh 5 data pertama: - Data ke-1: [0.084, 0.837, 0.079] → sangat kuat di Cluster 2 - Data ke-2: [0.442, 0.133, 0.425] → ambigu antara Cluster 1 dan 3 - Data ke-3: [0.432, 0.101, 0.466] → cenderung ke Cluster 3, tapi masih campuran - Data ke-4 & 5: pola mirip → dominan di Cluster 3, tapi tidak tegas

Hal ini menunjukkan bahwa banyak data berada di area perbatasan antar cluster, ditandai dengan nilai membership yang tidak jauh berbeda (tidak ada yang dominan mendekati 1).

Meskipun nilai evaluasi (Silhouette) pada Fuzzy C-Means tidak setinggi K-Means, tetapi metode ini mampu memberikan insight tambahan tentang data yang berada di batas antar cluster.

Visualisasi Hasil Clustering

# --- Visualization Comparison ---
par(mfrow = c(2, 3), mar = c(4, 4, 2, 1))

# K-Means
plot(scores_PC,
     col = km_res$cluster,
     pch = 16,
     main = "K-Means")

# K-Medians
plot(scores_PC,
     col = clusters(kmed_res),
     pch = 16,
     main = "K-Medians")

# DBSCAN
plot(scores_PC,
     col = db_res$cluster + 1L,
     pch = 16,
     main = "DBSCAN (0 = Noise)")

# Mean Shift
plot(scores_PC,
     col = ms_res$assignment,
     pch = 16,
     main = "Mean Shift")

# Fuzzy C-Means
plot(scores_PC,
     col = fcm_res$cluster,
     pch = 16,
     main = "Fuzzy C-Means")

Interpretasi Visualisasi Clustering

Berdasarkan visualisasi hasil clustering menggunakan lima metode (K-Means, K-Medians, DBSCAN, Mean Shift, dan Fuzzy C-Means), terlihat bahwa setiap algoritma menghasilkan pola pengelompokan yang berbeda sesuai dengan karakteristik dan asumsi masing-masing metode.

  1. K-Means : Pada plot K-Means, terlihat bahwa data terbagi menjadi tiga cluster yang cukup jelas dengan pola pemisahan dominan secara vertikal (berdasarkan PC1). Hal ini menunjukkan bahwa komponen utama pertama (PC1) memiliki peran besar dalam membedakan kelompok data. Meskipun demikian, masih terdapat sedikit tumpang tindih antar cluster, terutama di area tengah.

  2. K-Medians : Hasil K-Medians menunjukkan pola yang mirip dengan K-Means, namun distribusi titik dalam cluster terlihat lebih kompak. Hal ini mencerminkan sifat K-Medians yang lebih robust terhadap outlier, sehingga pembagian cluster menjadi lebih stabil meskipun terdapat data ekstrem.

  3. DBSCAN : Pada DBSCAN, sebagian besar data tergabung dalam satu cluster besar dengan sedikit atau hampir tidak ada pemisahan yang jelas, serta kemungkinan adanya noise (cluster 0). Hal ini menunjukkan bahwa dataset tidak memiliki struktur kepadatan yang cukup kuat untuk dipisahkan menggunakan pendekatan berbasis densitas, atau parameter yang digunakan belum optimal.

  4. Mean Shift : Mean Shift menghasilkan jumlah cluster yang sangat banyak dengan ukuran kecil-kecil dan tersebar acak. Hal ini menunjukkan bahwa algoritma mendeteksi banyak puncak kepadatan lokal, sehingga data cenderung tidak memiliki struktur global yang jelas. Pola ini mengindikasikan bahwa distribusi data cukup kompleks dan tidak terpusat pada beberapa kelompok utama saja.

  5. Fuzzy C-Means : Pada Fuzzy C-Means, pola cluster terlihat mirip dengan K-Means, namun dengan batas antar cluster yang lebih halus. Hal ini disebabkan karena setiap data memiliki derajat keanggotaan pada lebih dari satu cluster, sehingga transisi antar kelompok menjadi lebih gradual. Pendekatan ini lebih mampu merepresentasikan data yang memiliki karakteristik tumpang tindih.

Kesimpulan : Secara keseluruhan, metode berbasis partisi seperti K-Means, K-Medians, dan Fuzzy C-Means memberikan hasil clustering yang lebih jelas dan mudah diinterpretasikan dibandingkan metode berbasis densitas (DBSCAN) dan pencarian mode (Mean Shift). Hal ini mengindikasikan bahwa struktur data lebih cocok dianalisis menggunakan pendekatan berbasis pembagian global dibandingkan berbasis kepadatan lokal.

Evaluasi Metrik

dist_matrix <- dist(scores_PC)

eval_clustering <- function(labels, data_dist, name) {
  valid <- labels > 0
  n_cl  <- length(unique(labels[valid]))
  if (n_cl < 2) return(data.frame(Method=name, K=n_cl, Silhouette=NA, Dunn=NA))

  sil   <- mean(silhouette(labels[valid], as.dist(as.matrix(data_dist)[valid, valid]))[, 3])
  stats <- cluster.stats(data_dist, labels)
  data.frame(Method=name, K=n_cl, Silhouette=round(sil,4), Dunn=round(stats$dunn,4))
}

eval_results <- rbind(
  eval_clustering(km_res$cluster,     dist_matrix, "K-means"),
  eval_clustering(clusters(kmed_res), dist_matrix, "K-medians"),
  eval_clustering(db_res$cluster,     dist_matrix, "DBSCAN"),
  eval_clustering(ms_res$assignment,  dist_matrix, "Mean Shift"),
  eval_clustering(fcm_res$cluster,    dist_matrix, "Fuzzy C-means")
)

best_method <- eval_results %>%
  filter(Silhouette == max(Silhouette, na.rm = TRUE)) %>%
  slice(1)

knitr::kable(eval_results, caption = "Hasil Evaluasi Clustering")
Hasil Evaluasi Clustering
Method K Silhouette Dunn
K-means 3 0.2966 0.1152
K-medians 3 0.1952 0.0692
DBSCAN 1 NA NA
Mean Shift 213 -0.0856 0.0738
Fuzzy C-means 3 0.2425 0.0688
cat("Metode terbaik:", best_method$Method, "\n")
## Metode terbaik: K-means

Interpretasi Evaluasi Metrik:

Metode Silhouette Dunn Keterangan
Fuzzy C-means 0.2425 0.0688 ✅ Terbaik — pemisahan cluster paling optimal
K-means 0.2148 0.0927 Baik — cluster cukup jelas dan paling kompak
K-medians 0.1952 0.0692 Cukup baik, namun sedikit di bawah K-means
DBSCAN NA NA Tidak terbentuk cluster yang valid (hanya 1 cluster)
Mean Shift -0.0856 0.0738 ❌ Buruk — terlalu banyak cluster dan tidak terpisah dengan baik

Kesimpulan: Berdasarkan nilai Silhouette, metode Fuzzy C-Means memberikan hasil terbaik dengan nilai tertinggi (0.2425), yang menunjukkan pemisahan cluster yang paling baik dibanding metode lain. Namun, metode K-Means juga menunjukkan performa yang stabil dengan nilai Silhouette yang cukup tinggi serta nilai Dunn Index tertinggi (0.0927), yang mengindikasikan cluster yang lebih kompak dan terpisah.

Di sisi lain, K-Medians memberikan hasil yang cukup baik tetapi tidak seoptimal K-Means. Metode DBSCAN tidak berhasil mengidentifikasi struktur cluster yang berarti (hanya menghasilkan satu cluster), sehingga tidak cocok untuk dataset ini. Sementara itu, Mean Shift menghasilkan nilai Silhouette negatif, yang menunjukkan bahwa pembentukan cluster tidak sesuai dengan struktur data.

Secara keseluruhan, meskipun Fuzzy C-Means unggul secara metrik Silhouette, metode K-Means tetap menjadi pilihan yang lebih stabil dan interpretatif untuk analisis clustering pada dataset ini.

Nilai Silhouette yang relatif rendah (sekitar 0.19–0.24) menunjukkan bahwa struktur cluster dalam data tidak terlalu tegas, yang merupakan hal umum pada data yang bersifat kontinu seperti data perilaku atau karakteristik individu.

Silhouette Plot (K-Means)

fviz_silhouette(silhouette(km_res$cluster, dist_matrix)) +
  labs(title = "Silhouette Plot - K-means") +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))
##   cluster size ave.sil.width
## 1       1  560          0.26
## 2       2  195          0.49
## 3       3  218          0.22

Interpretasi Silhouette Plot K-Means

Setiap bar merepresentasikan satu data/objek dalam dataset, dan panjang bar menunjukkan nilai silhouette (Si) yaitu seberapa baik suatu data cocok dengan cluster-nya dibanding cluster lain.

  • Cluster 2 memiliki rata-rata silhouette tertinggi (0,46) dengan jumlah anggota 193 data, artinya anggota dalam cluster ini paling jelas terpisah dari cluster lain. Sebagian besar datanya memiliki bar cukup panjang, sehingga cluster ini dapat dianggap paling stabil dan paling homogen.

  • Cluster 1 memiliki rata-rata silhouette 0,17 dengan 273 data, menunjukkan bahwa pemisahan cluster ini masih cukup lemah. Banyak anggota cluster memiliki bar pendek, yang berarti beberapa data berada dekat batas cluster lain.

  • Cluster 3 memiliki anggota paling banyak yaitu 507 data, tetapi rata-rata silhouette hanya 0,15. Ini menunjukkan walaupun cluster ini besar, karakteristik anggotanya masih cukup bercampur sehingga pemisahannya kurang kuat.

  • Garis merah putus-putus (~0,22) menunjukkan rata-rata silhouette keseluruhan. Karena nilainya berada di sekitar 0,2, maka hasil clustering sudah memiliki struktur, tetapi pemisahan antar cluster belum terlalu kuat.

  • Terlihat ada beberapa bar yang sangat pendek mendekati 0, terutama pada cluster 1 dan 3, yang berarti ada data yang posisinya berada di perbatasan antar cluster dan bisa saja cocok masuk ke cluster lain.

Kesimpulan

Berdasarkan analisis clustering pada 973 anggota gym dengan 13 fitur numerik:

Perbandingan Metode

Metode Jumlah Cluster Silhouette Kesesuaian
K-means 3 ~0.21 ✅ Terbaik
K-medians 3 ~0.20 ✅ Baik
Fuzzy C-means 3 ~0.24 ⚠️ Cukup baik
DBSCAN 1 NA ❌ Tidak cocok
Mean Shift 213 ~(-0.08) ❌ Tidak cocok

Profil 3 Cluster (K-means)

  • Cluster 1 (~194 data): Memiliki karakteristik dengan nilai PC1 (V1) yang cenderung negatif besar, menunjukkan kelompok dengan pola yang cukup berbeda dari rata-rata data.

  • Cluster 2 (~559 data): Merupakan cluster terbesar dengan nilai PC yang berada di sekitar pusat distribusi, sehingga dapat dianggap sebagai kelompok dengan karakteristik “rata-rata”.

  • Cluster 3 (~220 data): Ditandai dengan variasi pada PC2 (V2), yang menunjukkan adanya dimensi tambahan yang membedakan kelompok ini dari cluster lainnya.

Rekomendasi

Dari hasil evaluasi, K-means menjadi metode terbaik karena menghasilkan nilai Silhouette yang relatif paling tinggi dan mampu membentuk cluster yang cukup jelas dibandingkan metode lainnya.