Tidak semua bug memunculkan error. Ketika dtype tidak sesuai, NumPy sering kali melanjutkan perhitungan dengan aturan konversi bawaannya — dan hasilnya bergeser tanpa suara: bilangan bulat dipromosikan menjadi float, pembagian menghasilkan desimal padahal kita mengharapkan bilangan bulat, atau teks terpotong saat array dibuat. Karena tidak ada traceback untuk dibaca, bug dtype membutuhkan kebiasaan pemeriksaan yang berbeda.
Bab ini menelusuri empat pola umum: promosi dtype pada operasi campuran, perbedaan pembagian bulat dan pembagian nyata, pemotongan diam-diam pada dtype string, serta kebiasaan memeriksa dtype sebelum beroperasi dan memperbaikinya dengan astype. Latar teori tipe data telah dibahas pada Chapter Atribut dtype dan Chapter Konversi Tipe Data.
Promosi dtype yang Berubah Diam-diam #
Mengoperasikan array int64 dengan array float64 menghasilkan array float64. Aturan ini disebut type promotion dan biasanya memang diinginkan — tetapi ia juga mengubah dtype keluaran yang mungkin kita harapkan tetap bulat:
import numpy as np
jumlah = np.array([10, 20, 30])
harga = np.array([3500.5, 1200.0, 980.25])
total = jumlah * harga
print("dtype jumlah:", jumlah.dtype)
print("dtype harga :", harga.dtype)
print("dtype total :", total.dtype)
print(total)
stok = np.array([1, 2, 3])
try:
stok += 0.5
except TypeError as e:
print("TypeError:", e)Output yang Dihasilkan:
dtype jumlah: int64
dtype harga : float64
dtype total : float64
[35005. 24000. 29407.5]
TypeError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'Bedah baris demi baris:
- Baris 3-4:
jumlahbertipeint64danhargabertipefloat64— dua dtype berbeda bertemu dalam satu operasi. - Baris 5-8: aturan promotion memilih dtype yang “lebih luas”, sehingga
totalmenjadifloat64meskipun kedua operand tak pernah kita minta berubah. Nilai keluarannya benar, tetapi dtype keluaran berbeda dari input pertama. - Baris 10-14: pola sebaliknya — menambahkan
0.5ke arrayint64secara in-place — ditolak interpreter denganTypeError. Menyimpan hasil float ke dalam penampung integer membutuhkan pemangkasan, dan NumPy menolak melakukannya diam-diam.
Pembagian Bulat vs Pembagian Nyata #
Operator / pada array NumPy selalu menghasilkan pembagian nyata (true division) dengan keluaran float64, meskipun kedua operand bertipe bulat. Jika kita mengharapkan pembagian bulat, operatornya adalah // (floor division):
import numpy as np
menit = np.array([7, 8, 9])
bagi_per_dua = menit / 2
print(menit.dtype, "->", bagi_per_dua, bagi_per_dua.dtype)
bagi_bulat = menit // 2
print(menit.dtype, "->", bagi_bulat, bagi_bulat.dtype)
diskon = np.array([1000, 2500, 4000])
persen = 0.1
potongan = diskon * persen
print(potongan.dtype, potongan)Output yang Dihasilkan:
int64 -> [3.5 4. 4.5] float64
int64 -> [3 4 4] int64
float64 [100. 250. 400.]Mari kita telaah:
- Baris 3-5:
menit / 2menghasilkan[3.5 4. 4.5]bertipefloat64— tidak ada pembagian bulat tersembunyi seperti pada bahasa pemrograman bergaya C. - Baris 6-7:
menit // 2menghasilkan[3 4 4]bertipeint64, membulatkan ke bawah menuju negatif tak hingga. - Baris 9-13: mengalikan array
int64dengan scalar Python0.1juga mempromosikan hasilnya menjadifloat64; deretan[100. 250. 400.]tampak bulat, tetapi dtype-nya kini float — detail yang penting saat hasilnya diteruskan ke fungsi yang mensyaratkan integer.
Bug klasik dari pola ini adalah indeks array yang dihitung lewat /: indeks bertipe float64 ditolak interpreter dengan IndexError: only integers, slices (...) are valid indices. Ketika pesan itu muncul, periksa apakah perhitungan indeks seharusnya memakai // atau dibungkus int().
Truncation pada dtype String #
Membuat array string dengan dtype ukuran tetap memotong elemen yang lebih panjang — tanpa error, tanpa peringatan. Perilaku ini berlaku untuk dtype byte S maupun unicode U:
import numpy as np
kode = np.array(["12", "345", "6789"], dtype="S2")
print(kode)
print(kode.dtype)
rusak = np.array(["12", "3a"])
try:
rusak.astype(np.int64)
except ValueError as e:
print("ValueError:", e)Output yang Dihasilkan:
[b'12' b'34' b'67']
|S2
ValueError: invalid literal for int() with base 10: np.str_('3a')Bedah baris demi baris:
- Baris 3-5: dtype
S2berarti dua byte per elemen;"345"terpotong menjadib'34'dan"6789"menjadib'67'. Tidak ada satu pun pesan peringatan — data langsung berubah. - Baris 6-7: hasilnya array byte (
b'...'), bukan string biasa, dan dtype-nya tercetak|S2. Awalanbmenandai tipebytes, dan pembandingannya dengan string Python bernilaiFalse. - Baris 9-13: pola senada terjadi saat konversi gagal —
astype(np.int64)pada teks"3a"memunculkanValueErroryang menyebut literal yang tidak dapat diurai.
Kebiasaan Memeriksa dtype dan Perbaikan astype #
Kebalikan masalah di atas adalah data teks yang seharusnya angka — misalnya hasil baca CSV yang belum dikonversi. Solusinya dua langkah: periksa dtype, lalu konversi dengan astype:
import numpy as np
teks = np.array(["15000", "22000", "18000"])
angka = teks.astype(np.int64)
print(angka.dtype, angka)
total = angka.sum()
print("total:", total)
data = np.array([1, 2, 3])
if data.dtype != np.float64:
data = data.astype(np.float64)
hasil = data / 4
print(hasil.dtype, hasil)Output yang Dihasilkan:
int64 [15000 22000 18000]
total: 55000
float64 [0.25 0.5 0.75]Penjelasan tiap bagian:
- Baris 3-4:
teksadalah array string;astype(np.int64)mengurai setiap elemen menjadi bilangan bulat. Konversi ini eksplisit — kita yang memutuskan tipe sasarannya. - Baris 5-7: setelah dtype benar,
summenghasilkan55000seperti diharapkan; pada array string,sumbahkan ditolak interpreter denganTypeErrorkarena dtype teks tidak mendukung operasi penjumlahan. - Baris 9-15: pola pemeriksaan bersyarat
if data.dtype != np.float64menjamin langkah perhitungan selalu menerima float; hasilnya[0.25 0.5 0.75]bertipefloat64.
Tabel kecil berikut merangkum kapan dtype berubah tanpa suara dan tindakannya:
| Situasi | Perilaku NumPy | Tindakan |
|---|---|---|
int64 beroperasi dengan float64 | hasil dipromosikan ke float64 | terima, atau konversi sumber dulu |
array_int += nilai_float | TypeError, casting ditolak | astype dulu, atau buat array baru |
int_array / 2 | selalu true division, hasil float | pakai // bila butuh bulat |
dtype="S2" atau "U2" pada teks panjang | elemen terpotong diam-diam | cek panjang maksimum, hindari dtype eksplisit |
astype(int) pada teks non-angka | ValueError per elemen | bersihkan data sebelum konversi |
Kebiasaan penutup yang layak dijadikan standar: sebelum operasi penting, cetak atau asersikan dtype array masukan; sesudah operasi, periksa dtype keluaran bila hasilnya diteruskan ke fungsi lain. Satu baris print(data.dtype) di titik yang tepat sering kali memangkas waktu debugging dtype-mismatch dari satu jam menjadi satu menit.