Kita kini memiliki tiga algoritme clustering dengan cara kerja dan asumsi yang berbeda — tetapi belum punya cara menilai hasilnya secara adil. Tanpa label kebenaran, akurasi tidak bisa dihitung; memeriksa profil klaster satu per satu melelahkan dan subjektif. Indeks silhouette mengisi kekosongan itu dengan satu pertanyaan sederhana untuk setiap titik: apakah titik ini lebih dekat ke klasternya sendiri dibanding ke klaster tetangga terdekat? Jawaban tiap titik dirata-ratakan menjadi satu angka yang bisa membandingkan k, algoritme, bahkan preprocessing yang berbeda. Chapter ini membedah rumusnya sampai hitungan tangan — data yang dipakai masih 120 pelanggan toko ritel dari chapter k-Means.

Rumus: Seberapa Nyaman Sebuah Titik #

Untuk sebuah titik i, silhouette menghitung dua jarak rata-rata:

text
a(i) = jarak rata-rata i ke SELURUH titik lain di klasternya sendiri
b(i) = jarak rata-rata i ke titik-titik klaster lain yang paling dekat
s(i) = (b(i) - a(i)) / max(a(i), b(i))

Nilai a kecil berarti klaster sendiri rapat; nilai b besar berarti klaster tetangga jauh. Pembagi max(a, b) menjaga hasil selalu di rentang [-1, 1]:

Nilai s(i)Makna
Mendekati 1Titik jauh lebih dekat ke klasternya sendiri — penempatan kokoh
Mendekati 0a dan b nyaris sama — titik berdiri di perbatasan dua klaster
NegatifTitik lebih dekat ke klaster tetangga — kemungkinan salah penempatan

Rata-rata s(i) seluruh titik inilah yang dilaporkan silhouette_score. Skor rata-rata tinggi berarti klaster rapat sekaligus saling terpisah — dua sisi kualitas yang inertia (hanya mengukur kerapatan) tidak pernah lihat.

Silhouette versus Inertia untuk Memilih k #

Bab k-Means sudah memperlihatkan kelemahan mendasar inertia: nilainya selalu turun setiap k ditambah, sampai k = n mencapai nol — jadi ia tidak bisa memilih k sendirian, dan posisi “siku” elbow sering kabur. Silhouette tidak punya kewajiban monoton itu: klaster baru yang hanya membelah-belah gumpalan utuh akan menghasilkan titik perbatasan dengan s kecil atau negatif, sehingga rata-ratanya turun. Pada data kita, sweep k = 2..6 memperlihatkan kontras keduanya dalam satu tabel: inertia turun terus dari 815 sampai 146, sementara silhouette memuncak di k=3 (0,676) lalu merosot.

Perbandingan yang adil menuntut dua syarat. Pertama, data yang sama — skor silhouette antar dua dataset berbeda tidak bisa dibandingkan karena skala jarannya berbeda. Kedua, scaling yang sama — silhouette dihitung dari jarak, dan fitur berskala besar akan mendominasi jarak persis seperti pada KNN, k-means, dan DBSCAN. Bandingkan dua konfigurasi hanya saat keduanya memakai pipeline yang sama dari data mentah yang sama.

Asumsi yang Perlu Dijaga #

Silhouette punya bias sendiri: ia menyukai klaster bulat dan padat. Pada data dengan klaster memanjang atau berlapis — tepat wilayah kekuatan DBSCAN — skor bisa tampak buruk padahal segmentasinya benar: titik-titik ujung sabit berjarak jauh dari rata-rata klasternya sehingga a besar dan s turun, dan titik noise yang justru berhasil diidentifikasi DBSCAN akan dihukum sebagai penyimpangan. Anggap silhouette sebagai penilai yang cerdas namun berprasangka konveks: andalkan ia untuk membandingkan k-means dan Ward, periksa ulang dengan penilaian domain saat algoritme Anda berbasis kepadatan.

Praktik: Verifikasi Rumus Titik demi Titik #

python
# Evaluasi clustering dengan silhouette: rumus, sweep k, dan verifikasi manual.
# Data: 120 pelanggan toko ritel (sama dengan chapter k-Means Clustering).

import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.metrics import silhouette_samples, silhouette_score

PUSAT_SEG = [[2.0, 2.0], [7.0, 4.0], [5.0, 9.0]]
X, _ = make_blobs(
    n_samples=120, centers=PUSAT_SEG, cluster_std=1.0, random_state=42
)

# 1) Sweep k=2..6: silhouette naik-turun, inertia monoton turun.
hasil = []
for k in range(2, 7):
    km = KMeans(n_clusters=k, n_init="auto", random_state=42).fit(X)
    hasil.append((k, silhouette_score(X, km.labels_), km.inertia_))
best_k = max(hasil, key=lambda baris: baris[1])[0]

print("Sweep k=2..6 (data 120 pelanggan):")
print("  k   silhouette   inertia")
for k, s, inert in hasil:
    penanda = "   <- silhouette tertinggi" if k == best_k else ""
    print(f"  {k}      {s:.3f}    {inert:>8.3f}{penanda}")

# 2) Bedah rumus untuk satu titik: yang paling ragu-ragu.
model3 = KMeans(n_clusters=3, n_init="auto", random_state=42).fit(X)
label = model3.labels_
samples = silhouette_samples(X, label)

i = int(np.argmin(samples))
print(f"\nBedah rumus silhouette untuk satu titik (indeks {i}, klaster {label[i]}):")
jarak = np.sqrt(((X - X[i]) ** 2).sum(axis=1))
a = jarak[label == label[i]].sum() / ((label == label[i]).sum() - 1)
print(f"  jarak rata-rata ke klaster sendiri (a) : {a:.3f}")
for c in range(3):
    if c != label[i]:
        print(f"  jarak rata-rata ke klaster {c}: {jarak[label == c].mean():.3f}")
b = min(jarak[label == c].mean() for c in range(3) if c != label[i])
print(f"  klaster lain terdekat (b)              : {b:.3f}")
s_manual = (b - a) / max(a, b)
print(f"  s = (b - a) / max(a, b) = ({b:.3f} - {a:.3f}) / {b:.3f} = {s_manual:.3f}")
print(f"  silhouette_samples[{i}] dari sklearn   : {samples[i]:.3f}")

# 3) Titik kontras: yang paling kokoh di dalam klasternya.
j = int(np.argmax(samples))
jarak_j = np.sqrt(((X - X[j]) ** 2).sum(axis=1))
a_j = jarak_j[label == label[j]].sum() / ((label == label[j]).sum() - 1)
b_j = min(jarak_j[label == c].mean() for c in range(3) if c != label[j])
s_j = (b_j - a_j) / max(a_j, b_j)
print(f"\nTitik kontras (indeks {j}, klaster {label[j]}): "
      f"a = {a_j:.3f}, b = {b_j:.3f}")
print(f"  s manual = {s_j:.3f} | silhouette_samples[{j}] = {samples[j]:.3f}")

# 4) Rata-rata seluruh titik == silhouette_score.
print(f"\nRata-rata silhouette seluruh titik : {samples.mean():.3f}")
print(f"silhouette_score (API)             : {silhouette_score(X, label):.3f}")

Output yang Dihasilkan:

text
Sweep k=2..6 (data 120 pelanggan):
  k   silhouette   inertia
  2      0.534     815.524
  3      0.676     220.353   <- silhouette tertinggi
  4      0.553     191.065
  5      0.430     167.398
  6      0.430     145.861

Bedah rumus silhouette untuk satu titik (indeks 57, klaster 0):
  jarak rata-rata ke klaster sendiri (a) : 3.229
  jarak rata-rata ke klaster 1: 3.361
  jarak rata-rata ke klaster 2: 7.622
  klaster lain terdekat (b)              : 3.361
  s = (b - a) / max(a, b) = (3.361 - 3.229) / 3.361 = 0.039
  silhouette_samples[57] dari sklearn   : 0.039

Titik kontras (indeks 95, klaster 1): a = 1.263, b = 6.053
  s manual = 0.791 | silhouette_samples[95] = 0.791

Rata-rata silhouette seluruh titik : 0.676
silhouette_score (API)             : 0.676

Mari kita bedah hasil eksekusinya:

  1. Baris 15–25 (sweep dua metrik): kolom inertia turun monoton 815,524 → 145,861 tanpa pernah naik; kolom silhouette memuncak 0,676 di k=3 lalu anjlok ke 0,430. Inertia tidak bisa berhenti di k=3 atas kemampuannya sendiri — silhouette bisa, karena membelah gumpalan utuh menghasilkan titik perbatasan yang menarik rata-rata ke bawah.
  2. Baris 32–44 (hitungan tangan untuk titik 57): titik ini adalah yang paling ragu-ragu di seluruh data. Jarak rata-ratanya ke klaster sendiri a = 3,229 — nyaris sama dengan jarak ke klaster tetangga b = 3,361. Masukkan ke rumus: s = (3,361 - 3,229) / 3,361 = 0,039, hampir nol — sang pelanggan memang berdiri di perbatasan dua segmen. Nilai silhouette_samples dari sklearn: 0,039, persis hitungan tangan kita. Perhatikan baris 35: jarak ke klaster sendiri dihitung dengan pembagi jumlah anggota dikurangi satu, karena jarak titik ke dirinya sendiri (nol) tidak diikutkan.
  3. Baris 47–54 (titik kontras 95): kebalikannya, a = 1,263 jauh lebih kecil daripada b = 6,053, sehingga s = 0,791 — pelanggan yang duduk nyaman jauh di dalam klasternya. Sekali lagi hasil manual dan API identik.
  4. Baris 57–58 (rata-rata): silhouette_score ternyata bukan perhitungan tersembunyi — ia tepat rata-rata dari silhouette_samples, 0,676 di kedua baris.

Empat chapter clustering kita tuntas: konsep, tiga algoritme, dan kini alat ukurnya. Section berikutnya berganti fokus dari mengelompokkan menjadi meringkas — bagaimana memampatkan puluhan fitur yang saling berkorelasi menjadi beberapa sumbu baru tanpa kehilangan banyak informasi. Mesin pertamanya adalah Reduksi Dimensi dengan PCA.

Referensi Resmi #