Dua proyek sebelumnya sama-sama berpijak pada data Toko Kopi Kita, tetapi kali ini sasarannya berpindah dari transaksi ke manusia yang bertransaksi. Sejak akhir 2025 toko Bu Rina menjalankan program kartu member sederhana: pelanggan mendaftarkan nama, lalu cukup menyebutkannya saat membayar agar aplikasi kasir mencatat kunjungan dan belanjanya. Pada 17 Januari 2026 Bu Rina mengekspor ringkasan 30 hari terakhir (18 Desember 2025 s.d. 16 Januari 2026) dan mendapati 32 baris data member yang belum pernah ia olah. Ia datang membawa tiga ide program penjualan — dan bagian ini kita pastikan dulu masalah apa yang sesungguhnya mau dipecahkan, karena clustering yang dijalankan tanpa masalah yang jelas hanya menghasilkan kelompok yang rapi tetapi tidak bisa ditindaklanjuti.
Tiga Ide Program yang Menunggu Jawaban #
Ketiga ide Bu Rina lahir dari pengamatan sehari-hari di balik kasir, dan ketiganya mengandung pertanyaan yang belum terjawab:
| Ide program | Pertanyaan yang menunggu jawaban |
|---|---|
| Kartu prabayar (deposit kunjungan) | Pelanggan seperti apa yang nyaman menyimpan uang di toko? |
| Promo yang tepat sasaran | Promo apa yang relevan untuk perilaku belanja yang mana? |
| Penawaran akhir pekan | Kelompok mana yang paling perlu didorong datang pada Sabtu-Minggu? |
Jawaban yang paling mudah untuk ketiganya adalah “semua pelanggan” — tetapi justru di situlah masalahnya. Promo yang ditujukan kepada semua orang biasanya terasa relevan bagi tidak seorang pun: pelanggan yang datang setiap hari butuh hal yang berbeda dengan pelanggan yang datang empat kali sebulan. Sebelum memutuskan program apa pun, kita perlu tahu apakah pelanggan memang berbeda-beda secara teratur, dan kalau ya, bedanya seperti apa.
Jebakan Pelanggan Rata-Rata #
Langkah pertama yang masuk akal adalah merangkum seluruh pelanggan dalam beberapa angka rata-rata. Kita lakukan hitungan cepat dengan menyisihkan dulu salinan persis dan sel yang tidak terbaca sebagai angka — dua kejanggalan yang sudah terlihat sekilas pada ekspor dan akan kita bereskan resmi pada Bagian 3:
import pandas as pd
mentah = pd.read_csv("../data/pelanggan.csv")
# Hitung cepat: singkirkan dulu salinan persis dan sel kunjungan yang tidak
# terbaca sebagai angka. Pembersihan resmi baru dikerjakan pada Bagian 3.
siap = mentah.drop_duplicates()
siap = siap.assign(
kunjungan_30hari=pd.to_numeric(siap["kunjungan_30hari"], errors="coerce")
).dropna(subset=["kunjungan_30hari"])
rata_kunjungan = siap["kunjungan_30hari"].mean()
rata_belanja = siap["belanja_ribu"].mean()
print(f"Pelanggan yang dirata-rata : {len(siap)}")
print(f"Rata-rata kunjungan : {rata_kunjungan:.1f} kali per 30 hari")
print(f"Rata-rata total belanja : Rp {rata_belanja * 1000:,.0f}".replace(",", "."))Output yang Dihasilkan:
Pelanggan yang dirata-rata : 30
Rata-rata kunjungan : 12.6 kali per 30 hari
Rata-rata total belanja : Rp 464.667Mari kita bedah hasilnya:
drop_duplicates(): salinan persis dibuang lebih dulu supaya pelanggan yang kebetulan tercatat dua kali tidak dihitung dua kali dalam rata-rata.pd.to_numeric(..., errors="coerce"): sel yang tidak terbaca sebagai angka (ada satu, seperti akan kita lihat) berubah menjadiNaN, laludropnamenyisihkan barisnya dari hitungan cepat ini.- Hasilnya: 30 pelanggan, rata-rata 12,6 kunjungan dan total belanja Rp 464.667 — nyaris setengah juta rupiah per pelanggan per bulan. Angka yang sehat di permukaan.
Sekarang kita uji: kalau rata-rata itu benar-benar mewakili pelanggan, seharusnya banyak pelanggan yang menyerupainya. Kita hitung berapa pelanggan yang berbeda paling jauh 10% dari rata-rata pada kedua metrik sekaligus, lalu sandingkan tiga pelanggan dengan total belanja nyaris setara rata-rata:
import pandas as pd
mentah = pd.read_csv("../data/pelanggan.csv")
siap = mentah.drop_duplicates()
siap = siap.assign(
kunjungan_30hari=pd.to_numeric(siap["kunjungan_30hari"], errors="coerce")
).dropna(subset=["kunjungan_30hari"]).reset_index(drop=True)
siap["kunjungan_30hari"] = siap["kunjungan_30hari"].astype(int)
# "Menyerupai rata-rata" = selisih paling jauh 10% pada kedua metrik.
kunjungan = siap["kunjungan_30hari"]
belanja = siap["belanja_ribu"]
mirip = siap[
kunjungan.between(kunjungan.mean() * 0.9, kunjungan.mean() * 1.1)
& belanja.between(belanja.mean() * 0.9, belanja.mean() * 1.1)
]
print(f"Pelanggan mirip rata-rata (selisih <= 10%): {len(mirip)} dari {len(siap)}")
print(mirip[["pelanggan", "kunjungan_30hari", "belanja_ribu"]].to_string(index=False))
pilihan = siap[
siap["pelanggan"].isin(["Siti Rahmawati", "Bambang Sutrisno", "Nurul Aini"])
].copy()
pilihan["belanja_per_kunjungan"] = (
pilihan["belanja_ribu"] / pilihan["kunjungan_30hari"]
)
print("\nTiga pelanggan dengan total belanja nyaris setara rata-rata:")
print(pilihan[["pelanggan", "kunjungan_30hari", "belanja_ribu",
"belanja_per_kunjungan"]].to_string(index=False))Output yang Dihasilkan:
Pelanggan mirip rata-rata (selisih <= 10%): 3 dari 30
pelanggan kunjungan_30hari belanja_ribu
Nurul Aini 12 420
Tina Melati 13 494
Putri Anggraini 12 432
Tiga pelanggan dengan total belanja nyaris setara rata-rata:
pelanggan kunjungan_30hari belanja_ribu belanja_per_kunjungan
Siti Rahmawati 25 500 20.0
Bambang Sutrisno 4 480 120.0
Nurul Aini 12 420 35.0Dua hal perlu digarisbawahi dari keluaran tersebut:
- Hanya 3 dari 30 pelanggan yang menyerupai rata-rata pada kedua metrik. Sisanya berada di luar zona nyaman itu — artinya “pelanggan rata-rata” adalah sosok yang hampir tidak menggambarkan siapa pun.
- Tiga pelanggan di tabel kedua menyimpan cerita yang bertolak belakang. Siti Rahmawati datang 25 kali dengan belanja kecil tiap kunjungan (20 ribu), Bambang Sutrisno datang 4 kali dengan belanja besar tiap kunjungan (120 ribu), dan Nurul Aini berada di tengah-tengah. Total bulanan ketiganya nyaris sama — bagi kasir ketiganya tampak sebagai “pelanggan Rp 400-500 ribu”, padahal cara mereka memakai toko jauh berbeda.
Clustering: Membiarkan Data Menemukan Kelompoknya #
Karena tidak ada label “segmen” yang ditentukan siapa pun di awal — tidak seperti proyek prediksi yang punya target — kita berada di wilayah unsupervised learning. Alih-alih memprediksi label yang sudah ada, kita membiarkan algoritma menemukan kelompok yang memang muncul dari data. Algoritme yang akan kita pakai adalah K-Means: ia memetakan tiap pelanggan sebagai titik pada bidang fitur (seberapa sering datang, dan seberapa besar belanja tiap kunjungan), lalu mencari sejumlah pusat kelompok yang paling mewakili sebaran titik-titik itu. Mekanisme lengkapnya sudah kita bahas pada chapter K-Means Clustering ; di proyek ini kita memakainya sebagai alat, bukan membedah ulang teorinya.
Keberhasilan proyek ini punya definisi yang konkret: pada akhir Bagian 8, harus ada segmen-segmen bernama yang masing-masing bisa dipasangkan dengan satu ide program Bu Rina beserta alasan angkanya. Kalau kelompok yang muncul tidak bisa dipasangkan dengan tindakan apa pun, segmentasi itu gagal — betapapun rapi kelompoknya.
Masalah sudah jelas, dan pertanyaannya kini menyangkut bahan bakunya: dari mana data member ini berasal, dan seperti apa isi di balik 32 barisnya? Kita periksa pada Bagian 2: Menyiapkan Data .