K-Means dan hierarchical clustering sama-sama bekerja dengan jarak antar titik, dan karenanya sama-sama berorientasi gumpalan: klaster dibayangkan bulat, dan setiap titik wajib masuk kelompok mana pun. Data nyata sering tidak sebaik itu — pelanggan bisa membentuk jalur memanjang, dan di antara mereka selalu ada beberapa titik pencilan yang tidak melambangkan kelompok apa pun. DBSCAN (Density-Based Spatial Clustering of Applications with Noise) mengganti logikanya dari jarak ke kepadatan: klaster adalah wilayah yang padat titik, dipisahkan oleh wilayah lengang — bentuknya bebas, dan titik yang terlalu sepi otomatis ditandai sebagai noise. Pada chapter ini kita mengadu DBSCAN melawan K-Means pada data berbentuk dua bulan sabit, lalu melihat mengapa label noise-nya berharga untuk deteksi anomali.
Tiga Peran Titik: Inti, Pinggir, Noise #
DBSCAN bekerja dengan dua parameter: eps (radius lingkungan) dan min_samples (jumlah tetangga minimal di dalam radius itu). Dari keduanya, setiap titik memperoleh salah satu dari tiga peran:
| Peran | Syarat | Perlakuan DBSCAN |
|---|---|---|
| Titik inti (core) | memiliki minimal min_samples tetangga dalam radius eps | menjadi fondasi klaster |
| Titik pinggir (border) | tetangganya kurang dari min_samples, tetapi berada dalam radius sebuah titik inti | ikut klaster titik inti itu |
| Noise | bukan inti dan tidak berdekatan dengan titik inti mana pun | diberi label -1, tidak masuk klaster apa pun |
Klaster tumbuh dari titik inti seperti simpul menyebar ke tetangga yang juga inti — bentuk akhirnya mengikuti kepadatan wilayah, bukan asumsi bulat. Konsekuensi pentingnya ada dua: kita tidak menetapkan jumlah klaster di muka (jumlahnya muncul sendiri dari data), dan klaster boleh melengkung, memanjang, atau melingkar.
Duel di Data Bulan Sabit #
Kita pakai dataset mainan make_moons dari scikit-learn: 60 titik membentuk dua bulan sabit yang saling menjalin, ditambah tiga titik pencilan yang sengaja kita letakkan jauh di penjuru. Data dibagi dua bulan sejak lahir — bulan 0 dan bulan 1 — sehingga kita bisa mengaudit isi tiap klaster hasil algoritma:
import numpy as np
from sklearn.cluster import DBSCAN, KMeans
from sklearn.datasets import make_moons
# Data bulan sabit: 60 titik membentuk dua bulan, seed 42 agar reproducible
bulan_X, bulan_asli = make_moons(n_samples=60, noise=0.06, random_state=42)
# Tiga transaksi tak wajar sebagai pencilan di luar kedua bulan
pencilan = np.array([[3.0, 2.5], [-3.0, -2.2], [2.8, -2.6]])
X = np.vstack([bulan_X, pencilan])
bulan_asli = np.concatenate([bulan_asli, [-1, -1, -1]]) # pencilan: tanpa bulan
# K-Means dipaksa membelah data menjadi dua bagian yang seimbang
label_km = KMeans(n_clusters=2, random_state=42, n_init="auto").fit_predict(X)
print("K-Means (k=2):")
for c in [0, 1]:
anggota = label_km == c
asal = np.bincount(bulan_asli[anggota & (bulan_asli >= 0)], minlength=2)
print(" klaster %d: %2d titik (dari bulan 0: %2d, dari bulan 1: %2d)"
% (c, anggota.sum(), asal[0], asal[1]))
# DBSCAN sensitif pada eps: coba tiga nilai
print()
print("DBSCAN, pengaruh eps (min_samples=5):")
for eps in [0.25, 0.35, 0.50]:
label = DBSCAN(eps=eps, min_samples=5).fit_predict(X)
n_klaster = len(set(label)) - (1 if -1 in label else 0)
print(" eps=%.2f -> %d klaster, %d titik noise" % (eps, n_klaster, (label == -1).sum()))
# DBSCAN dengan eps yang pas
label_db = DBSCAN(eps=0.35, min_samples=5).fit_predict(X)
print()
print("DBSCAN (eps=0.35, min_samples=5):")
for c in sorted(set(label_db)):
anggota = label_db == c
if c == -1:
print(" noise : %d titik (ketiga pencilan)" % anggota.sum())
continue
asal = np.bincount(bulan_asli[anggota & (bulan_asli >= 0)], minlength=2)
print(" klaster %d: %2d titik (dari bulan 0: %2d, dari bulan 1: %2d)"
% (c, anggota.sum(), asal[0], asal[1]))Output yang Dihasilkan:
K-Means (k=2):
klaster 0: 31 titik (dari bulan 0: 8, dari bulan 1: 21)
klaster 1: 32 titik (dari bulan 0: 22, dari bulan 1: 9)
DBSCAN, pengaruh eps (min_samples=5):
eps=0.25 -> 6 klaster, 16 titik noise
eps=0.35 -> 2 klaster, 3 titik noise
eps=0.50 -> 1 klaster, 3 titik noise
DBSCAN (eps=0.35, min_samples=5):
noise : 3 titik (ketiga pencilan)
klaster 0: 30 titik (dari bulan 0: 30, dari bulan 1: 0)
klaster 1: 30 titik (dari bulan 0: 0, dari bulan 1: 30)Mari kita telaah kode dan hasilnya:
make_moons(n_samples=60, noise=0.06, random_state=42)menghasilkan koordinat 60 titik plus label bulan asalnya;noisemengatur tebal kabut acak di sekitar lengkung, dan seed 42 menjamin bentuk yang sama setiap dijalankan.np.vstack+np.concatenatemenambahkan tiga pencilan ke data dan memberi mereka kode bulan-1supaya tidak ikut dihitung saat mengaudit komposisi klaster.- Audit K-Means: klaster 0 berisi 8 titik bulan 0 dan 21 titik bulan 1 — kedua klaster K-Means adalah campuran dua bulan. K-Means membelah data menjadi dua bagian kiri-kanan yang seimbang karena hanya tahu jarak ke centroid, bukan bentuk lengkung.
- Uji sensitivitas
eps: 0,25 terlalu sempit — banyak titik kehilangan tetangga sehingga pecah menjadi 6 klaster dan 16 noise; 0,50 terlalu lebar — celah antar bulan ikut tertutup dan semua menyatu; 0,35 pas. - Audit DBSCAN final: klaster 0 murni 30 titik bulan 0, klaster 1 murni 30 titik bulan 1, dan ketiga pencilan ditandai noise — bentuk lengkung diikuti, pencilan tidak dipaksa masuk kelompok mana pun.
Wujud Keduanya di Atas Kanvas #
Angka-angka di atas lebih mudah dipercaya bila dilihat. Kita gambar keduanya berdampingan:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import DBSCAN, KMeans
from sklearn.datasets import make_moons
bulan_X, _ = make_moons(n_samples=60, noise=0.06, random_state=42)
pencilan = np.array([[3.0, 2.5], [-3.0, -2.2], [2.8, -2.6]])
X = np.vstack([bulan_X, pencilan])
label_km = KMeans(n_clusters=2, random_state=42, n_init="auto").fit_predict(X)
label_db = DBSCAN(eps=0.35, min_samples=5).fit_predict(X)
fig, sumbu = plt.subplots(1, 2, figsize=(10, 4.5))
for c, warna in [(0, "#4a7ba6"), (1, "#c06040")]:
m = label_km == c
sumbu[0].scatter(X[m, 0], X[m, 1], s=25, color=warna, label="klaster %d" % c)
sumbu[0].set_title("K-Means membelah kedua bulan")
sumbu[0].legend()
for c, warna in [(0, "#4a7ba6"), (1, "#c06040")]:
m = label_db == c
sumbu[1].scatter(X[m, 0], X[m, 1], s=25, color=warna, label="klaster %d" % c)
m = label_db == -1
sumbu[1].scatter(X[m, 0], X[m, 1], s=60, color="gray", marker="x",
label="noise")
sumbu[1].set_title("DBSCAN mengikuti lengkung + menandai noise")
sumbu[1].legend()
plt.tight_layout()
plt.show()Bayangkan hasilnya seperti dua foto objek yang sama. Panel kiri (K-Means) memperlihatkan garis belah vertikal di tengah: warna biru dan merah masing-masing mengisi separuh kiri dan kanan, sehingga setiap warna memotong kedua bulan sabit sekaligus. Panel kanan (DBSCAN) mewarnai satu bulan penuh biru dan satu bulan penuh merah — warna mengikuti lengkung, bukan garis lurus — dan tiga tanda silang abu-abu berdiri sendiri di penjuru kanvas sebagai noise.
Hubungan dengan Deteksi Anomali #
Label -1 adalah fitur, bukan galat. Dalam data transaksi, titik yang tidak menempel pada kelompok padat mana pun justru sering menjadi temuan paling berharga: transaksi dengan nominal aneh, perangkat dengan pola login tak lazim, sensor yang membaca di luar wajar. DBSCAN memberi kita dua hasil dalam satu proses — klaster untuk data normal dan daftar kandidat anomali untuk data yang menyimpang — tanpa perlu ambang yang kita tebak sendiri.
Perbandingannya dengan pendekatan statistik pada chapter Menangani Outlier berguna untuk dipahami: aturan kuantil atau z-skor memeriksa satu fitur pada satu waktu terhadap sebaran global, sedangkan DBSCAN memeriksa kepadatan lingkungan lokal di ruang banyak fitur. Pencilan yang masih berada dalam rentang wajar tiap fitur — tetapi berada di wilayah yang lengang di gabungan fitur — hanya tertangkap oleh yang kedua.
Mengatur eps dan min_samples #
Kedua parameter DBSCAN menuntut rasa data, dan tiga pegangan ini membantu:
- Standarisasi dulu.
epsberarti “dekat” dalam satuan fitur; bila skala fitur berbeda jauh, radius yang sama menutup satu fitur terlalu longgar dan fitur lain terlalu sempit. min_samplesminimal sebesar dimensi data plus satu, lalu naikkan untuk data ber-noise tinggi; nilai 5 untuk data 2 dimensi seperti contoh kita adalah titik awal yang lazim.epsdicari dari data, bukan ditebak. Trik umumnya: untuk setiap titik, ukur jarak ke tetangga ke-min_samples, urutkan dari terbesar, lalu cari “lutut” lengkungan — nilaiepstepat di tikungan itu.
Rangkuman #
- DBSCAN mendefinisikan klaster lewat kepadatan (
eps,min_samples), bukan jarak ke centroid: bentuk klaster bebas, jumlah klaster tidak ditetapkan di muka. - Setiap titik berperan sebagai inti, pinggir, atau noise; noise mendapat label
-1dan tidak dipaksakan masuk klaster mana pun. - Pada data dua bulan sabit, K-Means membelah kedua bulan menjadi dua bagian kiri-kanan, sementara DBSCAN memisahkan tepat per bulan dan menandai tiga pencilan sebagai noise.
- Label noise adalah alat deteksi anomali bawaan: transaksi atau perangkat yang tidak menempel kelompok padat mana pun menjadi kandidat penyelidikan.
epsterlalu kecil memecah data, terlalu besar menyatukan semuanya; standarisasi fitur dan pemilihanepsdari data adalah kunci.
Kita kini memiliki tiga algoritma clustering dengan karakter berbeda — tetapi satu pertanyaan belum terjawab: dari semua pembagian yang mungkin, mana yang paling baik? Karena tidak ada label untuk menjadi wasit, kita perlu metrik yang menilai dari dalam data itu sendiri. Lanjut ke chapter Evaluasi Clustering .