Dari semua teknik hemat memori di Section ini, dtype category adalah yang paling agresif — dan satu-satunya yang bisa memperburuk keadaan bila dipakai di kolom yang salah. Kuncinya satu angka: kardinalitas, yaitu jumlah nilai unik dibanding jumlah baris. Kolom teks berulang seperti kota dengan lima nilai unik pada 100.000 baris sangat menguntungkan; kolom ID yang hampir unik justru membengkak setelah dikonversi. Chapter ini menghitung penghematannya dengan memory_usage(deep=True), membongkar anatomi category yang membuat penghematan itu terjadi, dan menetapkan ambang kapan konversi layak dilakukan.

Menghitung Penghematan pada Data Nyata #

Skrip berikut memakai tabel transaksi yang berisi dua kolom teks berulang (kota, produk) dan satu kolom teks hampir unik (id_pesanan) sebagai pembanding:

python
import numpy as np
import pandas as pd

rng = np.random.default_rng(42)
n = 100_000
df = pd.DataFrame(
    {
        "kota": rng.choice(["Jakarta", "Bandung", "Surabaya", "Medan", "Semarang"], size=n),
        "produk": rng.choice(
            ["Beras", "Minyak Goreng", "Gula Pasir", "Kopi", "Telur", "Tepung Terigu", "Sabun", "Sarden"],
            size=n,
        ),
        "id_pesanan": [f"TRX-{i:06d}" for i in range(n)],
        "jumlah": rng.integers(1, 11, size=n),
        "harga": rng.choice([15000, 25000, 40000, 55000, 70000], size=n),
    }
)
df["total"] = df["jumlah"] * df["harga"]

# 1. Peta memori sebelum konversi
mu = df.memory_usage(deep=True)
print(mu.to_string())
print("total:", f"{int(mu.sum()):,}", "byte")
print()

# 2. Konversi dua kolom berulang ke category
df["kota"] = df["kota"].astype("category")
df["produk"] = df["produk"].astype("category")
mu2 = df.memory_usage(deep=True)
print("total sesudah:", f"{int(mu2.sum()):,}", "byte")
print("rasio:", round(int(mu.sum()) / int(mu2.sum()), 1))
print()

# 3. Anatomi category: daftar kategori + kode integer
s = df["kota"]
print("kategori:", s.cat.categories.tolist())
print("dtype kode:", s.cat.codes.dtype)
print("kode 4 baris pertama:", s.cat.codes.head(4).tolist())
print()

# 4. Kardinalitas menentukan ukuran kode
for banyak in [5, 200, 70_000]:
    s_k = pd.Series([f"nilai-{i % banyak}" for i in range(n)]).astype("category")
    print(f"{banyak:>6,} kategori -> kode {s_k.cat.codes.dtype}, memori {int(s_k.memory_usage(deep=True)):,} byte")
print()

# 5. Gotcha: teks hampir unik justru lebih boros
print("id str      :", f"{int(df['id_pesanan'].memory_usage(deep=True)):,}", "byte")
print("id category :", f"{int(df['id_pesanan'].astype('category').memory_usage(deep=True)):,}", "byte")
print()

# 6. Analisis tetap berjalan seperti biasa
print("value_counts kota:", df["kota"].value_counts().to_dict())
print("groupby sum per kota:", df.groupby("kota")["total"].sum().astype(int).to_dict())

Output yang Dihasilkan:

text
Index             132
kota          1499235
produk        1563307
id_pesanan    1800000
jumlah         800000
harga          800000
total          800000
total: 7,262,674 byte

total sesudah: 4,400,334 byte
rasio: 1.7

kategori: ['Bandung', 'Jakarta', 'Medan', 'Semarang', 'Surabaya']
dtype kode: int8
kode 4 baris pertama: [1, 2, 2, 4]

     5 kategori -> kode int8, memori 100,208 byte
   200 kategori -> kode int16, memori 203,447 byte
70,000 kategori -> kode int32, memori 1,727,772 byte

id str      : 1,800,132 byte
id category : 2,212,632 byte

value_counts kota: {'Medan': 20268, 'Jakarta': 19981, 'Bandung': 19980, 'Surabaya': 19907, 'Semarang': 19864}
groupby sum per kota: {'Bandung': 4481275000, 'Jakarta': 4482480000, 'Medan': 4560765000, 'Semarang': 4459625000, 'Surabaya': 4515020000}

Mari kita telaah baris-baris pentingnya:

  1. Baris 21-23 (garis dasar): dua kolom teks berulang mendominasi peta memori — kota 1.499.235 byte dan produk 1.563.307 byte — sementara id_pesanan menyita 1.800.000 byte. Total tabel 7.262.674 byte.
  2. Baris 27-31 (konversi selektif): hanya dua kolom berulang yang dikonversi; total turun menjadi 4.400.334 byte (rasio 1.7 untuk tabel utuh). Kolom id_pesanan sengaja tidak disentuh — bagian ke-5 skrip menjelaskan alasannya.
  3. Baris 35-38 (anatomi category): kolom category hanya menyimpan dua hal — daftar kategori terurut (['Bandung', 'Jakarta', 'Medan', 'Semarang', 'Surabaya']) dan satu kode integer per baris (dtype int8, kode 4 baris pertama [1, 2, 2, 4]). Inilah sumber penghematan: teks utuh disimpan sekali, bukan seratus ribu kali.
  4. Baris 42-44 (kardinalitas menentukan dtype kode): 5 kategori memakai kode int8 (100.208 byte), 200 kategori naik ke int16 (203.447 byte), dan 70.000 kategori butuh int32 (1.727.772 byte). Kode tumbuh bertahap, tetapi daftar kategorinya juga ikut membesar.
  5. Baris 48-49 (gotcha teks hampir unik): id_pesanan sebagai str memakan 1.800.132 byte; sebagai category malah 2.212.632 byte — lebih besar, karena pandas menyimpan seluruh teks unik di daftar kategori plus kode int32 per baris. Konversi ini murni merugikan.
  6. Baris 53-54 (analisis tetap jalan): value_counts() dan groupby()...sum() pada kolom category menghasilkan angka yang sama dengan versi str — Medan 20.268 kemunculan, total per kota 4,4-4,6 miliar. Hemat memori tidak menuntut perubahan cara analisis.

Ambang Kapan Konversi Layak #

Kardinalitas (nunique vs panjang)Contoh kolomKeputusan
< 1% (5-10 dari 100.000)kota, status, produkkonversi, penghematan terbesar
1-10%kelurahan, kode_poskonversi, hitung dulu hasilnya
> 50%nama_pelanggan, id_pesananbiarkan str
100% unikkolom IDjangan konversi — memori malah naik

Aturan perhitungannya sederhana: penghematan per baris adalah (ukuran teks + 8 byte pointer) dikurangi ukuran kode (1-4 byte), lalu ditambah biaya sekali simpan daftar kategori. Makin rendah kardinalitas dan makin pendek teksnya, makin besar rasionya.

Dengan dtype yang tepat di setiap kolom, kita sudah menekan memori dari dua sisi: angka lewat downcast dan teks lewat category. Chapter terakhir Section ini memakai semua alat itu untuk menjawab pertanyaan yang lebih besar: sampai batas mana pandas sanggup, dan pilihan apa yang tersedia ketika data benar-benar melebihi kemampuannya.

Referensi resmi untuk pendalaman: panduan Categorical data , pandas.Categorical , dan pandas.Series.cat.codes .