Dua algoritme sebelumnya sama-sama jatuh cinta pada gumpalan bulat: k-means menarik garis lurus ke pusat, Ward menghukum jarak kuadrat terhadap rata-rata. Data nyata sering lebih bandel — kelompok melengkung seperti sabit, saling terkunci, atau datang dengan titik pencilan yang tidak sungguh-sungguh milik kelompok mana pun. DBSCAN (Density-Based Spatial Clustering of Applications with Noise) membuang konsep pusat sepenuhnya dan berpikir lewat kepadatan: klaster adalah wilayah yang ramai titik, dipisahkan oleh wilayah yang sepi. Kisah kita kali ini: data perilaku pelanggan yang membentuk dua pola lengkung saling terkunci, ditambah empat pelanggan dengan pola yang jauh dari kedua kelompok.

Tiga Jenis Titik: Inti, Batas, dan Noise #

DBSCAN mengklasifikasikan setiap titik ke salah satu dari tiga peran, ditentukan oleh dua hyperparameter: eps (radius lingkungan) dan min_samples (ambang jumlah tetangga):

  • Titik inti (core point): punya minimal min_samples tetangga dalam radius eps. Ini jantung klaster — wilayah yang benar-benar ramai.
  • Titik batas (border point): tetangganya kurang dari min_samples, tetapi berada dalam radius eps dari sebuah titik inti. Titik batas ikut klaster milik inti tersebut tanpa menjadi penopang.
  • Noise: keduanya gagal — sepi dan jauh dari inti mana pun. DBSCAN memberinya label khusus -1.

Klaster lalu terbentuk dengan aturan sederhana: dua titik inti yang berjarak ≤ eps saling menempel, dan rantai inti yang saling sambung membentuk satu klaster bersama seluruh titik batas yang menempel padanya. Bentuk apa pun yang dilalui rantai itu — sabit, cincin, huruf S — sah-sah saja, karena yang dihitung adalah kepadatan, bukan jarak ke pusat.

Kemampuan mengembalikan label -1 adalah fitur yang membedakan DBSCAN dari hampir semua tetangganya. k-means dan agglomerative memaksa setiap titik masuk salah satu klaster; DBSCAN boleh berkata jujur “titik ini tidak masuk kelompok mana pun”. Untuk deteksi transaksi tak wajar atau sensor rusak, label noise itu sendiri sering menjadi hasil utama analisis — dan menjadi pintu masuk alami menuju Section Anomaly Detection.

Mengatur eps dan min_samples #

Kedua hyperparameter ini menentukan nasib hasil, dan keduanya bekerja sebagai pasangan. eps terlalu kecil memecah data menjadi banyak klaster kerdil plus banjir titik noise; eps terlalu besar meleburkan kelompok yang seharusnya terpisah. Alat bantu standar untuk memilihnya adalah k-distance plot: hitung jarak tiap titik ke tetangga ke-k (biasanya k = min_samples), urutkan dari kecil ke besar, lalu cari “lutut” tempat kurva menanjak — nilai di titik itulah kandidat eps.

Untuk min_samples, pegangan umum: minimal dimensi + 1, dinaikkan untuk data bising atau berdimensi tinggi. Nilai 4–6 lazim untuk data dua dimensi seperti contoh kita; semakin besar min_samples, semakin ketat definisi “ramai”, sehingga klaster tipis mulai dianggap noise.

Kekuatan dan Batas #

KekuatanBatasan
Klaster berbentuk bebas (sabit, cincin, S)Satu eps untuk semua klaster — kepadatan berbeda jauh menyulitkan
Tidak perlu menentukan jumlah klasterSensitif terhadap pilihan eps/min_samples
Titik noise terdeteksi eksplisitDimensi tinggi membuat jarak menyempit seragam, kepadatan sulit dibedakan
Deterministik (tanpa acak, tanpa restart)Perlu scaling fitur seperti algoritme berbasis jarak lain

Untuk data dengan kepadatan campuran, varian lanjutan HDBSCAN membangun hierarki kepadatan dan memilih potongan terbaik secara otomatis — pola pikir yang menggabungkan DBSCAN dengan hierarchical clustering bab sebelumnya.

Praktik: KMeans Gagal, Kepadatan Berhasil #

python
# DBSCAN: klaster berbasis kepadatan yang mengenai titik noise.
# Kisah: pola belanja dua kelompok terkunci + 4 pelanggan pencilan.

import numpy as np
from sklearn.cluster import DBSCAN, KMeans
from sklearn.datasets import make_moons

# Dua kelompok berbentuk bulan sabit yang saling terkunci (120 titik),
# plus 4 pelanggan dengan pola jauh dari kedua kelompok.
X_bulan, y_bulan = make_moons(n_samples=120, noise=0.06, random_state=42)
pencilan = np.array([
    [0.2, 1.5],
    [2.3, -0.7],
    [-1.4, 0.2],
    [1.1, -1.3],
])
X = np.vstack([X_bulan, pencilan])

# 1) KMeans dengan k=2: dua pusat memotong kedua bulanan.
km = KMeans(n_clusters=2, n_init="auto", random_state=42).fit(X)
cocok_km = max(
    (km.labels_[:120] == y_bulan).mean(),
    (km.labels_[:120] == 1 - y_bulan).mean(),
)
print("KMeans (k=2) pada 124 titik:")
print(f"  kesepakatan dengan kelompok asli : {cocok_km:.3f}")
print(f"  pencilan dipaksa masuk klaster   : {km.labels_[120:].tolist()}")

# 2) DBSCAN: kepadatan, bukan pusat.
db = DBSCAN(eps=0.22, min_samples=5).fit(X)
label_db = db.labels_
print("\nDBSCAN (eps=0.22, min_samples=5):")
print(f"  jumlah klaster        : {len(set(label_db.tolist()) - {-1})}")
print(f"  jumlah titik noise    : {int((label_db == -1).sum())}")
print(f"  jumlah titik inti     : {len(db.components_)}")
cocok_db = max(
    (label_db[:120] == y_bulan).mean(),
    (label_db[:120] == 1 - y_bulan).mean(),
)
print(f"  kesepakatan 120 titik : {cocok_db:.3f}")
print(f"  label 4 pencilan      : {label_db[120:].tolist()}")

# 3) Sensitivitas eps: terlalu kecil -> terpotong-potong,
#    terlalu besar -> dua bulanan melebur jadi satu.
print("\nSensitivitas eps (min_samples=5):")
print("  eps    klaster   noise")
for eps in [0.15, 0.22, 0.50]:
    label_e = DBSCAN(eps=eps, min_samples=5).fit_predict(X)
    n_k = len(set(label_e.tolist()) - {-1})
    n_n = int((label_e == -1).sum())
    print(f"  {eps:.2f}      {n_k:>2}      {n_n:>2}")

Output yang Dihasilkan:

text
KMeans (k=2) pada 124 titik:
  kesepakatan dengan kelompok asli : 0.758
  pencilan dipaksa masuk klaster   : [1, 0, 1, 0]

DBSCAN (eps=0.22, min_samples=5):
  jumlah klaster        : 2
  jumlah titik noise    : 4
  jumlah titik inti     : 117
  kesepakatan 120 titik : 1.000
  label 4 pencilan      : [-1, -1, -1, -1]

Sensitivitas eps (min_samples=5):
  eps    klaster   noise
  0.15      10      27
  0.22       2       4
  0.50       1       2

Mari kita bedah hasil eksekusinya — perbandingan inilah inti chapter ini:

  1. Baris 19–27 (k-means pada data lengkung): dengan k=2, kesepakatan k-means terhadap kelompok asli hanya 0,758 — hampir seperempat titik salah tempat. Penyebabnya struktural: satu pusat menampung lengan kiri kedua bulanan, pusat lain menampung lengan kanannya, sehingga tiap klaster berisi campuran dua kelompok. Dan karena k-means tidak mengenal konsep “di luar klaster”, keempat pencilan dipaksa masuk klaster [1, 0, 1, 0] — tanpa peringatan apa pun.
  2. Baris 29–41 (DBSCAN pada data yang sama): hasilnya kontras total. Dua klaster ditemukan, kesepakatan 1,000 pada seluruh 120 titik bulanan, dan keempat pencilan semuanya berlabel -1. Atribut components_ memperlihatkan 117 titik inti — hanya 3 dari 120 titik bulanan yang “hanya” menjadi titik batas; tidak ada satu pun yang jatuh ke noise.
  3. Baris 43–51 (sensitivitas eps): pada eps=0.15 definisi “ramai” terlalu ketat — data pecah menjadi 10 klaster kerdil dengan 27 titik noise. Pada eps=0.50 kebalikannya: kedua bulanan yang terkunci menyatu menjadi satu klaster raksasa, dan dua pencilan ikut terserap. Nilai eps=0.22 berada di lembah yang tepat di antara dua kegagalan itu.
  4. Kesepakatan dihitung dua arah (baris 21–24 dan 36–39): fungsi max() membandingkan label asli dengan kedua kemungkinan penomoran (0↔1) — teknik menyamakan penomoran yang diperkenalkan pada chapter Hierarchical Clustering. Tanpa ini, partisi sempurna bisa tampak sepenuhnya salah hanya karena angkanya terbalik.

Tiga algoritme clustering kini ada di tangan Anda, dan semuanya menghasilkan klaster dengan cara — serta asumsi — yang berbeda. Pertanyaan berikutnya tidak bisa dielakkan: tanpa label kebenaran, bagaimana kita tahu hasil clustering mana yang lebih baik? Chapter Evaluasi Clustering dengan Silhouette membangun ukurannya dari pertanyaan sederhana: apakah tiap titik benar-benar nyaman berada di klasternya sendiri?

Referensi Resmi #