Fitur sudah dibuat dan disekalakan; kini giliran mesinnya bekerja. Pada bagian ini kita menjalankan K-Means pada 30 pelanggan dengan dua fitur ternormalisasi, menempelkan label cluster ke setiap pelanggan, lalu membaca pusat tiap cluster dalam satuan yang bisa dibaca manusia. Kita memakai k=3 sejak awal — tiga gerombolan terlihat pada eksplorasi Bagian 4 dan Bu Rina punya tiga ide program — tetapi pilihan ini belum boleh diterima begitu saja; ia akan diadili dengan angka silhouette dan elbow pada Bagian 7.

Cara Kerja K-Means dalam Singkat #

K-Means mengelompokkan titik dengan cara yang bisa diceritakan dalam empat langkah. Pertama, ia meletakkan k buah pusat (centroid) awal pada data — penempatan awal cerdas k-means++ menjauhkan pusat-pusat itu satu sama lain. Kedua, setiap pelanggan ditugaskan ke pusat terdekatnya berdasarkan jarak Euclidean. Ketiga, setiap pusat digeser ke rata-rata seluruh anggotanya. Keempat, langkah kedua dan ketiga diulang sampai tidak ada pelanggan yang berpindah kelompok. Detail algoritmenya sudah dibedah pada chapter K-Means Clustering dan panduan clustering scikit-learn .

Dua parameter praktis perlu kita pegang: random_state=42 membekukan unsur acak pada pemilihan pusat awal sehingga siapa pun yang menjalankan ulang kode ini mendapat hasil yang persis sama, dan n_init="auto" memakai konvensi scikit-learn modern untuk jumlah inisialisasi yang dijalankan. Keterulangan seperti ini bukan hiasan — laporan akhir kita berdiri di atas klaim bahwa seluruh angkanya bisa direproduksi.

Melatih Model dan Menempelkan Label #

Alur penuhnya pendek: muat data bersih, buat fitur, sekalakan, latih model, lalu tempelkan labelnya ke data. Label itulah yang mengubah sekumpulan titik menjadi segmen:

python
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler


def muat_bersih(path):
    """Muat data mentah pelanggan dan terapkan seluruh langkah pembersihan."""
    df = pd.read_csv(path)
    df = df.drop_duplicates().reset_index(drop=True)
    df["kunjungan_30hari"] = pd.to_numeric(df["kunjungan_30hari"], errors="coerce")
    df = df.dropna(subset=["kunjungan_30hari"]).reset_index(drop=True)
    df["kota"] = df["kota"].str.strip().str.title()
    df["kunjungan_30hari"] = df["kunjungan_30hari"].astype(int)
    return df


bersih = muat_bersih("../data/pelanggan.csv")
bersih["belanja_per_kunjungan"] = (
    bersih["belanja_ribu"] / bersih["kunjungan_30hari"]
)
fitur = bersih[["kunjungan_30hari", "belanja_per_kunjungan"]]

skala = StandardScaler().fit_transform(fitur)
model = KMeans(n_clusters=3, random_state=42, n_init="auto")
label = model.fit_predict(skala)

bersih["cluster"] = label
for nomor, n in bersih["cluster"].value_counts().sort_index().items():
    print(f"cluster {nomor}: {n} pelanggan")

print("\nSepuluh pelanggan pertama beserta label clusternya:")
tampil = bersih[["pelanggan", "kunjungan_30hari",
                 "belanja_per_kunjungan", "cluster"]].head(10)
print(tampil.to_string(index=False))

Output yang Dihasilkan:

text
cluster 0: 10 pelanggan
cluster 1: 8 pelanggan
cluster 2: 12 pelanggan

Sepuluh pelanggan pertama beserta label clusternya:
       pelanggan  kunjungan_30hari  belanja_per_kunjungan  cluster
  Siti Rahmawati                25                   20.0        0
Bambang Sutrisno                 4                  120.0        1
 Andi Firmansyah                10                   40.0        2
    Budi Santoso                22                   21.0        0
      Nurul Aini                12                   35.0        2
      Ratna Dewi                 5                  140.0        1
    Dewi Lestari                20                   19.0        0
  Dimas Prasetyo                 8                   55.0        2
    Agus Pratama                24                   24.0        0
      Ayu Kusuma                11                   45.0        2

Mari kita bedah hasilnya:

  1. Tiga cluster terbentuk: 10, 8, dan 12 pelanggan — komposisi yang persis menggemakan tiga gerombolan eksplorasi Bagian 4 (8-12-10 pada rentang kunjungan rendah-menengah-tinggi). Mata kita dan algoritme sepakat.
  2. fit_predict menjalankan pelatihan sekaligus mengembalikan label untuk setiap baris — setara dengan memanggil fit lalu predict secara terpisah.
  3. Label terbaca masuk akal baris demi baris. Siti (25 kunjungan, 20 ribu per kunjungan) masuk cluster 0 bersama Budi dan Dewi yang profilnya serupa; Bambang (4 kunjungan, 120 ribu) masuk cluster 1 bersama Ratna; Andi dan Nurul yang menengah masuk cluster 2. Pola yang sama seperti panel kanan scatter plot Bagian 4.

Membaca Pusat Cluster dalam Satuan Asli #

Label menunjuk kelompok, tetapi angka yang bisa dibaca pemangku kepentingan adalah pusat tiap cluster dalam satuan asli. Objek scaler menyimpan rata-rata dan simpangan baku dari proses fit, sehingga inverse_transform bisa mengembalikan pusat ternormalisasi ke satuan kunjungan dan rupiah ribu:

python
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler


def muat_bersih(path):
    """Muat data mentah pelanggan dan terapkan seluruh langkah pembersihan."""
    df = pd.read_csv(path)
    df = df.drop_duplicates().reset_index(drop=True)
    df["kunjungan_30hari"] = pd.to_numeric(df["kunjungan_30hari"], errors="coerce")
    df = df.dropna(subset=["kunjungan_30hari"]).reset_index(drop=True)
    df["kota"] = df["kota"].str.strip().str.title()
    df["kunjungan_30hari"] = df["kunjungan_30hari"].astype(int)
    return df


bersih = muat_bersih("../data/pelanggan.csv")
bersih["belanja_per_kunjungan"] = (
    bersih["belanja_ribu"] / bersih["kunjungan_30hari"]
)
fitur = bersih[["kunjungan_30hari", "belanja_per_kunjungan"]]

pensekala = StandardScaler()
skala = pensekala.fit_transform(fitur)
model = KMeans(n_clusters=3, random_state=42, n_init="auto").fit(skala)

pusat_asli = pensekala.inverse_transform(model.cluster_centers_)
pusat = pd.DataFrame(
    pusat_asli.round(2),
    columns=["kunjungan_pusat", "belanja_per_kunjungan_pusat"],
)
pusat.insert(0, "cluster", range(3))
print("Pusat tiap cluster, dikembalikan ke satuan asli:")
print(pusat.to_string(index=False))

Output yang Dihasilkan:

text
Pusat tiap cluster, dikembalikan ke satuan asli:
 cluster  kunjungan_pusat  belanja_per_kunjungan_pusat
       0            21.40                        21.30
       1             4.50                       115.00
       2            10.58                        43.58

Tabel pusat tersebut adalah ringkasan perilaku paling ringkas dari seluruh proyek:

  1. Cluster 0 berporos pada 21,40 kunjungan dengan 21,30 ribu per kunjungan — pengunjung rutin yang belanja kecil-kecil. Karena pusat cluster adalah rata-rata anggotanya, angka ini sekaligus janji bahwa anggota cluster ini memang berdekatan di titik itu.
  2. Cluster 1 berporos pada 4,50 kunjungan dengan 115,00 ribu per kunjungan — kebalikan sempurna dari cluster 0: jarang datang, tetapi sekali datang belanja besar.
  3. Cluster 2 di tengah: 10,58 kunjungan dan 43,58 ribu per kunjungan.

Pusat-pusat itu memang terpisah jelas, dan komposisi 10-8-12 terasa benar. Tetapi perasaan benar bukan barang bukti. Berapa pun k yang kita pilih, K-Means selalu menghasilkan kelompok — termasuk k yang salah. Kita adili pilihan k=3 dengan dua alat ukur pada Bagian 7: Mengevaluasi Clustering .