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?”.

AspekSupervised learningUnsupervised learning
Data yang dibutuhkanfitur X + label yfitur X saja
Pertanyaan kunci“ini anggota kelas mana?”“kelompok apa saja yang ada?”
Contoh tugasklasifikasi churn, prediksi hargasegmentasi pelanggan, deteksi anomali
Keluaranmodel yang memetakan X ke ystruktur: 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:

PelangganBelanja (ribu Rp/bulan)Kunjungan (per bulan)
budi45018
siti603
dewi42015
rani805
agus39016
maya702

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.

text
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):

python
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:

text
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.17

Mari kita telaah baris-baris kode di atas:

  1. pelanggan sebagai dictionary menyimpan pasangan fitur tiap orang; belanja dan frekuensi mengumpulkan tiap kolom menjadi list agar statistiknya bisa dihitung.
  2. rata_rata dan simpangan_baku dihitung manual dengan sum() — persis definisi statistiknya, tanpa library. Simpangan baku adalah akar dari rata-rata kuadrat selisih terhadap rata-rata.
  3. math.dist(p, q) menghitung jarak Euclidean dua titik sekaligus — setara dengan rumus pada blok text di atas.
  4. 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:

python
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:

text
Penugasan tiap pelanggan ke jangkar terdekat:
  budi  -> kelompok dewi
  siti  -> kelompok siti
  dewi  -> kelompok dewi
  rani  -> kelompok siti
  agus  -> kelompok dewi
  maya  -> kelompok siti

Bedah kodenya singkat saja karena idenya yang penting:

  1. jangkar menyimpan posisi z-skor dua titik pilihan — berperan seperti pusat kelompok sementara.
  2. min(jangkar, key=...) memilih nama jangkar dengan jarak terkecil; parameter key menerima fungsi sehingga min membandingkan hasil fungsi, bukan teksnya.
  3. 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:

AplikasiPertanyaan yang dijawabKeluaran
Segmentasi pelanggansegmen pasar apa saja yang ada?kelompok pelanggan + profilnya
Deteksi anomalititik mana yang tidak menempel di kelompok mana pun?daftar transaksi atau perangkat mencurigakan
Pengelompokan dokumenkumpulan berita terbagi ke topik apa?kelompok dokumen serumpun
Sistem rekomendasipengguna 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 .

Referensi #

  1. scikit-learn — Clustering
  2. scikit-learn — Preprocessing Data