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:

python
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:

text
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:

  1. Baris 3-4: jumlah bertipe int64 dan harga bertipe float64 — dua dtype berbeda bertemu dalam satu operasi.
  2. Baris 5-8: aturan promotion memilih dtype yang “lebih luas”, sehingga total menjadi float64 meskipun kedua operand tak pernah kita minta berubah. Nilai keluarannya benar, tetapi dtype keluaran berbeda dari input pertama.
  3. Baris 10-14: pola sebaliknya — menambahkan 0.5 ke array int64 secara in-place — ditolak interpreter dengan TypeError. 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):

python
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:

text
int64 -> [3.5 4.  4.5] float64
int64 -> [3 4 4] int64
float64 [100. 250. 400.]

Mari kita telaah:

  1. Baris 3-5: menit / 2 menghasilkan [3.5 4. 4.5] bertipe float64 — tidak ada pembagian bulat tersembunyi seperti pada bahasa pemrograman bergaya C.
  2. Baris 6-7: menit // 2 menghasilkan [3 4 4] bertipe int64, membulatkan ke bawah menuju negatif tak hingga.
  3. Baris 9-13: mengalikan array int64 dengan scalar Python 0.1 juga mempromosikan hasilnya menjadi float64; 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:

python
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:

text
[b'12' b'34' b'67']
|S2
ValueError: invalid literal for int() with base 10: np.str_('3a')

Bedah baris demi baris:

  1. Baris 3-5: dtype S2 berarti dua byte per elemen; "345" terpotong menjadi b'34' dan "6789" menjadi b'67'. Tidak ada satu pun pesan peringatan — data langsung berubah.
  2. Baris 6-7: hasilnya array byte (b'...'), bukan string biasa, dan dtype-nya tercetak |S2. Awalan b menandai tipe bytes, dan pembandingannya dengan string Python bernilai False.
  3. Baris 9-13: pola senada terjadi saat konversi gagal — astype(np.int64) pada teks "3a" memunculkan ValueError yang 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:

python
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:

text
int64 [15000 22000 18000]
total: 55000
float64 [0.25 0.5  0.75]

Penjelasan tiap bagian:

  1. Baris 3-4: teks adalah array string; astype(np.int64) mengurai setiap elemen menjadi bilangan bulat. Konversi ini eksplisit — kita yang memutuskan tipe sasarannya.
  2. Baris 5-7: setelah dtype benar, sum menghasilkan 55000 seperti diharapkan; pada array string, sum bahkan ditolak interpreter dengan TypeError karena dtype teks tidak mendukung operasi penjumlahan.
  3. Baris 9-15: pola pemeriksaan bersyarat if data.dtype != np.float64 menjamin langkah perhitungan selalu menerima float; hasilnya [0.25 0.5 0.75] bertipe float64.

Tabel kecil berikut merangkum kapan dtype berubah tanpa suara dan tindakannya:

SituasiPerilaku NumPyTindakan
int64 beroperasi dengan float64hasil dipromosikan ke float64terima, atau konversi sumber dulu
array_int += nilai_floatTypeError, casting ditolakastype dulu, atau buat array baru
int_array / 2selalu true division, hasil floatpakai // bila butuh bulat
dtype="S2" atau "U2" pada teks panjangelemen terpotong diam-diamcek panjang maksimum, hindari dtype eksplisit
astype(int) pada teks non-angkaValueError per elemenbersihkan 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.