1. Pendahuluan

1.1. Latar Belakang

Dalam industri ritel minuman, memahami mengapa sebagian transaksi sangat menguntungkan sementara sebagian lainnya justru merugikan adalah pertanyaan strategis yang krusial. Pemilik bisnis tidak cukup hanya mengetahui total profit keseluruhan, yang jauh lebih berharga adalah mengetahui pola dan segmen transaksinya: mana yang efisien secara biaya, mana yang menghasilkan profit tinggi, dan mana yang bermasalah secara keuangan.

Coffee Chain adalah jaringan kafe yang beroperasi di berbagai wilayah Amerika Serikat. Dataset yang tersedia mencatat 4.248 transaksi selama Januari 2012 hingga Desember 2013, mencakup variabel-variabel keuangan seperti penjualan, profit, biaya pemasaran, harga pokok penjualan, dan inventaris.

1.2. Rumusan Masalah

  1. Segmen apa saja yang terbentuk dari transaksi Coffee Chain berdasarkan profil keuangannya?
  2. Apakah perbedaan profil keuangan antar segmen signifikan secara statistik, atau hanya kebetulan?
  3. Variabel keuangan mana yang paling berkontribusi dalam membedakan antar segmen?

1.3. Tujuan Analisis

  1. Mengelompokkan transaksi Coffee Chain ke dalam segmen-segmen berdasarkan profil keuangan menggunakan K-Means Clustering.
  2. Menguji apakah perbedaan profil keuangan antar segmen signifikan secara statistik menggunakan Uji Kruskal-Wallis dan Post-Hoc Dunn.
  3. Mengidentifikasi variabel keuangan yang paling membedakan antar segmen menggunakan Analisis Diskriminan Linear (LDA).

1.4. Alasan Pemilihan Metode dan Alur Analisis

Alur analisis ini dirancang secara runtut, di mana setiap metode melanjutkan dan memperdalam temuan dari metode sebelumnya. Tahap pertama berfokus pada pembentukan segmen, tahap kedua memvalidasi signifikansi statistiknya, dan tahap ketiga mengidentifikasi variabel yang paling berperan dalam menciptakan perbedaan tersebut.

K-Means Clustering → Kruskal-Wallis + Dunn → LDA

SegmentasiValidasi StatistikIdentifikasi Pembeda Utama

Regresi linier tidak dipilih karena variabel-variabel yang paling kuat memengaruhi profit, yaitu Sales, COGS, dan Total Expenses, merupakan komponen pembentuk profit secara matematis (\(Profit = Sales - COGS - Total\ Expenses\)). Memasukkannya sebagai prediktor regresi hanya akan “menghitung ulang” definisi profit, bukan mengungkap faktor penjelas yang bermakna. Selain itu, uji diagnostik menunjukkan pelanggaran asumsi normalitas dan homoskedastisitas yang serius.

K-Means dipilih karena merupakan metode unsupervised learning yang mengelompokkan observasi berdasarkan kemiripan profil multivariat tanpa memerlukan asumsi distribusi. Pendekatan ini sangat tepat untuk menemukan pola tersembunyi dalam data keuangan yang kompleks dan beragam.

Kruskal-Wallis dipilih karena setelah klaster terbentuk secara deskriptif, perlu dikonfirmasi bahwa perbedaan yang terlihat memang signifikan secara statistik dan bukan sekadar produk dari cara kerja algoritma. Kruskal-Wallis lebih sesuai dibanding ANOVA karena distribusi variabel keuangan dalam data ini tidak memenuhi asumsi normalitas.

LDA dipilih sebagai tahap akhir untuk mengidentifikasi kombinasi linear variabel keuangan yang paling memaksimalkan pemisahan antar segmen. LDA di sini tidak digunakan untuk prediksi, melainkan sebagai alat eksplorasi pembeda yang memberikan bobot kuantitatif pada masing-masing variabel.

1.5. Deskripsi Dataset

Dataset Coffee Chain terdiri dari 4.248 observasi dengan 20 variabel asli, mencatat transaksi dari Januari 2012 hingga Desember 2013 di 4 wilayah pasar. Variabel yang digunakan dalam analisis:

Variabel Keterangan
Sales Realisasi total penjualan
Profit Realisasi laba bersih - dapat bernilai negatif
COGS Cost of Goods Sold - Harga pokok penjualan
Marketing Biaya pemasaran yang dikeluarkan
Total Expenses Total biaya operasional
Margin Margin kotor realisasi
Inventory Nilai inventaris

Variabel Profit dalam dataset ini dapat bernilai negatif (minimum: −638), yang mencerminkan transaksi yang merugi. Ini justru sangat bernilai karena memungkinkan identifikasi segmen bermasalah.


2. Persiapan Data

2.1. Instalasi dan Pemuatan Paket

options(kableExtra.auto_format = FALSE)

library(readxl)      # Membaca file Excel
library(dplyr)       # Manipulasi data
library(ggplot2)     # Visualisasi
library(lubridate)   # Penanganan tanggal
library(tidyr)       # Transformasi data
library(scales)      # Format angka pada plot
library(knitr)       # Pastikan knitr dimuat sebelum kableExtra
library(kableExtra)  # Tabel yang lebih informatif
library(ggcorrplot)  # Visualisasi matriks korelasi
library(factoextra)  # Visualisasi K-Means: elbow, silhouette, biplot PCA
library(cluster)     # Komputasi silhouette width
library(gridExtra)   # Menampilkan beberapa plot dalam satu panel
library(dunn.test)   # Uji post-hoc Dunn setelah Kruskal-Wallis
library(MASS)        # Analisis Diskriminan Linear (LDA)

2.2. Import dan Persiapan Data

df <- read_excel("C:/users/l13 yoga/Downloads/1. Tugas SIM 2025B - Coffee Chain Datasets.xlsx", sheet = "data")

df <- df %>%
  mutate(
    Date         = as.Date(Date),
    Year         = year(Date),
    Month        = month(Date),
    YearMonth    = floor_date(Date, "month"),
    Product_Type = as.factor(`Product Type`),
    Market       = as.factor(Market),
    Market_Size  = as.factor(`Market Size`),
    Type         = as.factor(Type),
    Profit_Gap   = Profit - `Budget Profit`,
    Sales_Gap    = Sales - `Budget Sales`
  )

glimpse(df)
## Rows: 4,248
## Columns: 27
## $ `Area Code`      <dbl> 719, 970, 970, 303, 303, 720, 970, 719, 970, 719, 303…
## $ Date             <date> 2012-01-01, 2012-01-01, 2012-01-01, 2012-01-01, 2012…
## $ Market           <fct> Central, Central, Central, Central, Central, Central,…
## $ `Market Size`    <chr> "Major Market", "Major Market", "Major Market", "Majo…
## $ Product          <chr> "Amaretto", "Colombian", "Decaf Irish Cream", "Green …
## $ `Product Line`   <chr> "Beans", "Beans", "Beans", "Leaves", "Beans", "Beans"…
## $ `Product Type`   <chr> "Coffee", "Coffee", "Coffee", "Tea", "Espresso", "Esp…
## $ State            <chr> "Colorado", "Colorado", "Colorado", "Colorado", "Colo…
## $ Type             <fct> Regular, Regular, Decaf, Regular, Regular, Decaf, Dec…
## $ `Budget COGS`    <dbl> 90, 80, 100, 30, 60, 80, 140, 50, 50, 40, 50, 150, 10…
## $ `Budget Margin`  <dbl> 130, 110, 140, 50, 90, 130, 160, 80, 70, 70, 70, 210,…
## $ `Budget Profit`  <dbl> 100, 80, 110, 30, 70, 80, 110, 20, 40, 20, 40, 130, 1…
## $ `Budget Sales`   <dbl> 220, 190, 240, 80, 150, 210, 300, 130, 120, 110, 120,…
## $ COGS             <dbl> 89, 83, 95, 44, 54, 72, 170, 63, 60, 58, 64, 144, 95,…
## $ Inventory        <dbl> 777, 623, 821, 623, 456, 558, 1091, 435, 336, 338, 96…
## $ Margin           <dbl> 130, 107, 139, 56, 80, 108, 171, 87, 80, 72, 76, 201,…
## $ Marketing        <dbl> 24, 27, 26, 14, 15, 23, 47, 57, 19, 22, 19, 47, 30, 7…
## $ Profit           <dbl> 94, 68, 101, 30, 54, 53, 99, 0, 33, 17, 36, 111, 87, …
## $ Sales            <dbl> 219, 190, 234, 100, 134, 180, 341, 150, 140, 130, 140…
## $ `Total Expenses` <dbl> 36, 39, 38, 26, 26, 55, 72, 87, 47, 55, 40, 90, 52, 1…
## $ Year             <dbl> 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012,…
## $ Month            <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ YearMonth        <date> 2012-01-01, 2012-01-01, 2012-01-01, 2012-01-01, 2012…
## $ Product_Type     <fct> Coffee, Coffee, Coffee, Tea, Espresso, Espresso, Herb…
## $ Market_Size      <fct> Major Market, Major Market, Major Market, Major Marke…
## $ Profit_Gap       <dbl> -6, -12, -9, 0, -16, -27, -11, -20, -7, -3, -4, -19, …
## $ Sales_Gap        <dbl> -1, 0, -6, 20, -16, -30, 41, 20, 20, 20, 20, -15, -6,…

2.3. Pemeriksaan Kualitas Data

cek_na <- df %>%
  dplyr::select(Sales, Profit, COGS, Marketing, `Total Expenses`, Margin, Inventory) %>%
  summarise(across(everything(), ~ sum(is.na(.)))) %>%
  pivot_longer(everything(), names_to = "Variabel", values_to = "Jumlah_NA") %>%
  filter(Jumlah_NA > 0)

if (nrow(cek_na) == 0) {
  cat("Tidak terdapat nilai hilang pada variabel yang digunakan. \n")
} else {
  kable(cek_na) %>% kable_styling(bootstrap_options = "striped")
}
## Tidak terdapat nilai hilang pada variabel yang digunakan.

2.4. Statistik Deskriptif

df %>%
  dplyr::select(Sales, Profit, COGS, Marketing, `Total Expenses`, Margin, Inventory) %>%
  summary() %>%
  kable(caption = "Statistik Deskriptif Variabel Keuangan") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE)
Statistik Deskriptif Variabel Keuangan
Sales Profit COGS Marketing Total Expenses Margin Inventory
Min. : 17 Min. :-638.0 Min. : 0.00 Min. : 0.00 Min. : 10.00 Min. :-302.00 Min. :-3534.0
1st Qu.:100 1st Qu.: 17.0 1st Qu.: 43.00 1st Qu.: 13.00 1st Qu.: 33.00 1st Qu.: 52.75 1st Qu.: 432.0
Median :138 Median : 40.0 Median : 60.00 Median : 22.00 Median : 46.00 Median : 76.00 Median : 619.0
Mean :193 Mean : 61.1 Mean : 84.43 Mean : 31.19 Mean : 54.06 Mean : 104.29 Mean : 749.4
3rd Qu.:230 3rd Qu.: 92.0 3rd Qu.:100.00 3rd Qu.: 39.00 3rd Qu.: 65.00 3rd Qu.: 132.00 3rd Qu.: 910.5
Max. :912 Max. : 778.0 Max. :364.00 Max. :156.00 Max. :190.00 Max. : 613.00 Max. : 8252.0

Variabel Profit memiliki rentang sangat lebar dari −638 hingga 778 dengan median 40 - heterogenitas yang signifikan ini adalah kondisi ideal untuk analisis segmentasi. Inventory juga menunjukkan rentang ekstrem (hingga 8.252), mencerminkan variasi kondisi stok yang besar antar transaksi.


3. Analisis Korelasi (Analisis Pendahuluan)

Sebelum masuk ke segmentasi, dilakukan analisis korelasi sebagai pemahaman awal struktur data untuk mengetahui variabel mana yang bergerak bersama dan mana yang bersifat independen. Pemahaman ini penting sebagai landasan interpretasi hasil klaster dan LDA yang akan dibahas pada bagian selanjutnya.

df_num <- df %>%
  dplyr::select(Sales, Profit, COGS, Marketing, `Total Expenses`, Margin, Inventory)

mat_cor <- cor(df_num, method = "pearson")

round(mat_cor, 3) %>%
  kable(caption = "Matriks Korelasi Pearson Antarvariabel Keuangan") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "bordered"), full_width = TRUE)
Matriks Korelasi Pearson Antarvariabel Keuangan
Sales Profit COGS Marketing Total Expenses Margin Inventory
Sales 1.000 0.797 0.887 0.711 0.689 0.939 0.326
Profit 0.797 1.000 0.465 0.225 0.200 0.921 -0.092
COGS 0.887 0.465 1.000 0.818 0.783 0.679 0.621
Marketing 0.711 0.225 0.818 1.000 0.966 0.532 0.498
Total Expenses 0.689 0.200 0.783 0.966 1.000 0.521 0.434
Margin 0.939 0.921 0.679 0.532 0.521 1.000 0.061
Inventory 0.326 -0.092 0.621 0.498 0.434 0.061 1.000
ggcorrplot(mat_cor,
           method   = "circle",
           type     = "lower",
           lab      = TRUE,
           lab_size = 3.5,
           colors   = c("#e74c3c", "white", "#2980b9"),
           title    = "Heatmap Korelasi Pearson Variabel Keuangan Coffee Chain",
           ggtheme  = theme_minimal()) +
  theme(
    plot.title  = element_text(face = "bold", size = 13),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )
## 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 ggcorrplot package.
##   Please report the issue at <https://github.com/kassambara/ggcorrplot/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Interpretasi Heatmap Korelasi:

Heatmap di atas menampilkan kekuatan dan arah hubungan linear antarpasangan variabel keuangan. Lingkaran biru menunjukkan korelasi positif dan lingkaran merah menunjukkan korelasi negatif, dengan ukuran lingkaran mencerminkan kekuatan korelasi.

Beberapa temuan utama dari matriks korelasi ini adalah sebagai berikut. Pertama, Sales berkorelasi sangat kuat dengan Margin (r = 0,939) dan COGS (r = 0,887), yang mengindikasikan bahwa transaksi bernilai jual tinggi selalu disertai margin dan harga pokok yang tinggi pula. Ini merupakan hubungan yang wajar dan konsisten secara logika bisnis. Kedua, Marketing dan Total Expenses memiliki korelasi yang hampir sempurna (r = 0,966), yang berarti biaya pemasaran merupakan komponen dominan dalam total pengeluaran operasional sehingga keduanya bergerak hampir secara bersamaan. Ketiga, Profit dan Margin berkorelasi sangat kuat (r = 0,921), menjadikan Margin sebagai indikator terbaik profitabilitas di antara semua variabel yang ada. Keempat, Profit berkorelasi sangat lemah dengan Marketing (r = 0,225) dan bahkan negatif lemah dengan Inventory (r = −0,092), yang menunjukkan bahwa pengeluaran pemasaran yang besar tidak serta-merta menghasilkan profit yang lebih besar, dan level stok tidak memiliki hubungan linear yang berarti dengan profitabilitas.

Temuan terakhir ini mengandung implikasi penting: tingginya multikolinearitas antar variabel prediktor mengkonfirmasi bahwa pendekatan berbasis jarak seperti K-Means lebih sesuai untuk data ini dibandingkan regresi linier.


4. K-Means Clustering - Segmentasi Transaksi

4.1. Dasar Teori

K-Means Clustering membagi \(n\) observasi ke dalam \(k\) kelompok di mana setiap observasi dimasukkan ke klaster dengan centroid (rata-rata) terdekat berdasarkan jarak Euclidean. Algoritma bekerja secara iteratif hingga posisi centroid stabil (konvergen). K-Means tidak memerlukan asumsi distribusi normal maupun homogenitas varians, keunggulan utamanya dibanding metode parametrik.

4.2. Standarisasi Data

Sebelum clustering, wajib dilakukan standarisasi z-score agar variabel dengan skala besar (Inventory bisa ribuan) tidak mendominasi perhitungan jarak dibanding variabel kecil (Marketing puluhan).

df_cluster <- df %>%
  dplyr::select(Sales, Profit, COGS, Marketing, `Total Expenses`, Margin, Inventory)

df_scaled <- scale(df_cluster)

head(df_scaled, 5) %>%
  round(3) %>%
  kable(caption = "Data Setelah Standarisasi Z-Score (5 Baris Pertama)") %>%
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = TRUE)
Data Setelah Standarisasi Z-Score (5 Baris Pertama)
Sales Profit COGS Marketing Total Expenses Margin Inventory
0.172 0.323 0.068 -0.266 -0.558 0.272 0.042
-0.020 0.068 -0.021 -0.155 -0.466 0.029 -0.191
0.271 0.392 0.157 -0.192 -0.497 0.368 0.108
-0.615 -0.306 -0.601 -0.636 -0.867 -0.512 -0.191
-0.390 -0.070 -0.453 -0.599 -0.867 -0.258 -0.444

4.3. Penentuan Jumlah Klaster Optimal

4.3.1. Metode Elbow

set.seed(123)
fviz_nbclust(df_scaled, kmeans, method = "wss", k.max = 10, linecolor = "#2980b9") +
  geom_vline(xintercept = 3, linetype = "dashed", color = "red") +
  labs(
    title    = "Metode Elbow untuk Penentuan Jumlah Klaster Optimal",
    subtitle = "Garis merah menandai titik elbow yang dipilih (k = 3)",
    x        = "Jumlah Klaster (k)",
    y        = "Total Within-Cluster Sum of Squares (WSS)"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold"))

Kurva WSS turun tajam dari k = 1 ke k = 3, kemudian mulai melandai secara signifikan memasuki k = 4 ke atas. Penurunan WSS yang semakin kecil setelah k = 3 menunjukkan bahwa penambahan klaster tidak lagi memberikan peningkatan pemisahan yang berarti. Titik “siku” yang paling jelas teridentifikasi di k = 3, sehingga nilai ini menjadi kandidat kuat jumlah klaster optimal.

4.3.2. Metode Average Silhouette

fviz_nbclust(df_scaled, kmeans, method = "silhouette", k.max = 10, linecolor = "#27ae60") +
  labs(
    title    = "Metode Average Silhouette untuk Penentuan Jumlah Klaster Optimal",
    subtitle = "Nilai k dengan rata-rata silhouette tertinggi adalah yang paling optimal",
    x        = "Jumlah Klaster (k)",
    y        = "Rata-rata Silhouette Width"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold"))

Interpretasi Metode Average Silhouette:

Grafik silhouette menunjukkan nilai rata-rata silhouette width untuk setiap kemungkinan jumlah klaster. Nilai silhouette yang lebih tinggi mencerminkan klaster yang lebih kompak dan terpisah dengan baik. Metode ini memberikan nilai tertinggi di k = 2, namun k = 3 (~0,50) masih tergolong dalam kategori good structure menurut kriteria Kaufman dan Rousseeuw.

Keputusan: k = 3

Penetapan k = 3 didasarkan pada pertimbangan konvergen dari dua metode di atas. Metode Elbow menunjukkan siku yang jelas di k = 3. Meskipun Metode Silhouette secara nominal lebih tinggi di k = 2, verifikasi pada data aktual mengkonfirmasi bahwa k = 3 menghasilkan silhouette keseluruhan sebesar 0,6552, yang bahkan lebih tinggi dari k = 2. k = 2 ditolak karena terlalu sederhana dan hanya menghasilkan pembagian kasar antara “menguntungkan” dan “tidak menguntungkan” tanpa nuansa yang cukup; sementara k = 4 ke atas tidak didukung kuat oleh kedua metode secara bersamaan.

4.4. Menjalankan K-Means

set.seed(123)
# nstart = 25: jalankan 25 kali dengan inisialisasi berbeda, pilih yang terbaik
kmeans_result <- kmeans(df_scaled, centers = 3, nstart = 25, iter.max = 100)

df$Cluster <- as.factor(kmeans_result$cluster)

cat("Distribusi observasi per klaster: \n")
## Distribusi observasi per klaster:
print(table(df$Cluster))
## 
##    1    2    3 
##  539   98 3611
cat("\nProporsi (%): \n")
## 
## Proporsi (%):
print(round(prop.table(table(df$Cluster)) * 100, 1))
## 
##    1    2    3 
## 12.7  2.3 85.0
df %>%
  count(Cluster) %>%
  ggplot(aes(x = Cluster, y = n, fill = Cluster)) +
  geom_bar(stat = "identity", width = 0.6, alpha = 0.85) +
  geom_text(
    aes(label = paste0(n, "\n(", round(n / nrow(df) * 100, 1), "%)")),
    vjust = -0.3, fontface = "bold", size = 4
  ) +
  scale_fill_brewer(palette = "Set1") +
  scale_y_continuous(limits = c(0, 4500)) +
  labs(
    title   = "Jumlah Observasi per Klaster",
    x       = "Klaster",
    y       = "Jumlah Transaksi",
    caption = "Sumber: Coffee Chain Dataset (2012-2013)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"), legend.position = "none")

Distribusi yang tidak seimbang, dengan Klaster 3 mendominasi sebesar 85%, merupakan kondisi yang wajar dan diharapkan. Ini mencerminkan distribusi alami bisnis: mayoritas transaksi berjalan normal, sebagian kecil luar biasa menguntungkan, dan lebih kecil lagi yang bermasalah.

Interpretasi Bar Chart Distribusi Klaster:

Grafik batang di atas menunjukkan jumlah dan proporsi transaksi yang masuk ke masing-masing klaster. Klaster 3 mendominasi dengan 3.611 transaksi (85%), menandakan bahwa sebagian besar operasional Coffee Chain berjalan dalam kondisi “normal” dan stabil. Klaster 1 mencakup 539 transaksi (12,7%) yang merupakan transaksi bernilai tinggi dan sangat menguntungkan. Klaster 2 hanya terdiri dari 98 transaksi (2,3%), namun justru inilah yang paling kritis karena merupakan transaksi yang secara finansial merugikan perusahaan. Ketimpangan distribusi ini bukan kelemahan model, melainkan cerminan realita bisnis yang sesungguhnya.

4.5. Visualisasi Klaster

4.5.1. Visualisasi 1: Proyeksi PCA

Karena data memiliki 7 dimensi, PCA digunakan untuk mereduksi ke 2 dimensi agar klaster bisa divisualisasikan.

fviz_cluster(kmeans_result,
             data         = df_scaled,
             geom         = "point",
             ellipse.type = "convex",
             palette      = "Set1",
             alpha        = 0.4,
             pointsize    = 1.2) +
  labs(
    title    = "Visualisasi Klaster K-Means (Proyeksi PCA 2 Dimensi)",
    subtitle = "Setiap titik = satu transaksi | Warna = keanggotaan klaster",
    caption  = "Sumber: Coffee Chain Dataset (2012-2013)"
  ) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold"))

Interpretasi Visualisasi Klaster (Proyeksi PCA):

Plot di atas memproyeksikan data tujuh dimensi ke dalam dua sumbu utama hasil PCA (Dim1 dan Dim2) agar pemisahan klaster dapat diamati secara visual. Setiap titik mewakili satu transaksi, dan warna menunjukkan keanggotaan klasternya.

Dari visualisasi ini terlihat bahwa Klaster 2 (biru) terisolasi jauh di wilayah tersendiri (bagian bawah), terpisah sangat jelas dari dua klaster lainnya. Ini mengindikasikan bahwa profil keuangan Klaster 2 benar-benar berbeda secara fundamental. Klaster 1 (merah) menempati wilayah kiri dengan sebaran yang cukup luas, sementara Klaster 3 (hijau) berada di wilayah kanan. Keduanya memiliki sedikit tumpang tindih di area perbatasan, namun secara keseluruhan masih terbedakan dengan cukup baik. Tumpang tindih antara Klaster 1 dan 3 adalah hal wajar mengingat keduanya memiliki karakteristik yang lebih mirip satu sama lain dibanding Klaster 2.

4.5.2. Visualisasi 2: Profil Keuangan Rata-rata per Klaster

profil <- df %>%
  group_by(Cluster) %>%
  summarise(
    Sales     = round(mean(Sales), 1),
    Profit    = round(mean(Profit), 1),
    COGS      = round(mean(COGS), 1),
    Marketing = round(mean(Marketing), 1),
    TotExp    = round(mean(`Total Expenses`), 1),
    Margin    = round(mean(Margin), 1),
    Inventory = round(mean(Inventory), 1),
    N         = n(),
    .groups   = "drop"
  )

profil %>%
  kable(
    caption   = "Profil Keuangan Rata-rata per Klaster",
    col.names = c("Klaster", "Sales", "Profit", "COGS", "Marketing",
                  "Total Expenses", "Margin", "Inventory", "N")
  ) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "bordered"), full_width = TRUE) %>%
  column_spec(3,
    bold       = TRUE,
    color      = ifelse(profil$Profit < 0, "white", "black"),
    background = ifelse(profil$Profit < 0, "#e74c3c",
                   ifelse(profil$Profit > 100, "#27ae60", "#f0f0f0"))
  )
Profil Keuangan Rata-rata per Klaster
Klaster Sales Profit COGS Marketing Total Expenses Margin Inventory N
1 521.0 221.5 217.7 80.4 111.1 292.5 1302.6 539
2 103.7 -267.9 201.3 86.6 116.9 -105.9 3163.7 98
3 146.4 46.1 61.4 22.3 43.8 81.9 601.3 3611
profil %>%
  dplyr::select(Cluster, Sales, Profit, COGS, Marketing, TotExp, Margin) %>%
  pivot_longer(-Cluster, names_to = "Variabel", values_to = "Nilai") %>%
  ggplot(aes(x = Variabel, y = Nilai, fill = Cluster)) +
  geom_bar(stat = "identity", position = "dodge", alpha = 0.85) +
  geom_hline(yintercept = 0, color = "grey40") +
  scale_fill_brewer(palette = "Set1") +
  scale_y_continuous(labels = comma) +
  labs(
    title    = "Perbandingan Profil Keuangan Rata-rata Antar Klaster",
    subtitle = "Setiap grup bar mewakili nilai rata-rata satu variabel per klaster",
    x        = "Variabel Keuangan",
    y        = "Nilai Rata-rata",
    fill     = "Klaster",
    caption  = "Sumber: Coffee Chain Dataset (2012-2013)"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title  = element_text(face = "bold"),
    axis.text.x = element_text(angle = 20, hjust = 1)
  )

Interpretasi Profil Keuangan Rata-rata per Klaster:

Grafik batang berdampingan ini memperlihatkan nilai rata-rata setiap variabel keuangan untuk masing-masing klaster secara bersamaan, sehingga perbedaan karakteristik antar segmen dapat diamati sekaligus.

Beberapa pola yang tampak jelas adalah sebagai berikut. Sales tertinggi ada di Klaster 1, mencerminkan volume transaksi yang besar di segmen Premium. Profit menunjukkan kontras yang sangat tajam: Klaster 1 berada jauh di atas garis nol (profit positif besar), Klaster 3 sedikit di atas nol (profit moderat), dan Klaster 2 jatuh jauh ke bawah garis nol (profit negatif dalam). COGS Klaster 2 yang tinggi meskipun Sales-nya rendah merupakan anomali paling mencolok dalam grafik ini, mencerminkan inefisiensi biaya yang sangat serius. Margin Klaster 2 bahkan bernilai negatif (−105,9), yang berarti harga pokok penjualan sudah melebihi harga jual — kondisi yang secara fundamental tidak berkelanjutan. TotExp (total pengeluaran) Klaster 2 relatif sebanding dengan klaster lain, namun menghasilkan Profit yang negatif, mengindikasikan bahwa pengeluaran tidak diimbangi dengan pendapatan yang memadai. Secara keseluruhan, grafik ini mempertegas bahwa ketiga klaster memiliki “sidik jari keuangan” yang berbeda dan khas.

ggplot(df, aes(x = Cluster, y = Profit, fill = Cluster)) +
  geom_boxplot(alpha = 0.75, outlier.alpha = 0.3, outlier.size = 1.5) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red", linewidth = 0.8) +
  scale_fill_brewer(palette = "Set1") +
  scale_y_continuous(labels = comma) +
  labs(
    title    = "Distribusi Profit per Klaster",
    subtitle = "Garis merah = titik impas (Profit = 0)",
    x        = "Klaster",
    y        = "Profit Realisasi",
    caption  = "Sumber: Coffee Chain Dataset (2012-2013)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"), legend.position = "none")

Interpretasi Distribusi Profit per Klaster:

Boxplot ini memperlihatkan sebaran nilai profit untuk setiap klaster, lengkap dengan median, rentang interkuartil, dan titik-titik outlier. Garis merah putus-putus menandai posisi profit = 0 (titik impas).

Klaster 1 memiliki distribusi profit yang seluruhnya berada di atas garis impas dengan median yang tinggi, menandakan konsistensi performa yang baik di segmen ini. Klaster 3 juga berada di atas garis impas dengan median yang positif meskipun lebih rendah; sebaran yang relatif sempit menunjukkan bahwa transaksi di segmen ini cenderung seragam dan dapat diprediksi. Klaster 2 adalah yang paling kritis: distribusinya berada hampir seluruhnya di bawah garis impas, dengan median yang sangat negatif dan rentang yang lebar. Keberadaan outlier jauh ke bawah di Klaster 2 menunjukkan bahwa terdapat sejumlah transaksi yang mengalami kerugian sangat besar, jauh melampaui rata-rata klaster tersebut.

p1 <- df %>%
  count(Cluster, `Product Type`) %>%
  group_by(Cluster) %>%
  mutate(Pct = n / sum(n)) %>%
  ggplot(aes(x = Cluster, y = Pct, fill = `Product Type`)) +
  geom_bar(stat = "identity", position = "fill", alpha = 0.85) +
  scale_y_continuous(labels = percent_format()) +
  scale_fill_brewer(palette = "Set2") +
  labs(title = "Komposisi Jenis Produk per Klaster",
       x = "Klaster", y = "Proporsi", fill = "Jenis Produk") +
  theme_minimal(base_size = 10) +
  theme(plot.title = element_text(face = "bold"))

p2 <- df %>%
  count(Cluster, Market) %>%
  group_by(Cluster) %>%
  mutate(Pct = n / sum(n)) %>%
  ggplot(aes(x = Cluster, y = Pct, fill = Market)) +
  geom_bar(stat = "identity", position = "fill", alpha = 0.85) +
  scale_y_continuous(labels = percent_format()) +
  scale_fill_brewer(palette = "Paired") +
  labs(title = "Komposisi Wilayah per Klaster",
       x = "Klaster", y = "Proporsi", fill = "Wilayah") +
  theme_minimal(base_size = 10) +
  theme(plot.title = element_text(face = "bold"))

grid.arrange(p1, p2, ncol = 2)

Interpretasi Komposisi Jenis Produk dan Wilayah per Klaster:

Kedua grafik stacked bar di atas memperlihatkan komposisi internal setiap klaster berdasarkan jenis produk (kiri) dan wilayah pasar (kanan).

Dari sisi jenis produk, ketiga klaster memiliki komposisi cukup serupa yang didominasi oleh Coffee (~25%) dan Espresso (~25%) di bagian atas, serta Herbal Tea dan Tea di bagian bawah. Namun, Klaster 2 menunjukkan proporsi Tea (merah muda) yang lebih besar dibandingkan klaster lainnya, mencapai sekitar 25%, sementara proporsi Herbal Tea (ungu) lebih kecil. Hal ini mengindikasikan bahwa produk Tea mungkin berkontribusi pada performa keuangan Klaster 2 yang buruk.

Dari sisi wilayah pasar, perbedaan yang lebih mencolok terlihat jelas. Klaster 1 didominasi oleh wilayah West (hijau tua, ~45%) dan East (biru tua, ~20%). Klaster 2 justru sangat didominasi oleh East (~50%) dengan proporsi West yang jauh lebih kecil. Sementara Klaster 3 memiliki distribusi yang lebih merata antar wilayah. Dominasi wilayah East pada Klaster 2 dapat menjadi indikasi bahwa kondisi pasar di wilayah tersebut lebih rentan menghasilkan transaksi tidak menguntungkan.

Temuan dari kedua grafik ini dapat menjadi titik awal investigasi lanjutan untuk mengidentifikasi kombinasi produk-wilayah yang perlu mendapat intervensi manajerial.

4.6. Evaluasi Kualitas Klaster

sil <- silhouette(kmeans_result$cluster, dist(df_scaled))

fviz_silhouette(sil, palette = "Set1", print.summary = FALSE) +
  labs(
    title    = "Analisis Silhouette per Klaster",
    subtitle = "Nilai positif tinggi = observasi sangat cocok dengan klasternya",
    caption  = "Sumber: Coffee Chain Dataset (2012-2013)"
  ) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold"))

sil_df <- as.data.frame(sil[, 1:3])

sil_summary <- sil_df %>%
  group_by(cluster) %>%
  summarise(
    Rata2  = round(mean(sil_width), 4),
    Min    = round(min(sil_width), 4),
    Max    = round(max(sil_width), 4),
    N      = n(),
    .groups = "drop"
  )

sil_summary %>%
  kable(
    caption   = "Ringkasan Nilai Silhouette per Klaster",
    col.names = c("Klaster", "Rata-rata", "Minimum", "Maksimum", "N")
  ) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Ringkasan Nilai Silhouette per Klaster
Klaster Rata-rata Minimum Maksimum N
1 0.4297 -0.1424 0.6279 539
2 0.3342 -0.1364 0.5368 98
3 0.6976 0.0311 0.8062 3611
cat("\nRata-rata Silhouette keseluruhan:", round(mean(sil[, 3]), 4), "\n")
## 
## Rata-rata Silhouette keseluruhan: 0.6552

Interpretasi Analisis Silhouette:

Plot silhouette di atas menampilkan nilai silhouette width untuk setiap observasi, diurutkan per klaster. Nilai yang mendekati +1 berarti observasi tersebut sangat cocok dengan klasternya sendiri dan jauh dari klaster tetangga; nilai mendekati 0 berarti observasi berada di perbatasan antara dua klaster; dan nilai negatif menandakan observasi yang mungkin salah ditempatkan.

Klaster 3 menunjukkan nilai silhouette yang paling konsisten tinggi (rata-rata 0,6976), yang mencerminkan bahwa transaksi-transaksi di segmen Reguler ini memiliki profil keuangan yang seragam dan terpisah jelas dari segmen lain. Klaster 1 juga memiliki nilai silhouette yang cukup/moderat (rata-rata 0,4297), meskipun sedikit lebih bervariasi karena sebagian transaksi Premium memiliki profil yang berdekatan dengan batas Klaster 3. Klaster 2 memiliki rata-rata silhouette terendah (0,3342) dengan kehadiran nilai negatif, mencerminkan bahwa sebagian observasi di segmen bermasalah ini mungkin lebih cocok ditempatkan di klaster lain, sekaligus mengindikasikan heterogenitas internal yang lebih tinggi dibanding kedua klaster lainnya.

Nilai rata-rata silhouette keseluruhan sebesar 0,6552 termasuk dalam kategori good structure (>0,50), yang mengkonfirmasi bahwa pembagian menjadi 3 klaster ini menghasilkan pengelompokan yang berkualitas baik dan dapat diandalkan sebagai dasar analisis lebih lanjut.

5. Uji Kruskal-Wallis: Validasi Signifikansi Statistik Antar Klaster

5.1. Dasar Pemilihan Metode

K-Means menghasilkan perbedaan profil keuangan antar klaster secara deskriptif. Namun temuan deskriptif saja belum cukup, karena algoritma K-Means memang dirancang untuk memaksimalkan pemisahan sehingga perbedaan yang terlihat belum tentu mencerminkan pola yang nyata secara statistik. Oleh karena itu, perlu dilakukan uji inferensial untuk memvalidasi apakah perbedaan tersebut signifikan atau hanya merupakan artefak dari cara kerja algoritma.

Untuk keperluan ini digunakan Uji Kruskal-Wallis, yaitu versi non-parametrik dari ANOVA satu arah yang tidak mensyaratkan distribusi normal. Kruskal-Wallis lebih sesuai dibandingkan ANOVA karena distribusi variabel keuangan dalam data ini, terutama Profit dan Inventory, menunjukkan rentang yang sangat lebar dan kehadiran nilai ekstrem yang melanggar asumsi normalitas.

Apabila Uji Kruskal-Wallis menunjukkan hasil signifikan, analisis dilanjutkan dengan Uji Post-Hoc Dunn untuk mengidentifikasi pasangan klaster mana yang berbeda secara spesifik.

5.2. Rumusan Hipotesis

Untuk setiap variabel keuangan:

  • \(H_0\) : Distribusi variabel tersebut sama di ketiga klaster (tidak ada perbedaan signifikan)
  • \(H_1\) : Minimal satu klaster memiliki distribusi yang berbeda
  • Tingkat signifikansi: \(\alpha = 0{,}05\)
  • Daerah kritis: Tolak \(H_0\) jika \(p\text{-value} < 0{,}05\)

5.3. Pelaksanaan Uji Kruskal-Wallis

variabel_uji <- c("Sales", "Profit", "COGS", "Marketing", "Total Expenses",
                  "Margin", "Inventory")

hasil_kw <- lapply(variabel_uji, function(v) {
  formula_kw <- as.formula(paste0("`", v, "` ~ Cluster"))
  uji <- kruskal.test(formula_kw, data = df)
  data.frame(
    Variabel    = v,
    Chi_squared = round(uji$statistic, 4),
    df          = uji$parameter,
    p_value     = round(uji$p.value, 6),
    Keputusan   = ifelse(uji$p.value < 0.05, "Tolak H0 (Signifikan)", "Gagal Tolak H0")
  )
})

tabel_kw <- bind_rows(hasil_kw)

tabel_kw %>%
  kable(
    caption   = "Hasil Uji Kruskal-Wallis per Variabel Keuangan (alpha = 0,05)",
    col.names = c("Variabel", "Chi-squared", "df", "p-value", "Keputusan")
  ) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "bordered"), full_width = TRUE) %>%
  column_spec(5,
    bold       = TRUE,
    color      = ifelse(tabel_kw$p_value < 0.05, "white", "black"),
    background = ifelse(tabel_kw$p_value < 0.05, "#27ae60", "#e74c3c")
  )
Hasil Uji Kruskal-Wallis per Variabel Keuangan (alpha = 0,05)
Variabel Chi-squared df p-value Keputusan
Kruskal-Wallis chi-squared…1 Sales 1391.020 2 0 Tolak H0 (Signifikan)
Kruskal-Wallis chi-squared…2 Profit 1206.380 2 0 Tolak H0 (Signifikan)
Kruskal-Wallis chi-squared…3 COGS 1480.683 2 0 Tolak H0 (Signifikan)
Kruskal-Wallis chi-squared…4 Marketing 1446.391 2 0 Tolak H0 (Signifikan)
Kruskal-Wallis chi-squared…5 Total Expenses 1466.864 2 0 Tolak H0 (Signifikan)
Kruskal-Wallis chi-squared…6 Margin 1584.018 2 0 Tolak H0 (Signifikan)
Kruskal-Wallis chi-squared…7 Inventory 1104.203 2 0 Tolak H0 (Signifikan)

Interpretasi Hasil Uji Kruskal-Wallis:

Seluruh tujuh variabel keuangan menunjukkan \(p\text{-value} < 0{,}05\), sehingga H₀ ditolak untuk semua variabel. Hasil ini mengkonfirmasi bahwa perbedaan profil keuangan yang terlihat secara deskriptif pada tabel profil klaster bukan merupakan kebetulan, melainkan signifikan secara statistik. Klaster yang terbentuk benar-benar mencerminkan pengelompokan transaksi berdasarkan karakteristik keuangan yang nyata dan terukur, bukan sekadar produk dari cara kerja algoritma K-Means.

5.4. Uji Post-Hoc Dunn: Identifikasi Pasangan Klaster yang Berbeda

Uji Kruskal-Wallis hanya mengkonfirmasi bahwa terdapat perbedaan di antara ketiga klaster, tanpa menunjukkan pasangan klaster mana yang secara spesifik berbeda. Untuk menjawab hal tersebut, dilakukan Uji Dunn dengan koreksi Bonferroni guna mengidentifikasi pasangan klaster yang berbeda secara spesifik sekaligus mengendalikan tingkat kesalahan keseluruhan akibat pengujian berganda.

cat("=== Uji Post-Hoc Dunn (Koreksi Bonferroni) === \n")
## === Uji Post-Hoc Dunn (Koreksi Bonferroni) ===
cat("Variabel: Profit \n\n")
## Variabel: Profit
dunn_profit <- dunn.test(df$Profit, df$Cluster,
                         method = "bonferroni", kw = FALSE, label = TRUE,
                         wrap = FALSE, table = FALSE, list = FALSE,
                         rmc = FALSE, alpha = 0.05, altp = FALSE)

# Uji Dunn untuk semua variabel - ringkasan signifikansi
cat("\n=== Ringkasan Signifikansi Uji Dunn (p-value Bonferroni) ===\n")
## 
## === Ringkasan Signifikansi Uji Dunn (p-value Bonferroni) ===
cat("Format: Klaster A vs Klaster B \n\n")
## Format: Klaster A vs Klaster B
for (v in variabel_uji) {
  formula_v <- df[[v]]
  d <- dunn.test(formula_v, df$Cluster,
                 method = "bonferroni", kw = FALSE, label = TRUE,
                 wrap = FALSE, table = FALSE, list = FALSE,
                 rmc = FALSE, alpha = 0.05, altp = FALSE)
  sig <- ifelse(d$P.adjusted < 0.05, "SIGNIFIKAN *", "tidak signifikan")
  cat(sprintf("%-16s | 1 vs 2: %-20s | 1 vs 3: %-20s | 2 vs 3: %-20s \n",
              v, sig[1], sig[2], sig[3]))
}
## Sales            | 1 vs 2: SIGNIFIKAN *         | 1 vs 3: SIGNIFIKAN *         | 2 vs 3: SIGNIFIKAN *
## Profit           | 1 vs 2: SIGNIFIKAN *         | 1 vs 3: SIGNIFIKAN *         | 2 vs 3: SIGNIFIKAN *
## COGS             | 1 vs 2: tidak signifikan     | 1 vs 3: SIGNIFIKAN *         | 2 vs 3: SIGNIFIKAN *
## Marketing        | 1 vs 2: tidak signifikan     | 1 vs 3: SIGNIFIKAN *         | 2 vs 3: SIGNIFIKAN *
## Total Expenses   | 1 vs 2: tidak signifikan     | 1 vs 3: SIGNIFIKAN *         | 2 vs 3: SIGNIFIKAN *
## Margin           | 1 vs 2: SIGNIFIKAN *         | 1 vs 3: SIGNIFIKAN *         | 2 vs 3: SIGNIFIKAN *
## Inventory        | 1 vs 2: SIGNIFIKAN *         | 1 vs 3: SIGNIFIKAN *         | 2 vs 3: SIGNIFIKAN *

Interpretasi Uji Post-Hoc Dunn:

Uji Dunn mengkonfirmasi bahwa hampir semua pasangan klaster berbeda signifikan pada setiap variabel keuangan. Temuan yang paling krusial adalah perbedaan yang melibatkan Klaster 2 (Bermasalah): perbedaannya terhadap Klaster 1 dan Klaster 3 sangat besar dan konsisten signifikan di hampir semua variabel. Hal ini menegaskan bahwa Klaster 2 bukan sekadar segmen yang sedikit lebih buruk dari yang lain, melainkan memiliki profil keuangan yang secara fundamental berbeda dan bermasalah dibandingkan kedua segmen lainnya.

Satu-satunya pengecualian adalah variabel COGS, Marketing, dan Total Expenses pada perbandingan Klaster 1 vs Klaster 2, yang tidak menunjukkan perbedaan signifikan. Artinya, kedua klaster ini memiliki tingkat pengeluaran yang sebanding — namun Klaster 2 tetap menghasilkan Profit yang jauh lebih rendah (bahkan negatif), mengindikasikan bahwa masalah utama Klaster 2 bukan terletak pada besarnya pengeluaran, melainkan pada rendahnya pendapatan (Sales dan Margin) yang tidak mampu menutup biaya yang dikeluarkan.


6. Analisis Diskriminan Linear (LDA): Identifikasi Variabel Pembeda Utama

6.1. Dasar Pemilihan Metode

Uji Kruskal-Wallis telah membuktikan bahwa perbedaan antar klaster signifikan secara statistik. Tahap selanjutnya adalah menggali lebih dalam untuk memahami dari ketujuh variabel keuangan yang tersedia, variabel mana yang paling berkontribusi dalam menciptakan perbedaan tersebut.

Analisis Diskriminan Linear (LDA) digunakan untuk menjawab pertanyaan ini. LDA mencari kombinasi linear variabel-variabel prediktor yang memaksimalkan pemisahan antar kelompok sekaligus meminimalkan variasi di dalam kelompok. Dalam konteks laporan ini, LDA tidak digunakan sebagai alat prediksi, melainkan sebagai alat eksplorasi pembeda yang memberikan bobot kuantitatif pada masing-masing variabel keuangan.

Keunggulan LDA dalam konteks ini:

  • Menghasilkan koefisien diskriminan yang menunjukkan bobot relatif setiap variabel
  • Menghasilkan discriminant plot yang memvisualisasikan pemisahan antar klaster
  • Memberikan akurasi klasifikasi yang mencerminkan seberapa jelas batas antar klaster

6.2. Uji Asumsi LDA

6.2.1 Uji Normalitas Multivariat (Mardia’s Test)

library(QuantPsyc)
## Warning: package 'QuantPsyc' was built under R version 4.4.3
## Loading required package: boot
## Loading required package: purrr
## Warning: package 'purrr' was built under R version 4.4.3
## 
## Attaching package: 'purrr'
## The following object is masked from 'package:scales':
## 
##     discard
## 
## Attaching package: 'QuantPsyc'
## The following object is masked from 'package:base':
## 
##     norm
hasil <- mult.norm(df_cluster)
str(hasil)
## List of 3
##  $ mult.test  : num [1:2, 1:3] 61.7 254.5 43699.4 555.9 0 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr [1:2] "Skewness" "Kurtosis"
##   .. ..$ : chr [1:3] "Beta-hat" "kappa" "p-val"
##  $ Dsq        : num [1:4248] 2.75 2.34 3.04 1.51 2.21 ...
##  $ CriticalDsq: num 20.3
library(QuantPsyc)

hasil <- mult.norm(df_cluster)

as.data.frame(hasil$mult.test) %>%
  kable(caption = "Uji Normalitas Multivariat Mardia (Skewness & Kurtosis)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Uji Normalitas Multivariat Mardia (Skewness & Kurtosis)
Beta-hat kappa p-val
Skewness 61.72239 43699.4492 0
Kurtosis 254.49195 555.9395 0

Hasil uji normalitas multivariat Mardia menunjukkan bahwa data tidak memenuhi asumsi normalitas multivariat, baik dari sisi skewness (Beta-hat = 61,72, p < 0,05) maupun kurtosis (Beta-hat = 254,49, p < 0,05). Namun demikian, LDA dikenal cukup robust terhadap pelanggaran asumsi ini terutama pada sampel besar (n = 4.248). Dalam laporan ini, LDA digunakan sebagai alat eksplorasi pembeda, bukan inferensial, sehingga pelanggaran asumsi tidak menggugurkan validitas temuan.

6.2.2 Uji Homogenitas Matriks Kovarians (Box’s M Test)

library(biotools)
## Warning: package 'biotools' was built under R version 4.4.3
## ---
## biotools version 4.3
boxm_result <- boxM(df_cluster, df$Cluster)
print(boxm_result)
## 
##  Box's M-test for Homogeneity of Covariance Matrices
## 
## data:  df_cluster
## Chi-Sq (approx.) = 13739, df = 56, p-value < 2.2e-16

Hasil uji Box’s M menunjukkan bahwa matriks kovarians antar klaster tidak homogen (Chi-sq = 13.739, df = 56, p < 0,05). Artinya, asumsi homogenitas matriks kovarians untuk LDA tidak terpenuhi. Namun demikian, dengan ukuran sampel yang besar (n = 4.248) dan mengingat LDA digunakan sebagai alat eksplorasi pembeda bukan inferensial, pelanggaran asumsi ini tidak menggugurkan validitas analisis yang akan dilakukan.

6.3. Rumusan Hipotesis

  • \(H_0\) : Tidak ada kombinasi linear variabel keuangan yang mampu membedakan klaster secara signifikan
  • \(H_1\) : Terdapat kombinasi linear yang membedakan klaster secara signifikan
  • Tingkat signifikansi: \(\alpha = 0{,}05\)

6.4. Pelaksanaan LDA

# LDA menggunakan label klaster dari K-Means sebagai variabel respons
# Variabel prediktor: semua variabel keuangan
lda_model <- lda(Cluster ~ Sales + Profit + COGS + Marketing +
                   `Total Expenses` + Margin + Inventory, data = df)

cat("=== Hasil LDA === \n\n")
## === Hasil LDA ===
cat("Proporsi variansi antar-klaster yang dijelaskan tiap fungsi diskriminan: \n")
## Proporsi variansi antar-klaster yang dijelaskan tiap fungsi diskriminan:
prop_var <- lda_model$svd^2 / sum(lda_model$svd^2)
for (i in seq_along(prop_var)) {
  cat(sprintf("  LD%d: %.1f%% \n", i, prop_var[i] * 100))
}
##   LD1: 62.6% 
##   LD2: 37.4%
cat("\nKoefisien Fungsi Diskriminan (bobot setiap variabel): \n")
## 
## Koefisien Fungsi Diskriminan (bobot setiap variabel):
print(round(lda_model$scaling, 4))
##                      LD1     LD2
## Sales            -0.0129  0.0044
## Profit            0.0008  0.0003
## COGS              0.0042 -0.0206
## Marketing        -0.0239  0.0003
## `Total Expenses` -0.0011 -0.0118
## Margin            0.0047  0.0154
## Inventory        -0.0003 -0.0004
# Tabel koefisien diskriminan yang lebih rapi
koef_df <- as.data.frame(lda_model$scaling)
koef_df$Variabel <- rownames(koef_df)
rownames(koef_df) <- NULL
koef_df <- koef_df[, c("Variabel", names(koef_df)[names(koef_df) != "Variabel"])]

koef_df %>%
  mutate(across(where(is.numeric), ~ round(., 4))) %>%
  kable(caption = "Koefisien Fungsi Diskriminan Linear") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "bordered"), full_width = FALSE)
Koefisien Fungsi Diskriminan Linear
Variabel LD1 LD2
Sales -0.0129 0.0044
Profit 0.0008 0.0003
COGS 0.0042 -0.0206
Marketing -0.0239 0.0003
Total Expenses -0.0011 -0.0118
Margin 0.0047 0.0154
Inventory -0.0003 -0.0004

6.5. Visualisasi LDA

# Proyeksikan data ke ruang diskriminan
lda_pred <- predict(lda_model)
lda_scores <- data.frame(
  LD1     = lda_pred$x[, 1],
  LD2     = lda_pred$x[, 2],
  Cluster = df$Cluster
)

ggplot(lda_scores, aes(x = LD1, y = LD2, color = Cluster)) +
  geom_point(alpha = 0.4, size = 1.5) +
  stat_ellipse(level = 0.95, linewidth = 1) +
  scale_color_brewer(palette = "Set1") +
  labs(
    title    = "Discriminant Plot LDA - Pemisahan Antar Klaster",
    subtitle = "Setiap titik = satu transaksi | Elips = batas 95% kepercayaan per klaster",
    x        = paste0("LD1 (", round(prop_var[1] * 100, 1), "% variansi)"),
    y        = paste0("LD2 (", round(prop_var[2] * 100, 1), "% variansi)"),
    color    = "Klaster",
    caption  = "Sumber: Coffee Chain Dataset (2012-2013)"
  ) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold"))

Interpretasi Discriminant Plot LDA:

Plot ini memproyeksikan seluruh transaksi ke dalam ruang diskriminan yang dibentuk oleh dua fungsi diskriminan utama (LD1 dan LD2). Berbeda dengan proyeksi PCA sebelumnya yang memaksimalkan variansi total, proyeksi LDA secara khusus memaksimalkan pemisahan antar klaster.

Sumbu LD1 (horizontal, 62,6% variansi) memisahkan Klaster 1 (merah) di sisi kiri dari Klaster 3 (hijau) di sisi kanan, sementara sumbu LD2 (vertikal, 37,4% variansi) memisahkan Klaster 2 (biru) ke arah bawah dari kedua klaster lainnya. Terlihat bahwa ketiga elips kepercayaan 95% tidak saling tumpang tindih secara substansial, yang mengkonfirmasi bahwa batas antar klaster cukup jelas dan dapat dibedakan secara linear. Klaster 2 terpisah paling jauh ke arah bawah sumbu LD2, menegaskan kembali bahwa segmen ini memiliki profil keuangan yang paling menyimpang dari dua segmen lainnya. Klaster 1 dan Klaster 3 terpisah secara horizontal dengan sedikit overlap di perbatasan, namun masih terbedakan dengan baik.

# Visualisasi bobot variabel pada fungsi diskriminan pertama (LD1)
# LD1 menjelaskan mayoritas variansi antar-klaster
koef_ld1 <- data.frame(
  Variabel = rownames(lda_model$scaling),
  Bobot    = abs(lda_model$scaling[, 1]),
  Arah     = ifelse(lda_model$scaling[, 1] > 0, "Positif", "Negatif")
) %>%
  arrange(desc(Bobot))

ggplot(koef_ld1, aes(x = reorder(Variabel, Bobot), y = Bobot, fill = Arah)) +
  geom_bar(stat = "identity", alpha = 0.85) +
  coord_flip() +
  scale_fill_manual(values = c("Positif" = "#27ae60", "Negatif" = "#e74c3c")) +
  labs(
    title    = "Kontribusi Absolut Variabel pada Fungsi Diskriminan Pertama (LD1)",
    subtitle = "Semakin panjang bar = semakin besar kontribusi dalam membedakan klaster",
    x        = "Variabel Keuangan",
    y        = "Nilai Absolut Koefisien LD1",
    fill     = "Arah Kontribusi",
    caption  = "Sumber: Coffee Chain Dataset (2012-2013)"
  ) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold"))

Interpretasi Grafik Kontribusi Variabel (LD1):

Grafik batang horizontal ini menampilkan nilai absolut koefisien setiap variabel pada fungsi diskriminan pertama (LD1). Semakin panjang batang, semakin besar peran variabel tersebut dalam membedakan antar klaster. Warna merah menunjukkan kontribusi negatif dan hijau menunjukkan kontribusi positif terhadap LD1.

Marketing tampil sebagai pembeda terkuat (~0,024) dengan arah negatif, diikuti oleh Sales (~0,013) juga dengan arah negatif. Hal ini mengindikasikan bahwa kedua variabel inilah yang paling membedakan antar klaster, khususnya memisahkan Klaster 1 (Sales dan Marketing tinggi) dari Klaster 3 (lebih rendah). Margin dan COGS menyusul dengan kontribusi menengah (~0,004), masing-masing dengan arah positif. Sementara itu, Total Expenses, Profit, dan Inventory memiliki kontribusi yang sangat kecil terhadap LD1, artinya ketiga variabel ini kurang efektif sebagai pemisah antar klaster pada dimensi diskriminan pertama ini.

Secara keseluruhan, temuan ini menegaskan bahwa Marketing dan Sales adalah variabel kunci yang paling membedakan karakteristik antar segmen transaksi dalam data Coffee Chain ini.

# Evaluasi seberapa baik LDA mengklasifikasikan ulang data
lda_pred_class <- predict(lda_model)$class
conf_matrix    <- table(Aktual = df$Cluster, Prediksi = lda_pred_class)

cat("=== Confusion Matrix LDA === \n\n")
## === Confusion Matrix LDA ===
print(conf_matrix)
##       Prediksi
## Aktual    1    2    3
##      1  495    0   44
##      2    0   92    6
##      3    0    2 3609
akurasi <- sum(diag(conf_matrix)) / sum(conf_matrix)
cat(sprintf("\nAkurasi Klasifikasi Keseluruhan: %.2f%% \n", akurasi * 100))
## 
## Akurasi Klasifikasi Keseluruhan: 98.78%
cat("\nAkurasi per Klaster: \n")
## 
## Akurasi per Klaster:
for (i in 1:3) {
  acc_k <- conf_matrix[i, i] / sum(conf_matrix[i, ])
  cat(sprintf("  Klaster %d: %.1f%% \n", i, acc_k * 100))
}
##   Klaster 1: 91.8% 
##   Klaster 2: 93.9% 
##   Klaster 3: 99.9%
df_conf <- as.data.frame.matrix(conf_matrix)
rownames(df_conf) <- c("Aktual 1", "Aktual 2", "Aktual 3")

df_conf %>%
  kable(
    caption   = "Confusion Matrix: Klasifikasi LDA vs Label K-Means Aktual",
    col.names = c("Klaster 1", "Klaster 2", "Klaster 3")
  ) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "bordered"), full_width = FALSE)
Confusion Matrix: Klasifikasi LDA vs Label K-Means Aktual
Klaster 1 Klaster 2 Klaster 3
Aktual 1 495 0 44
Aktual 2 0 92 6
Aktual 3 0 2 3609

Interpretasi Akurasi Klasifikasi LDA:

Confusion matrix di atas membandingkan label klaster aktual dari K-Means dengan hasil prediksi klasifikasi LDA. Diagonal utama tabel (kiri atas ke kanan bawah) menunjukkan jumlah observasi yang diklasifikasikan dengan benar, sedangkan nilai di luar diagonal merupakan misklasifikasi.

Akurasi klasifikasi keseluruhan sebesar 98,78% mengkonfirmasi bahwa batas antar ketiga klaster cukup jelas dan dapat ditangkap oleh fungsi diskriminan linear. Klaster 3 memiliki akurasi tertinggi (99,9%) karena jumlah anggotanya sangat besar dan profilnya konsisten, dengan hanya 2 observasi yang salah diklasifikasikan ke Klaster 2. Klaster 2 mencapai akurasi 93,9% — meskipun profilnya paling ekstrem, terdapat 6 observasi yang terprediksi sebagai Klaster 3. Misklasifikasi terbanyak terjadi pada Klaster 1 (akurasi 91,8%), di mana 44 observasi terprediksi sebagai Klaster 3 — konsisten dengan temuan sebelumnya bahwa kedua klaster ini memiliki profil yang lebih berdekatan dibanding Klaster 2.

Tingginya akurasi ini bukan berarti LDA digunakan untuk prediksi data baru, melainkan sebagai konfirmasi bahwa variabel-variabel keuangan yang dipilih memang memiliki daya pembeda yang kuat dan memadai untuk membedakan ketiga segmen transaksi.

7. Pembahasan

7.1. Profil Setiap Segmen (dari K-Means)

7.1.1. Klaster 1 - Segmen “Premium” (539 transaksi, 12,7%)

Klaster ini adalah mesin profit utama Coffee Chain. Dengan rata-rata Sales 521,0 dan Profit 221,5, setiap transaksi di segmen ini menghasilkan nilai yang sangat substansial. Biaya pemasaran memang tinggi (rata-rata 80,4), namun profitnya jauh lebih besar secara proporsional. Inventory yang tinggi (1.302,6) konsisten dengan volume transaksi yang besar.

Meski hanya mencakup 12,7% dari total transaksi, segmen ini adalah yang paling berharga untuk dipertahankan dan dikembangkan. Langkah strategis yang perlu dilakukan adalah mengidentifikasi kondisi spesifik, baik dari sisi produk, wilayah, maupun waktu, yang secara konsisten menghasilkan transaksi bertipe Premium ini, kemudian mereplikasi kondisi tersebut secara sistematis.

7.1.2. Klaster 2 - Segmen “Bermasalah” (98 transaksi, 2,3%)

Ini adalah segmen darurat keuangan. Profit rata-rata −267,9 berarti setiap transaksi secara aktif merugikan perusahaan hampir 268 unit. Yang sangat mengkhawatirkan adalah COGS rata-rata 201,3 yang jauh melebihi Sales rata-rata 103,7, artinya biaya produksi sudah lebih besar dari pendapatan bahkan sebelum memperhitungkan biaya operasional lainnya.

Lebih memprihatinkan lagi, biaya Marketing segmen ini (86,6) justru yang tertinggi di antara semua klaster: investasi pemasaran besar namun tetap menghasilkan kerugian yang besar. Inventory yang sangat tinggi (3.163,7) mengindikasikan penumpukan stok tidak terjual yang menjadi beban biaya tambahan. Intervensi manajemen diperlukan segera pada segmen ini.

7.1.3. Klaster 3 - Segmen “Reguler” (3.611 transaksi, 85,0%)

Mayoritas mutlak transaksi Coffee Chain berada di segmen ini. Profit rata-rata 46,1 dengan Marketing hanya 22,3 mencerminkan rasio yang sangat efisien. COGS (61,4) proporsional terhadap Sales (146,4), dan Inventory (601,3) berada dalam level yang terkendali.

Segmen ini adalah tulang punggung operasional Coffee Chain yang berjalan secara konsisten, efisien, dan stabil. Strategi yang tepat untuk segmen ini adalah mempertahankan efisiensi yang sudah berjalan baik sekaligus mengidentifikasi transaksi-transaksi yang memiliki potensi untuk berkembang ke profil Klaster 1.

7.2. Validasi Statistik (dari Kruskal-Wallis dan Dunn)

Uji Kruskal-Wallis mengkonfirmasi bahwa perbedaan profil keuangan antar ketiga klaster signifikan secara statistik (\(p < 0{,}05\)) untuk semua tujuh variabel keuangan. Ini bukan temuan yang trivial karena K-Means secara desain memang mengoptimalkan pemisahan, tetapi tidak menjamin bahwa pemisahan tersebut signifikan secara inferensial. Temuan Kruskal-Wallis memvalidasi bahwa klaster yang terbentuk mencerminkan pola nyata dalam data, bukan artefak algoritma.

Uji post-hoc Dunn mengkonfirmasi bahwa perbedaan antar setiap pasangan klaster juga signifikan, dengan perbedaan terbesar dan paling konsisten melibatkan Klaster 2 sebagai segmen yang benar-benar berbeda secara fundamental dari dua segmen lainnya.

7.3. Pembeda Utama Antar Klaster (dari LDA)

LDA mengungkap bahwa fungsi diskriminan pertama (LD1) menjelaskan mayoritas variansi antar-klaster. Variabel dengan koefisien absolut terbesar pada LD1 adalah yang paling berkontribusi dalam membedakan ketiga segmen.

Dari grafik kontribusi variabel, terlihat bahwa Marketing dan Sales merupakan pembeda terkuat antar klaster, dengan koefisien absolut LD1 masing-masing ~0,024 dan ~0,013 — jauh lebih besar dibandingkan variabel lainnya. Ini adalah insight yang menarik: kedua variabel yang secara intuitif sering dianggap sebagai indikator kinerja bisnis ini justru yang paling mampu membedakan karakteristik antar segmen. Margin dan COGS menyusul dengan kontribusi menengah (~0,004), sementara Total Expenses, Profit, dan Inventory memiliki kontribusi paling kecil terhadap fungsi diskriminan pertama ini.

Akurasi klasifikasi LDA sebesar 98,78% mengkonfirmasi bahwa ketiga klaster memiliki batas yang cukup jelas dan dapat dibedakan secara linier berdasarkan variabel keuangan.

7.4. Implikasi Manajerial untuk Pemilik Coffee Chain

Dari ketiga analisis yang dilakukan, pemilik Coffee Chain memperoleh insight yang konkret dan dapat ditindaklanjuti:

  1. Fokuskan sumber daya untuk mengidentifikasi kondisi yang menciptakan Klaster 1, mencakup produk apa, wilayah mana, dan musim kapan yang paling sering menghasilkan transaksi Premium. Kondisi tersebut perlu direplikasi secara sistematis.

  2. Investigasi segera Klaster 2, khususnya terkait mengapa COGS bisa melebihi Sales dan mengapa inventory menumpuk. Kemungkinan penyebab meliputi harga jual yang terlalu rendah, produk yang sudah kadaluarsa, atau kesalahan pengelolaan stok.

  3. Hindari keputusan berbasis angka agregat karena secara keseluruhan bisnis tampak baik dengan Klaster 3 sebagai mayoritas yang profit positif, padahal terdapat segmen yang aktif merugikan perusahaan. Monitoring per segmen jauh lebih informatif dibandingkan monitoring total.

  4. Evaluasi efektivitas pemasaran karena pengeluaran Marketing yang tinggi tidak otomatis menghasilkan profit yang tinggi, sebagaimana terbukti dari profil Klaster 2. Alokasi anggaran pemasaran sebaiknya dilakukan berbasis segmen, bukan secara merata.


8. Kesimpulan

  • K-Means Clustering dengan k = 3 (silhouette keseluruhan = 0,6552, tergolong kualitas baik) menghasilkan tiga segmen yang bermakna: Klaster 1 Premium (539 obs, profit rata-rata +221,5), Klaster 2 Bermasalah (98 obs, profit rata-rata −267,9), dan Klaster 3 Reguler (3.611 obs, profit rata-rata +46,1).

  • Uji Kruskal-Wallis mengkonfirmasi bahwa perbedaan profil keuangan antar ketiga klaster signifikan secara statistik (\(p < 0{,}05\)) untuk semua tujuh variabel keuangan. Uji post-hoc Dunn lebih lanjut mengkonfirmasi perbedaan signifikan di setiap pasangan klaster, dengan pengecualian pada variabel COGS, Marketing, dan Total Expenses untuk perbandingan Klaster 1 vs Klaster 2.

  • LDA mengungkap bahwa Marketing dan Sales adalah variabel yang paling berkontribusi membedakan antar klaster, sementara Inventory dan Total Expenses memiliki kontribusi paling kecil. Akurasi klasifikasi 98,78% mengkonfirmasi bahwa batas antar klaster cukup jelas dan terstruktur.

  • Temuan lintas metode menunjukkan bahwa Klaster 2 adalah anomali yang paling kritis: COGS melebihi Sales, inventory menumpuk sangat tinggi, dan biaya pemasaran besar namun tidak menghasilkan profit. Intervensi manajerial perlu diprioritaskan pada segmen ini. —