Nilai numerik kontinu seperti nominal transaksi sulit dibaca kalau ditampilkan apa adanya. Laporan bisnis biasanya lebih mudah dipahami bila nilainya dikelaskan, misalnya menjadi Murah, Sedang, dan Mahal. Proses membagi rentang angka ke dalam beberapa interval (bin) ini disebut binning, dan pandas menyediakan fungsi pd.cut() untuk keperluan itu. Pada Chapter ini kita menentukan batas bin secara manual, memberi label Indonesia, lalu memeriksa perilaku tepi interval dan nilai yang berada di luar rentang.

Membagi Transaksi ke Kelas Harga #

Skrip berikut membagi nilai_transaksi menjadi tiga kelas harga dengan tepi yang kita pilih sendiri. Setelah kolom baru terbentuk, skrip menghitung rekap per kelas, lalu menguji dua perilaku tepi yang paling sering membuat pemula terkejut. Jalankan dan amati kelima cetakannya.

python
import pandas as pd

pesanan = pd.DataFrame({
    "id_pesanan": ["PSN-001", "PSN-002", "PSN-003", "PSN-004",
                   "PSN-005", "PSN-006", "PSN-007", "PSN-008"],
    "nama_pelanggan": ["Budi", "Siti", "Dewi", "Andi",
                       "Rina", "Tono", "Lina", "Sari"],
    "kota": ["Jakarta", "Bandung", "Surabaya", "Jakarta",
             "Medan", "Bandung", "Jakarta", "Surabaya"],
    "pembayaran": ["QRIS", "Transfer", "QRIS", "Kartu Kredit",
                   "COD", "Transfer", "QRIS", "COD"],
    "nilai_transaksi": [250_000, 1_800_000, 750_000, 2_400_000,
                        480_000, 1_200_000, 320_000, 980_000],
    "berat_paket": [1.2, 4.5, 0.8, 7.0, 2.5, 3.0, 1.0, 5.5],
})

tepi = [0, 500_000, 1_500_000, 2_500_000]
label = ["Murah", "Sedang", "Mahal"]

pesanan["kelas_harga"] = pd.cut(
    pesanan["nilai_transaksi"], bins=tepi, labels=label
)
print(pesanan[["id_pesanan", "nilai_transaksi", "kelas_harga"]])
print()
print(pesanan["kelas_harga"].value_counts())
print()
print("dtype:", pesanan["kelas_harga"].dtype)
print()

transaksi_lain = pd.Series([300_000, 3_000_000, 2_500_000])
print(pd.cut(transaksi_lain, bins=tepi, labels=label))
print()

print(pd.cut(pd.Series([0]), bins=tepi, labels=label).tolist())
print(pd.cut(pd.Series([0]), bins=tepi, labels=label, include_lowest=True).tolist())

Output yang Dihasilkan:

text
  id_pesanan  nilai_transaksi kelas_harga
0    PSN-001           250000       Murah
1    PSN-002          1800000       Mahal
2    PSN-003           750000      Sedang
3    PSN-004          2400000       Mahal
4    PSN-005           480000       Murah
5    PSN-006          1200000      Sedang
6    PSN-007           320000       Murah
7    PSN-008           980000      Sedang

kelas_harga
Murah     3
Sedang    3
Mahal     2
Name: count, dtype: int64

dtype: category

0    Murah
1      NaN
2    Mahal
dtype: category
Categories (3, str): ['Murah' < 'Sedang' < 'Mahal']

[nan]
['Murah']

Mari kita telaah apa yang terjadi pada kode di atas:

  1. Baris 17-18 (tepi = [...], label = [...]): list tepi berisi empat angka yang membentuk tiga interval: 0-500 ribu, 500 ribu-1,5 juta, dan 1,5-2,5 juta. Panjang label harus sama dengan jumlah interval, yaitu tiga.
  2. Baris 20-22 (pesanan["kelas_harga"] = pd.cut(...)): pd.cut() mencari interval untuk setiap nilai nilai_transaksi dan menggantinya dengan label kelasnya. Hasilnya Series bertipe category dengan urutan kategori Murah < Sedang < Mahal, terlihat pada cetakan dtype: category di baris 27.
  3. Baris 25 (value_counts()): method Series.value_counts() merangkum isi tiap kelas: Murah 3, Sedang 3, Mahal 2. Bin dengan lebar sama belum tentu berisi jumlah data yang sama, karena isi bin mengikuti sebaran data.
  4. Baris 30-31 (transaksi_lain): nilai 3.000.000 berada di atas tepi atas sehingga menjadi NaN, sedangkan 2.500.000 yang tepat di tepi atas tetap masuk Mahal. Secara bawaan interval ditutup di sisi kanan (right=True), sehingga bentuknya (tepi kiri, tepi kanan].
  5. Baris 34-35 (pd.Series([0])): nilai 0 menjadi NaN karena interval pertama terbuka di sisi kiri. Argumen include_lowest=True menutup tepi pertama, sehingga 0 ikut masuk ke kelas Murah.

Aturan Tepi Interval dan Nilai di Luar Rentang #

Konvensi interval pd.cut() mengikuti notasi matematika. Dengan right=True (bawaan), interval berbentuk (a, b]: nilai a tidak termasuk, nilai b termasuk. Mengirim right=False membalik konvensinya menjadi [a, b); pada data Chapter ini, nilai 500.000 yang semula masuk Murah akan berpindah ke Sedang. Pilihan right penting saat tepi bin bertepatan dengan nilai data yang nyata, misalnya batas 500 ribu yang sering muncul di transaksi.

pd.cut() memberi kontrol penuh atas batas kelas, sehingga setiap kelas punya definisi bisnis yang tetap dan mudah dikomunikasikan. Bila tujuannya berubah menjadi menjaga jumlah isi tiap bin tetap seimbang, batas harus dihitung dari data itu sendiri. Chapter berikutnya membahas pd.qcut(), yang menentukan batas bin dari kuantil data.