Sepanjang Section Supervised Learning kita bekerja dengan data yang sudah berlabel: setiap pelanggan bertanda churn atau tidak churn, setiap email bertanda spam atau bukan. Di dunia nyata label semacam itu mahal — harus ditempel satu per satu oleh tim operasional, sering kali tidak lengkap, dan kadang memang belum ada pertanyaannya. Clustering adalah keluarga teknik unsupervised learning untuk situasi itu: tanpa label apa pun, kita membiarkan kemiripan antar data yang menentukan kelompoknya. Pada chapter ini kita membangun fondasinya — apa itu kemiripan, bagaimana mengukurnya dengan jarak, dan mengapa skala fitur menentukan siapa yang dianggap mirip.
Belajar dari Data Tanpa Label #
Perbedaan supervised dan unsupervised learning bukan sekadar ada-tidaknya kolom target, melainkan bergesernya pertanyaan. Pada supervised learning kita bertanya “data baru ini termasuk kelas yang mana?”; pada unsupervised learning kita bertanya “kelompok apa saja sesungguhnya yang hidup di dalam data ini?”.
| Aspek | Supervised learning | Unsupervised learning |
|---|---|---|
| Data yang dibutuhkan | fitur X + label y | fitur X saja |
| Pertanyaan kunci | “ini anggota kelas mana?” | “kelompok apa saja yang ada?” |
| Contoh tugas | klasifikasi churn, prediksi harga | segmentasi pelanggan, deteksi anomali |
| Keluaran | model yang memetakan X ke y | struktur: kelompok, arah variansi, aturan |
Karena tidak ada y yang menjadi wasit, cara menilai hasilnya juga berbeda. Model supervised dinilai dari akurasi memprediksi label yang kita simpan; hasil clustering dinilai dari seberapa rapat anggota satu kelompok, seberapa terpisah antar kelompok, dan — tidak kalah penting — apakah kelompoknya masuk akal bagi orang yang memahami bisnisnya. Kita akan kembali ke isu penilaian ini di chapter Evaluasi Clustering
.
Mengelompokkan karena Kemiripan #
Satu klaster adalah kumpulan titik yang lebih mirip satu sama lain daripada mirip dengan titik di luar kelompoknya. “Mirip” perlu didefinisikan secara terukur, dan cara paling umum adalah lewat jarak: dua titik berdekatan dianggap mirip, dua titik berjauhan dianggap berbeda. Sebagai bahan kerja, kita pakai enam pelanggan Toko Kopi Kita dengan dua fitur:
| Pelanggan | Belanja (ribu Rp/bulan) | Kunjungan (per bulan) |
|---|---|---|
| budi | 450 | 18 |
| siti | 60 | 3 |
| dewi | 420 | 15 |
| rani | 80 | 5 |
| agus | 390 | 16 |
| maya | 70 | 2 |
Dengan mata telanjang kita melihat dua kubu: budi–dewi–agus yang belanja besar dan sering datang, serta siti–rani–maya yang belanja kecil dan jarang datang. Clustering mengotomatikan intuisi itu dengan rumus jarak, bukan dengan mata — sehingga bisa dijalankan pada ribuan pelanggan dan puluhan fitur sekaligus.
Jarak Euclidean dan Jebakan Skala Fitur #
Jarak yang paling sering dipakai adalah Euclidean: akar dari jumlah selisih kuadrat tiap fitur.
jarak(a, b) = akar( (a_belanja - b_belanja)^2 + (a_kunjungan - b_kunjungan)^2 )Rumusnya sederhana, tetapi menyimpan satu jebakan: fitur dengan angka besar akan mendominasi hasil. Kita hitung jarak budi–siti dua kali — sekali dengan angka mentah, sekali setelah setiap fitur dinormalkan menjadi z-skor (nilai dikurangi rata-rata, dibagi simpangan baku):
import math
# Enam pelanggan Toko Kopi Kita: (belanja ribu Rp/bulan, kunjungan/bulan)
pelanggan = {
"budi": (450, 18),
"siti": (60, 3),
"dewi": (420, 15),
"rani": (80, 5),
"agus": (390, 16),
"maya": (70, 2),
}
belanja = [v[0] for v in pelanggan.values()]
frekuensi = [v[1] for v in pelanggan.values()]
def rata_rata(daftar):
return sum(daftar) / len(daftar)
def simpangan_baku(daftar):
r = rata_rata(daftar)
return math.sqrt(sum((x - r) ** 2 for x in daftar) / len(daftar))
rb, sb = rata_rata(belanja), simpangan_baku(belanja)
rf, sf = rata_rata(frekuensi), simpangan_baku(frekuensi)
print("belanja : rata-rata %6.1f, simpangan baku %6.2f" % (rb, sb))
print("frekuensi: rata-rata %6.1f, simpangan baku %6.2f" % (rf, sf))
jarak_mentah = math.dist(pelanggan["budi"], pelanggan["siti"])
print("Jarak mentah budi-siti : %.2f" % jarak_mentah)
def z_skor(nilai, r, s):
return (nilai - r) / s
budi_z = (z_skor(450, rb, sb), z_skor(18, rf, sf))
siti_z = (z_skor(60, rb, sb), z_skor(3, rf, sf))
print("Jarak z-skor budi-siti : %.2f" % math.dist(budi_z, siti_z))Output yang Dihasilkan:
belanja : rata-rata 245.0, simpangan baku 175.95
frekuensi: rata-rata 9.8, simpangan baku 6.62
Jarak mentah budi-siti : 390.29
Jarak z-skor budi-siti : 3.17Mari kita telaah baris-baris kode di atas:
pelanggansebagai dictionary menyimpan pasangan fitur tiap orang;belanjadanfrekuensimengumpulkan tiap kolom menjadi list agar statistiknya bisa dihitung.rata_ratadansimpangan_bakudihitung manual dengansum()— persis definisi statistiknya, tanpa library. Simpangan baku adalah akar dari rata-rata kuadrat selisih terhadap rata-rata.math.dist(p, q)menghitung jarak Euclidean dua titik sekaligus — setara dengan rumus pada blok text di atas.z_skor(nilai, r, s)memetakan nilai mentah ke skala baru: nol berarti tepat di rata-rata, satu berarti satu simpangan baku di atasnya.
Angka 390,29 hampir seluruhnya berasal dari selisih belanja (450 − 60 = 390); selisih kunjungan (18 − 3 = 15) nyaris tidak terbaca. Setelah z-skor, kedua fitur menyumbang secara seimbang dan jarak menjadi 3,17 — budi memang berbeda dari siti di kedua dimensi sekaligus. Kesimpulannya satu arah: lakukan penyekalaan fitur sebelum menghitung jarak, kecuali satuan semua fitur memang sudah setara.
Clustering Buatan Tangan: Jangkar Terdekat #
Sebelum memakai algoritma sungguhan, kita coba versi paling sederhana dengan tangan sendiri. Pilih dua titik jangkar — dewi mewakili kelompok belanja besar, siti mewakili kelompok belanja kecil — lalu setiap pelanggan bergabung ke jangkar yang terdekat menurut jarak z-skor:
import math
# Enam pelanggan Toko Kopi Kita: (belanja ribu Rp/bulan, kunjungan/bulan)
pelanggan = {
"budi": (450, 18),
"siti": (60, 3),
"dewi": (420, 15),
"rani": (80, 5),
"agus": (390, 16),
"maya": (70, 2),
}
belanja = [v[0] for v in pelanggan.values()]
frekuensi = [v[1] for v in pelanggan.values()]
def rata_rata(daftar):
return sum(daftar) / len(daftar)
def simpangan_baku(daftar):
r = rata_rata(daftar)
return math.sqrt(sum((x - r) ** 2 for x in daftar) / len(daftar))
rb, sb = rata_rata(belanja), simpangan_baku(belanja)
rf, sf = rata_rata(frekuensi), simpangan_baku(frekuensi)
def z_skor(nilai, r, s):
return (nilai - r) / s
def jarak(p, q):
return math.dist(p, q)
# Dua titik jangkar: dewi mewakili kelompok belanja besar, siti belanja kecil
jangkar = {
"kelompok dewi": (z_skor(420, rb, sb), z_skor(15, rf, sf)),
"kelompok siti": (z_skor(60, rb, sb), z_skor(3, rf, sf)),
}
print("Penugasan tiap pelanggan ke jangkar terdekat:")
for nama, (b, f) in pelanggan.items():
titik = (z_skor(b, rb, sb), z_skor(f, rf, sf))
pilihan = min(jangkar, key=lambda k: jarak(titik, jangkar[k]))
print(" %-5s -> %s" % (nama, pilihan))Output yang Dihasilkan:
Penugasan tiap pelanggan ke jangkar terdekat:
budi -> kelompok dewi
siti -> kelompok siti
dewi -> kelompok dewi
rani -> kelompok siti
agus -> kelompok dewi
maya -> kelompok sitiBedah kodenya singkat saja karena idenya yang penting:
jangkarmenyimpan posisi z-skor dua titik pilihan — berperan seperti pusat kelompok sementara.min(jangkar, key=...)memilih nama jangkar dengan jarak terkecil; parameterkeymenerima fungsi sehinggaminmembandingkan hasil fungsi, bukan teksnya.- Hasilnya persis intuisi awal: budi, dewi, agus di satu kubu; siti, rani, maya di kubu lain.
Tiga kelemahan versi tangan ini sekaligus menjadi agenda algoritma sungguhan: jangkarnya kita pilih sendiri, jumlahnya kita tetapkan sendiri, dan posisinya tidak pernah diperbarui. K-Means memperbaiki ketiganya — posisi jangkar (centroid) digeser ke rata-rata anggotanya lalu penugasan diulang, sampai stabil. Itulah materi chapter berikutnya.
Aplikasi dan Tantangan #
Clustering dipakai di banyak bidang, dan bentuk keluarannya berbeda-beda:
| Aplikasi | Pertanyaan yang dijawab | Keluaran |
|---|---|---|
| Segmentasi pelanggan | segmen pasar apa saja yang ada? | kelompok pelanggan + profilnya |
| Deteksi anomali | titik mana yang tidak menempel di kelompok mana pun? | daftar transaksi atau perangkat mencurigakan |
| Pengelompokan dokumen | kumpulan berita terbagi ke topik apa? | kelompok dokumen serumpun |
| Sistem rekomendasi | pengguna mana yang perilakunya mirip? | komunitas pengguna sebagai dasar rekomendasi |
Tantangan terbesarnya bukan teknis, melainkan soal tolak ukur: karena label tidak ada, “benar” tidak terdefinisi secara tunggal. Memotong pelanggan menjadi tiga kelompok bisa tepat untuk keperluan promosi, tetapi terlalu kasar untuk keperluan desain menu — keduanya sah pada data yang sama. Karena itu praktik yang sehat selalu menggabungkan metrik internal (seberapa rapat dan terpisah kelompoknya) dengan pengetahuan domain (apakah kelompoknya bisa dimaknai dan ditindaklanjuti).
Rangkuman #
- Clustering adalah unsupervised learning: mengelompokkan data karena kemiripan (jarak), tanpa label dan tanpa pertanyaan kelas.
- Jarak Euclidean adalah ukuran kemiripan paling umum; fitur berskala besar mendominasi jarak, sehingga standarisasi z-skor dikerjakan lebih dulu.
- Penugasan “jangkar terdekat” adalah embrio clustering: K-Means mengotomatiskannya dengan menggeser jangkar ke rata-rata anggota dan mengulang sampai stabil.
- Karena label tidak ada, “benar” tidak terdefinisi tunggal — hasil selalu dinilai dengan metrik internal plus pengetahuan domain, sesuai tujuan analisis.
Dengan intuisi jarak dan skala di tangan, kita siap bertemu algoritma clustering yang paling banyak dipakai: K-Means, yang mengubah jangkar tadi menjadi centroid yang belajar posisinya sendiri dari data. Lanjut ke chapter K-Means Clustering .