Bagian 3 menyerahkan empat segmen yang sudah bernama, terkuantifikasi, dan terpisah dengan silhouette 0,5544: Pelanggan Setia 26,0% basis dengan 69,1% pendapatan, Pelanggan Mengendur 20,0%, Pelanggan Baru 27,3%, dan Pembeli Hemat 26,7%. Namun segmen yang berhenti sebagai tabel belum menghasilkan nilai apa pun — nilai muncul ketika setiap profil menjadi keputusan: siapa dilindungi, siapa direaktivasi, siapa dikembangkan, dan siapa cukup dilayani dengan cara paling murah. Bagian penutup ini menerjemahkan profil menjadi rekomendasi aksi berurutan prioritas, menghitung skenario dampak ilustratif, lalu merangkai seluruh temuan proyek menjadi laporan teks yang bisa dibaca pemangku kepentingan non-teknis.
Dari Profil ke Prioritas Aksi #
Urutan prioritas mengikuti logika perlindungan nilai: segmen dengan pangsa pendapatan terbesar didahulukan, bukan segmen dengan jumlah pelanggan terbanyak. Pelanggan Setia menopang 69,1% pendapatan sehingga risiko kehilangan satu pelanggan dari segmen ini paling mahal — program loyalitas untuk mempertahankan segmen tersebut menempati prioritas pertama. Pelanggan Mengendur menyusul karena segmen tersebut menyumbang 20,0% pendapatan yang sedang menurun recency-nya — kandidat utama kampanye win-back. Pelanggan Baru adalah investensi masa depan: pangsa pendapatannya baru 6,7% tetapi segmen tersebut baru saja aktif bertransaksi. Pembeli Hemat ditangani paling efisien: pangsa 4,2% tidak menutup biaya kampanye premium.
| Segmen | Jumlah | Pangsa pendapatan | Tujuan | Instrumen utama |
|---|---|---|---|---|
| Pelanggan Setia | 78 | 69,1% | mempertahankan | program loyalitas berjenjang |
| Pelanggan Mengendur | 60 | 20,0% | mereaktivasi | kampanye win-back |
| Pelanggan Baru | 82 | 6,7% | mengembangkan | onboarding dan voucher kedua |
| Pembeli Hemat | 80 | 4,2% | efisiensi biaya | kampanye berkala berbiaya rendah |
Skrip bagian ini menjalankan ulang seluruh pipeline dari data/pelanggan.csv — pra-pemrosesan, pemilihan k, model final — sehingga setiap angka laporan dapat direproduksi tanpa keadaan tersimpan. Tiga aksi konkret per segmen disimpan dalam kamus AKSI, prioritas dalam kamus PRIORITAS, dan skenario dampak dihitung dari asumsi yang ditulis eksplisit.
# Bagian 4 - rekomendasi aksi: terjemahkan profil klaster menjadi
# strategi pemasaran yang dapat dieksekusi, lalu tulis laporan teks
# proyek pada laporan/laporan-segmentasi.txt dan cetak isinya.
# Pipeline dijalankan ulang penuh dari data/pelanggan.csv sehingga
# seluruh angka laporan dapat direproduksi tanpa keadaan tersimpan.
# Jalankan dari root proyek: python3 src/05_laporan.py
import os
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
FITUR = [
"total_belanja_ribu",
"frekuensi_pesanan",
"hari_sejak_transaksi",
"rata_belanja_ribu",
]
def rupiah(x_ribu):
"""Format nilai dalam ribu rupiah menjadi teks rupiah penuh."""
return "Rp" + f"{round(x_ribu) * 1000:,.0f}".replace(",", ".")
# ---------- Pipeline lengkap (identik Bagian 2 dan 3) ----------
df = pd.read_csv("data/pelanggan.csv")
X_log = df[FITUR].copy()
X_log["total_belanja_ribu"] = np.log1p(X_log["total_belanja_ribu"])
X = StandardScaler().fit_transform(X_log)
sil_per_k = {}
for k in range(2, 9):
km = KMeans(n_clusters=k, random_state=42, n_init=10).fit(X)
sil_per_k[k] = silhouette_score(X, km.labels_)
k_terbaik = max(sil_per_k, key=lambda k: sil_per_k[k])
km_final = KMeans(n_clusters=k_terbaik, random_state=42, n_init=10).fit(X)
df_klaster = df.copy()
df_klaster["klaster"] = km_final.labels_
profil = (
df_klaster.groupby("klaster")
.agg(
n=("klaster", "size"),
total=("total_belanja_ribu", "mean"),
frek=("frekuensi_pesanan", "mean"),
hari=("hari_sejak_transaksi", "mean"),
rata=("rata_belanja_ribu", "mean"),
pendapatan=("total_belanja_ribu", "sum"),
)
.sort_values("total", ascending=False)
)
pangsa_pendapatan = profil["pendapatan"] / profil["pendapatan"].sum()
pangsa_pelanggan = profil["n"] / profil["n"].sum()
def label_bisnis(baris):
if baris["hari"] < 30 and baris["frek"] >= 8:
return "Pelanggan Setia"
if baris["hari"] < 30 and baris["frek"] < 8:
return "Pelanggan Baru"
if baris["hari"] >= 30 and baris["frek"] >= 8:
return "Pelanggan Mengendur"
return "Pembeli Hemat"
AKSI = {
"Pelanggan Setia": [
"program loyalitas berjenjang dengan poin tiap transaksi",
"akses awal koleksi baru sebelum dijual ke publik",
"undangan survei kepuasan langsung oleh tim layanan",
],
"Pelanggan Mengendur": [
"kampanye win-back: voucher reaktivasi berbatas waktu",
"survei singkat alasan berhenti belanja (maksimal 3 pertanyaan)",
"penawaran produk yang dulu sering dibeli (reminder personal)",
],
"Pelanggan Baru": [
"alur onboarding 30 hari pertama lewat email berseri",
"voucher pesanan kedua berlaku 14 hari",
"rekomendasi produk populer untuk mengangkat frekuensi",
],
"Pembeli Hemat": [
"bundel hemat dan voucher minimal belanja agar rata naik",
"kanal biaya rendah (email dan notifikasi) tanpa cetak fisik",
"jadwal kampanye berkala, bukan insentif besar satu kali",
],
}
PRIORITAS = {
"Pelanggan Setia": 1,
"Pelanggan Mengendur": 2,
"Pelanggan Baru": 3,
"Pembeli Hemat": 4,
}
# ---------- Rekomendasi aksi per segmen ----------
print("== Rekomendasi aksi per segmen ==")
for id_k, baris in profil.iterrows():
label = label_bisnis(baris)
print(f"\n-- {label} (klaster {id_k}, prioritas {PRIORITAS[label]}) --")
print(f"jumlah : {int(baris['n'])} pelanggan "
f"({pangsa_pelanggan[id_k]:.1%} basis, "
f"{pangsa_pendapatan[id_k]:.1%} pendapatan)")
print(f"profil : total {rupiah(baris['total'])}; "
f"{baris['frek']:.1f} pesanan; transaksi terakhir "
f"{baris['hari']:.0f} hari lalu")
for i, aksi in enumerate(AKSI[label], start=1):
print(f"aksi {i} : {aksi}")
# ---------- Skenario dampak ilustratif ----------
n_menge = int(profil.iloc[1]["n"])
total_menge = profil.iloc[1]["total"]
naik_menge = 0.20 * n_menge * total_menge
n_baru = int(profil.iloc[2]["n"])
rata_baru = profil.iloc[2]["rata"]
frek_baru = profil.iloc[2]["frek"]
naik_baru = n_baru * (4.0 - frek_baru) * rata_baru
print("\n== Skenario dampak (ilustratif, berbasis asumsi) ==")
print(f"reaktivasi 20% Pelanggan Mengendur : +{rupiah(naik_menge)}")
print(f"pesanan kedua Pelanggan Baru (frekuensi 2.5 -> 4.0): "
f"+{rupiah(naik_baru)}")
# ---------- Tulis laporan teks ----------
os.makedirs("laporan", exist_ok=True)
baris_laporan = [
"=" * 64,
"LAPORAN PROYEK: SEGMENTASI PELANGGAN RITEL ONLINE",
"=" * 64,
"",
"[RINGKASAN DATA]",
f"- Pelanggan : {len(df)} baris dari data/pelanggan.csv",
"- Fitur : total_belanja_ribu, frekuensi_pesanan,",
" hari_sejak_transaksi, rata_belanja_ribu (RFM + nilai pesanan)",
"- Label : tidak ada - masalah unsupervised learning",
"- Sumber : data sintetis deterministik (rng seed 42)",
"",
"[PRA-PEMROSESAN]",
"- Kemiringan : total_belanja_ribu skewness 1.016 -> 0.136",
" setelah transformasi log1p; fitur lain cukup simetris",
"- Scaling : StandardScaler pada keempat fitur",
"- Alasan : pada data mentah total_belanja_ribu",
" menyumbang 99.95% total varians sehingga jarak Euclidean",
" didominasi satu fitur; data murni numerik sehingga tidak",
" ada encoding kategorikal dan tidak ada dummy trap",
"",
"[JUMLAH KLASTER TERPILIH]",
f"- Rentang diuji : k=2 sampai k=8 (KMeans random_state=42,",
" n_init=10)",
f"- Terpilih : k={k_terbaik} (silhouette "
f"{sil_per_k[k_terbaik]:.4f})",
"- Pertimbangan : elbow pada inertia lembut (turun tajam ke",
" k=4 lalu mendatar); silhouette memuncak jelas di k=4",
"",
"[PROFIL KLASTER]",
]
for id_k, baris in profil.iterrows():
label = label_bisnis(baris)
baris_laporan += [
f"- {label} (klaster {id_k})",
f" n={int(baris['n'])} ({pangsa_pelanggan[id_k]:.1%} pelanggan,"
f" {pangsa_pendapatan[id_k]:.1%} pendapatan)",
f" total {rupiah(baris['total'])} | frekuensi {baris['frek']:.1f}"
f" | recency {baris['hari']:.0f} hari | rata {rupiah(baris['rata'])}",
]
baris_laporan += [
"",
"[REKOMENDASI AKSI]",
]
for id_k, baris in profil.iterrows():
label = label_bisnis(baris)
baris_laporan.append(f"- {label} (prioritas {PRIORITAS[label]}):")
for aksi in AKSI[label]:
baris_laporan.append(f" * {aksi}")
baris_laporan += [
"",
"[SKENARIO DAMPAK ILUSTRATIF]",
f"- Reaktivasi 20% Pelanggan Mengendur : +{rupiah(naik_menge)}",
f"- Pesanan kedua Pelanggan Baru : +{rupiah(naik_baru)}",
"",
"[ASUMSI DAN CATATAN]",
"- Ambang label bisnis: recency 30 hari, frekuensi 8 pesanan.",
"- Skenario dampak memakai asumsi sederhana (tingkat reaktivasi",
" dan kenaikan frekuensi) dan bukan proyeksi operasional nyata.",
"- Seluruh angka dihitung ulang dari data sintetis 300 pelanggan",
" untuk keperluan pembelajaran.",
"",
"Model final: KMeans k=4 pada fitur log1p(total) + StandardScaler;",
"laporan ini merangkum keputusan akhir proyek.",
]
with open("laporan/laporan-segmentasi.txt", "w", encoding="utf-8") as f:
f.write("\n".join(baris_laporan) + "\n")
print("\n== Laporan ditulis ke laporan/laporan-segmentasi.txt ==")
print("\n== Isi laporan/laporan-segmentasi.txt ==")
with open("laporan/laporan-segmentasi.txt", encoding="utf-8") as f:
print(f.read().rstrip("\n"))Output yang Dihasilkan:
== Rekomendasi aksi per segmen ==
-- Pelanggan Setia (klaster 3, prioritas 1) --
jumlah : 78 pelanggan (26.0% basis, 69.1% pendapatan)
profil : total Rp13.525.000; 32.2 pesanan; transaksi terakhir 9 hari lalu
aksi 1 : program loyalitas berjenjang dengan poin tiap transaksi
aksi 2 : akses awal koleksi baru sebelum dijual ke publik
aksi 3 : undangan survei kepuasan langsung oleh tim layanan
-- Pelanggan Mengendur (klaster 1, prioritas 2) --
jumlah : 60 pelanggan (20.0% basis, 20.0% pendapatan)
profil : total Rp5.103.000; 14.2 pesanan; transaksi terakhir 58 hari lalu
aksi 1 : kampanye win-back: voucher reaktivasi berbatas waktu
aksi 2 : survei singkat alasan berhenti belanja (maksimal 3 pertanyaan)
aksi 3 : penawaran produk yang dulu sering dibeli (reminder personal)
-- Pelanggan Baru (klaster 0, prioritas 3) --
jumlah : 82 pelanggan (27.3% basis, 6.7% pendapatan)
profil : total Rp1.241.000; 2.5 pesanan; transaksi terakhir 6 hari lalu
aksi 1 : alur onboarding 30 hari pertama lewat email berseri
aksi 2 : voucher pesanan kedua berlaku 14 hari
aksi 3 : rekomendasi produk populer untuk mengangkat frekuensi
-- Pembeli Hemat (klaster 2, prioritas 4) --
jumlah : 80 pelanggan (26.7% basis, 4.2% pendapatan)
profil : total Rp807.000; 3.4 pesanan; transaksi terakhir 81 hari lalu
aksi 1 : bundel hemat dan voucher minimal belanja agar rata naik
aksi 2 : kanal biaya rendah (email dan notifikasi) tanpa cetak fisik
aksi 3 : jadwal kampanye berkala, bukan insentif besar satu kali
== Skenario dampak (ilustratif, berbasis asumsi) ==
reaktivasi 20% Pelanggan Mengendur : +Rp61.236.000
pesanan kedua Pelanggan Baru (frekuensi 2.5 -> 4.0): +Rp58.625.000
== Laporan ditulis ke laporan/laporan-segmentasi.txt ==
== Isi laporan/laporan-segmentasi.txt ==
================================================================
LAPORAN PROYEK: SEGMENTASI PELANGGAN RITEL ONLINE
================================================================
[RINGKASAN DATA]
- Pelanggan : 300 baris dari data/pelanggan.csv
- Fitur : total_belanja_ribu, frekuensi_pesanan,
hari_sejak_transaksi, rata_belanja_ribu (RFM + nilai pesanan)
- Label : tidak ada - masalah unsupervised learning
- Sumber : data sintetis deterministik (rng seed 42)
[PRA-PEMROSESAN]
- Kemiringan : total_belanja_ribu skewness 1.016 -> 0.136
setelah transformasi log1p; fitur lain cukup simetris
- Scaling : StandardScaler pada keempat fitur
- Alasan : pada data mentah total_belanja_ribu
menyumbang 99.95% total varians sehingga jarak Euclidean
didominasi satu fitur; data murni numerik sehingga tidak
ada encoding kategorikal dan tidak ada dummy trap
[JUMLAH KLASTER TERPILIH]
- Rentang diuji : k=2 sampai k=8 (KMeans random_state=42,
n_init=10)
- Terpilih : k=4 (silhouette 0.5544)
- Pertimbangan : elbow pada inertia lembut (turun tajam ke
k=4 lalu mendatar); silhouette memuncak jelas di k=4
[PROFIL KLASTER]
- Pelanggan Setia (klaster 3)
n=78 (26.0% pelanggan, 69.1% pendapatan)
total Rp13.525.000 | frekuensi 32.2 | recency 9 hari | rata Rp421.000
- Pelanggan Mengendur (klaster 1)
n=60 (20.0% pelanggan, 20.0% pendapatan)
total Rp5.103.000 | frekuensi 14.2 | recency 58 hari | rata Rp362.000
- Pelanggan Baru (klaster 0)
n=82 (27.3% pelanggan, 6.7% pendapatan)
total Rp1.241.000 | frekuensi 2.5 | recency 6 hari | rata Rp493.000
- Pembeli Hemat (klaster 2)
n=80 (26.7% pelanggan, 4.2% pendapatan)
total Rp807.000 | frekuensi 3.4 | recency 81 hari | rata Rp241.000
[REKOMENDASI AKSI]
- Pelanggan Setia (prioritas 1):
* program loyalitas berjenjang dengan poin tiap transaksi
* akses awal koleksi baru sebelum dijual ke publik
* undangan survei kepuasan langsung oleh tim layanan
- Pelanggan Mengendur (prioritas 2):
* kampanye win-back: voucher reaktivasi berbatas waktu
* survei singkat alasan berhenti belanja (maksimal 3 pertanyaan)
* penawaran produk yang dulu sering dibeli (reminder personal)
- Pelanggan Baru (prioritas 3):
* alur onboarding 30 hari pertama lewat email berseri
* voucher pesanan kedua berlaku 14 hari
* rekomendasi produk populer untuk mengangkat frekuensi
- Pembeli Hemat (prioritas 4):
* bundel hemat dan voucher minimal belanja agar rata naik
* kanal biaya rendah (email dan notifikasi) tanpa cetak fisik
* jadwal kampanye berkala, bukan insentif besar satu kali
[SKENARIO DAMPAK ILUSTRATIF]
- Reaktivasi 20% Pelanggan Mengendur : +Rp61.236.000
- Pesanan kedua Pelanggan Baru : +Rp58.625.000
[ASUMSI DAN CATATAN]
- Ambang label bisnis: recency 30 hari, frekuensi 8 pesanan.
- Skenario dampak memakai asumsi sederhana (tingkat reaktivasi
dan kenaikan frekuensi) dan bukan proyeksi operasional nyata.
- Seluruh angka dihitung ulang dari data sintetis 300 pelanggan
untuk keperluan pembelajaran.
Model final: KMeans k=4 pada fitur log1p(total) + StandardScaler;
laporan ini merangkum keputusan akhir proyek.Mari kita bedah skrip laporan baris-per-baris:
- Baris 23–25 (
rupiah): pembantu format mengubah nilai ribu rupiah menjadi teks penuh — 13525.1 menjadiRp13.525.000— sehingga laporan terbaca tanpa konversi manual oleh pembaca bisnis. - Baris 27–37 (pipeline ulang): pra-pemrosesan dan pemilihan k dihitung ulang dari CSV; laporan tidak bergantung pada hasil eksekusi bagian sebelumnya yang mungkin sudah tidak berada di memori.
- Baris 39–56 (model final dan pangsa): KMeans final dilatih, profil diagregasi, lalu dua kolom pangsa — pelanggan dan pendapatan — dihitung; keduanya menjadi dasar tabel prioritas di atas.
- Baris 58–65 (
label_bisnis): fungsi ambang domain yang sama dengan Bagian 3 didefinisikan ulang agar skrip tetap mandiri; recency 30 hari dan frekuensi 8 pesanan tetap tertulis eksplisit. - Baris 67–94 (
AKSIdanPRIORITAS): seluruh rekomendasi disimpan sebagai data, bukan teks bebas — kamus ini bisa diganti tim pemasaran tanpa menyentuh logika analisis. - Baris 111–121 (skenario dampak): dua angka ilustratif dihitung dengan asumsi tertulis — reaktivasi 20% Pelanggan Mengendur dan kenaikan frekuensi Pelanggan Baru ke 4 pesanan. Posisi baris
iloc[1]daniloc[2]mengacu profil terurut total: Mengendur dan Baru. - Baris 124–191 (perakitan laporan): daftar
baris_laporandisusun bagian demi bagian — data, pra-pemrosesan, jumlah klaster, profil, rekomendasi, skenario, asumsi — lalu ditulis kelaporan/laporan-segmentasi.txt. - Baris 193–196 (cetak isi): berkas laporan dibaca balik dan dicetak penuh, memastikan apa yang tersimpan di disk identik dengan yang diperiksa pembaca.
Membaca Laporan dan Batasnya #
Laporan akhir merangkum benang merah seluruh proyek dalam enam bagian berlabel. Skenario dampak mencoret dua angka yang menarik perhatian: reaktivasi 20% Pelanggan Mengendur bernilai +Rp61.236.000 dan keberhasilan program pesanan kedua Pelanggan Baru bernilai +Rp58.625.000. Kedua angka tersebut dihitung dari rata-rata belanja segmen dikali asumsi tingkat keberhasilan — cara sederhana yang lazim dipakai untuk uji kelayakan inisiatif sebelum eksperimen sungguhan dijalankan.
Dengan laporan tertulis, proyek segmentasi ditutup penuh: dari data tanpa label menjadi empat segmen bernama, tiap segmen membawa aksi dan prioritas, dan seluruh keputusan dapat direproduksi ulang dengan satu perintah per skrip. Pola unsupervised yang kita jalankan — eksplorasi distribusi, ruang fitur yang adil, pemilihan k terukur, profil berbahasa bisnis — berlaku umum untuk masalah segmentasi lain: pengunjung situs, pengguna aplikasi, hingga produk yang dijual bersama.
Proyek ini memandang basis pelanggan sebagai potret statis satu periode — sekian ratus baris yang dibekukan pada satu titik waktu. Pertanyaan yang belum terjawab adalah pertanyaan waktu: bagaimana permintaan bergerak dari bulan ke bulan, dan apa yang bisa diprediksi dari pergerakan tersebut? Jawabannya menuntut model yang membaca urutan waktu, bukan kedekatan antar baris. Kita kerjakan pada proyek berikutnya: Proyek: Forecasting Penjualan.
Referensi Resmi #
- Clustering — scikit-learn User Guide — dasar pemilihan jumlah klaster untuk laporan.
- sklearn.metrics.silhouette_score — API — metrik internal yang dikutip laporan.
- pandas.DataFrame.groupby — API — agregasi profil klaster dan pangsa pendapatan.