Satu pelanggan bisa memiliki tiga puluh baris transaksi; satu toko bisa memiliki ribuan. Model yang memprediksi “apakah pelanggan ini akan berhenti berlangganan?” tidak menerima tiga puluh baris — ia menerima satu baris per pelanggan, dan baris itu harus meringkas seluruh riwayatnya. Agregasi fitur adalah tekniknya: merangkum banyak baris menjadi beberapa statistik per entitas dengan groupby, lalu menempelkan hasilnya kembali ke tabel utama dengan merge. Pada chapter ini kita bangun fitur pelanggan dari data transaksi Toko Kopi Kita: jumlah transaksi, rata-rata belanja, total belanja, dan recency — berapa hari sejak terakhir belanja.
Dari Banyak Baris ke Satu Baris per Pelanggan #
Data mentah kita mencatat empat belas transaksi dari lima pelanggan, lengkap dengan tanggal dan nilai belanja. Tabel semacam ini disebut data level transaksi; model pelanggan membutuhkan data level entitas. Jembatannya adalah groupby + agg:
import pandas as pd
transaksi = pd.DataFrame({
"pelanggan": ["Budi", "Budi", "Budi", "Siti", "Siti",
"Dewi", "Dewi", "Rani", "Rani", "Rani",
"Agus", "Agus", "Siti", "Budi"],
"tanggal": pd.to_datetime([
"2026-01-02", "2026-01-08", "2026-01-15", "2026-01-03",
"2026-01-12", "2026-01-05", "2026-01-18", "2026-01-04",
"2026-01-09", "2026-01-16", "2026-01-07", "2026-01-14",
"2026-01-20", "2026-01-21",
]),
"nilai_ribu": [42, 25, 38, 55, 30, 22, 48, 65, 35, 28, 18, 24, 50, 33],
})
fitur_pelanggan = transaksi.groupby("pelanggan").agg(
jumlah_transaksi=("nilai_ribu", "count"),
rata_belanja=("nilai_ribu", "mean"),
total_belanja=("nilai_ribu", "sum"),
).round(1)
print(fitur_pelanggan)Output yang Dihasilkan:
jumlah_transaksi rata_belanja total_belanja
pelanggan
Agus 2 21.0 42
Budi 4 34.5 138
Dewi 2 35.0 70
Rani 3 42.7 128
Siti 3 45.0 135Mari kita bedah baris demi baris:
- Baris 16-20 (
groupby("pelanggan").agg(...)):groupbymengelompokkan baris menurut pelanggan, laluaggmenghitung beberapa statistik sekaligus — empat belas baris terlipat menjadi lima. - Baris 17 (
jumlah_transaksi=("nilai_ribu", "count")): pasangan(kolom, fungsi)memberi tahu agregasi mana dijalankan pada kolom mana: hitung banyaknya baris dari kolomnilai_ribu. Nama di kiri tanda sama dengan sekaligus menjadi nama kolom hasil — fitur bernama rapi tanpa perlu mengubah nama setelahnya. - Baris 18-19 (
mean,sum): rata-rata dan total belanja per pelanggan. Perhatikan karakter yang muncul: Siti jarang datang tetapi belanja besar sekali datang (rata-rata 45,0); Agus datang sesering Dewi tetapi belanjanya paling kecil (rata-rata 21,0). - Baris 20 (
.round(1)): pembulatan agar tabel terbaca rapi. Baris hasil groupby tersusun menurut indeks — di sini nama pelanggan — secara alfabetis, bukan urutan kemunculan.
Recency dan Penggabungan ke Tabel Utama #
Statistik belanja menangkap “berapa dan berapa sering”, tetapi satu dimensi penting masih hilang: “seberapa baru”. Recency dihitung dari tanggal transaksi terakhir dikurangi tanggal acuan — dan penahapannya mengajarkan pola baru: agregasi kolom tanggal, bukan kolom angka. Setelah lengkap, seluruh fitur digabungkan ke tabel pelanggan induk dengan merge:
import pandas as pd
transaksi = pd.DataFrame({
"pelanggan": ["Budi", "Budi", "Budi", "Siti", "Siti",
"Dewi", "Dewi", "Rani", "Rani", "Rani",
"Agus", "Agus", "Siti", "Budi"],
"tanggal": pd.to_datetime([
"2026-01-02", "2026-01-08", "2026-01-15", "2026-01-03",
"2026-01-12", "2026-01-05", "2026-01-18", "2026-01-04",
"2026-01-09", "2026-01-16", "2026-01-07", "2026-01-14",
"2026-01-20", "2026-01-21",
]),
"nilai_ribu": [42, 25, 38, 55, 30, 22, 48, 65, 35, 28, 18, 24, 50, 33],
})
pelanggan = pd.DataFrame({
"pelanggan": ["Budi", "Siti", "Dewi", "Rani", "Agus"],
"kota": ["Bandung", "Bandung", "Surabaya", "Medan", "Bandung"],
})
acuan = pd.Timestamp("2026-01-31")
fitur = transaksi.groupby("pelanggan").agg(
jumlah_transaksi=("nilai_ribu", "count"),
rata_belanja=("nilai_ribu", "mean"),
terakhir_belanja=("tanggal", "max"),
)
fitur["recency_hari"] = (acuan - fitur["terakhir_belanja"]).dt.days
fitur["rata_belanja"] = fitur["rata_belanja"].round(1)
tabel_lengkap = pelanggan.merge(fitur, on="pelanggan", how="left")
print(tabel_lengkap.to_string(index=False))Output yang Dihasilkan:
pelanggan kota jumlah_transaksi rata_belanja terakhir_belanja recency_hari
Budi Bandung 4 34.5 2026-01-21 10
Siti Bandung 3 45.0 2026-01-20 11
Dewi Surabaya 2 35.0 2026-01-18 13
Rani Medan 3 42.7 2026-01-16 15
Agus Bandung 2 21.0 2026-01-14 17Inilah pekerjaan tiap barisnya:
- Baris 25 (
terakhir_belanja=("tanggal", "max")): agregasi tidak terbatas pada angka —maxpada kolom tanggal mengembalikan transaksi terakhir tiap pelanggan, tetap berupa tipe datetime. - Baris 27 (
(acuan - fitur["terakhir_belanja"]).dt.days): recency dihitung terhadap tanggal acuan yang kita tetapkan (akhir Januari 2026). Pengurangan datetime menghasilkan durasi, dan.dt.daysmengambil jumlah harinya. Angka kecil berarti baru saja belanja; angka Agus (17) berarti hampir tiga pekan tanpa kunjungan. - Baris 30 (
pelanggan.merge(fitur, on="pelanggan", how="left")): tabel fitur ditempelkan ke tabel pelanggan berdasarkan kolom kuncipelanggan.how="left"menjaga seluruh baris tabel kiri — pelanggan tanpa satu pun transaksi tetap hadir dengan nilai kosong, bukan hilang diam-diam. - Hasil akhir: satu baris per pelanggan yang kini memuat identitas (kota) sekaligus perilaku belanja (frekuensi, nilai, kapan terakhir) — bentuk persis yang diminta model klasifikasi pelanggan.
Rangkuman #
groupby(entitas).agg(nama=(kolom, fungsi), ...)melipat data level transaksi menjadi fitur level entitas; pasangan penamaan langsung memberi nama kolom hasil.- Statistik dasar per entitas:
countuntuk frekuensi,meandansumuntuk nilai; kolom tanggal diagregasi denganmaxuntuk menangkap kejadian terakhir. - Recency = selisih tanggal acuan beku dengan transaksi terakhir, diambil lewat
.dt.days; angka ini sering menjadi fitur prediktif terkuat untuk churn. merge(on=kunci, how="left")menempelkan fitur agregat kembali ke tabel utama tanpa kehilangan baris entitas yang tidak punya riwayat.
Fitur pelanggan kita kini lengkap — tetapi jangan buru-buru beranjak: makin banyak fitur, makin besar peluang dua di antaranya mengukur hal yang sama. Kolom kembar semacam itu mengaburkan interpretasi model, dan cara menemukannya adalah pokok bahasan chapter berikutnya.