Setiap sel numeric di pandas memakan ruang sesuai dtype-nya: int64 selalu 8 byte per baris, int32 4 byte, int8 cukup 1 byte. Masalahnya, pandas hampir selalu memilih int64 dan float64 secara bawaan saat membaca data, meskipun nilainya hanya 1 sampai 10. Dengan memilih dtype sesuai rentang nilai — lewat astype() — tabel yang sama dapat memakan memori delapan kali lebih kecil tanpa mengubah satu nilai pun. Chapter ini mengukur penghematan per dtype, lalu menunjukkan dua jebakan yang harus dicek sebelum menurunkan resolusi: overflow pada integer dan kehilangan presisi pada float.
Mengukur Penghematan per dtype #
Skrip berikut mengambil dua kolom transaksi dan satu kolom suhu, lalu membandingkan memori memory_usage(deep=True) sebelum dan sesudah astype():
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
n = 100_000
# 1. Kolom jumlah: int64 bawaan vs varian yang muat
jumlah = pd.Series(rng.integers(1, 11, size=n)) # nilai 1..10, dtype int64
print("jumlah int64 :", int(jumlah.memory_usage(deep=True)), "byte")
print("jumlah int32 :", int(jumlah.astype("int32").memory_usage(deep=True)), "byte")
print("jumlah int8 :", int(jumlah.astype("int8").memory_usage(deep=True)), "byte")
print()
# 2. Kolom harga: int64 -> int32 masih muat
harga = pd.Series(rng.choice([15000, 25000, 40000, 55000, 70000], size=n))
print("harga int64:", int(harga.memory_usage(deep=True)), "byte")
print("harga int32:", int(harga.astype("int32").memory_usage(deep=True)), "byte")
print("nilai tetap sama:", harga.equals(harga.astype("int32").astype("int64")))
print()
# 3. Kolom suhu: float64 vs float32
suhu = pd.Series(np.round(rng.uniform(20, 35, size=n), 2))
print("suhu float64:", int(suhu.memory_usage(deep=True)), "byte")
print("suhu float32:", int(suhu.astype("float32").memory_usage(deep=True)), "byte")
print()
# 4. Overflow: int8 hanya menampung -128..127, tanpa galat
print("astype int8 dari 200 :", pd.Series([200]).astype("int8").tolist())
print("astype int8 dari 300 :", pd.Series([300]).astype("int8").tolist())
print("astype uint8 dari 200:", pd.Series([200]).astype("uint8").tolist())
print()
# 5. Presisi float32: sekitar 7 digit signifikan
presisi = pd.Series([0.1, 16777217.0])
print("float64:", presisi.tolist())
print("float32:", presisi.astype("float32").tolist())
print()
# 6. Dtype pyarrow: semantik Arrow, bukan penghemat memori
s_pa = jumlah.astype("int64[pyarrow]")
print("dtype pyarrow:", s_pa.dtype, "| memori:", int(s_pa.memory_usage(deep=True)), "byte")
print()
# 7. Category untuk teks berulang
kota = pd.Series(rng.choice(["Jakarta", "Bandung", "Surabaya", "Medan", "Semarang"], size=n))
print("str deep :", int(kota.memory_usage(deep=True)), "byte")
print("category deep :", int(kota.astype("category").memory_usage(deep=True)), "byte")Output yang Dihasilkan:
jumlah int64 : 800132 byte
jumlah int32 : 400132 byte
jumlah int8 : 100132 byte
harga int64: 800132 byte
harga int32: 400132 byte
nilai tetap sama: True
suhu float64: 800132 byte
suhu float32: 400132 byte
astype int8 dari 200 : [-56]
astype int8 dari 300 : [44]
astype uint8 dari 200: [200]
float64: [0.1, 16777217.0]
float32: [0.10000000149011612, 16777216.0]
dtype pyarrow: int64[pyarrow] | memori: 800132 byte
str deep : 1499778 byte
category deep : 100208 byteMari kita telaah baris-baris pentingnya:
- Baris 5-9 (kolom
jumlah): nilai 1..10 tersimpan sebagaiint64sebesar 800.132 byte.astype("int32")memangkasnya menjadi 400.132 byte, danastype("int8")menjadi 100.132 byte — delapan kali lebih kecil, karena semua nilai muat dalam rentang -128..127. - Baris 12-16 (kolom
harga): harga terbesar 70.000 masih muat diint32(rentang sekitar ±2,1 miliar), sehingga 800.132 byte turun menjadi 400.132 byte. Pemeriksaanequals()setelah bolak-balikastypemenegaskan nilainya tetap sama — konversi ini aman. - Baris 19-22 (kolom
suhu):float64800.132 byte turun menjadifloat32400.132 byte. Penghematan setengahnya nyata, tetapi lihat jebakan presisinya di bawah sebelum memutuskan. - Baris 25-28 (overflow tanpa galat):
astype("int8")pada 200 menghasilkan -56, dan pada 300 menghasilkan 44 — pandas tidak menolak, nilai hanya melingkar (wrap) sesuai aritmetika modulo. Untuk rentang 0..255 tersediauint8; 200 aman di sana. - Baris 31-33 (presisi
float32): angka 0.1 berubah menjadi 0.10000000149011612, dan 16777217.0 turun menjadi 16777216.0.float32hanya menjamin sekitar 7 digit signifikan — cukup untuk sensor atau skor, berbahaya untuk total keuangan. - Baris 36-38 (dtype pyarrow):
astype("int64[pyarrow]")menghasilkan dtypeint64[pyarrow]yang membawa semantik Arrow (nilai missingpd.NA, interoperabilitas ekosistem Arrow) — dan memori terukurnya sama, 800.132 byte. Dtype pyarrow bukan alat hemat memori; nilai plusnya ada di semantik dan interoperabilitas. - Baris 41-43 (teks berulang): kolom
kotasebagaistrmemakan 1.499.778 byte; sebagaicategoryhanya 100.208 byte karena pandas menyimpan daftar kategori plus kode integer per baris. Bahasan lengkapnya ada di Chapter Categorical dan Memory Usage.
Panduan Memilih dtype #
| dtype | Rentang aman | Byte/baris | Cocok untuk |
|---|---|---|---|
int8 | -128 .. 127 | 1 | status, kuantitas kecil |
int16 | -32.768 .. 32.767 | 2 | tahun, jumlah kamar |
int32 | sekitar ±2,1 miliar | 4 | harga, ID pendek |
int64 | sekitar ±9,2 triliun | 8 | ID besar, nilai bawaan |
float32 | presisi ±7 digit | 4 | sensor, skor ML |
float64 | presisi ±15-16 digit | 8 | uang, hasil perhitungan |
category | sesuai jumlah kategori | 1-4 (kode) | teks berulang |
Chapter berikutnya menyatukan teknik ini menjadi alur kerja utuh: mengukur memori per kolom, memangkasnya dengan pd.to_numeric(downcast=...) dan category, lalu memverifikasi nilai tidak berubah.
Referensi resmi untuk pendalaman: pandas.DataFrame.astype , pandas.Series.astype , dan pandas.DataFrame.memory_usage .