Tipe data

dtype numerik, inspeksi tipe, dan demo overflow integer.

Daftar isi

Tipe data (dtype) menentukan cara setiap elemen array disimpan di memori, termasuk banyaknya byte dan jangkauan nilainya. Topik ini membahas keluarga tipe bilangan bulat dan pecahan pada NumPy, cara menginspeksi tipe melalui atribut dtype, konversi tipe dengan astype, serta overflow, yaitu kondisi ketika hasil perhitungan melebihi kapasitas tipe datanya. Setelah topik ini dipahami, tipe data dapat dipilih secara sadar, bukan sekadar mengandalkan nilai bawaan.

dtype: kontrak representasi setiap elemen#

List Python memperbolehkan isi campur, sedangkan array NumPy memakai satu tipe data untuk semua elemennya. Aturan ini menjadi kontrak yang berdampak luas. Ukuran memori setiap elemen sama, sehingga posisi elemen dapat dihitung langsung, dan operasi dapat memproses seluruh nilai dengan cara yang seragam. Sebagai konsekuensinya, jangkauan nilai juga terkunci oleh tipe yang dipilih.

Nama tipe menyertakan ukurannya dalam bit. int8 memakai 8 bit atau 1 byte, sehingga hanya menampung 256 kombinasi nilai, yaitu dari -128 sampai 127. int16, int32, dan int64 berlipat dua ukurannya secara berturut-turut, dan keluarga uint menyimpan bilangan bulat non-negatif dengan jangkauan yang sama lebar tetapi bergeser ke atas. Untuk pecahan, float32 memakai 4 byte dan float64 memakai 8 byte dengan presisi yang lebih tinggi. Nilai boolean juga merupakan tipe tersendiri bernama bool yang memakai 1 byte per elemen.

Batas jangkauan suatu tipe integer dapat diperiksa dengan np.iinfo, sehingga perhitungan tidak perlu menduga-duga kapasitasnya.

python
import numpy as np

print(np.iinfo(np.int8).min, np.iinfo(np.int8).max)
print(np.iinfo(np.int64).max)
text Keluaran
-128 127
9223372036854775807

Menginspeksi dan memilih tipe#

Atribut dtype pada array melaporkan tipe datanya. Apabila array dibuat tanpa parameter dtype, NumPy menebak tipe yang paling sesuai dari datanya.

python
import numpy as np

x = np.array([1, 2, 3])
y = np.array([1.0, 2.5])
z = np.array([True, False, True])
print(x.dtype, y.dtype, z.dtype)
text Keluaran
int64 float64 bool

List bilangan bulat menghasilkan int64, list pecahan menghasilkan float64, dan list boolean menghasilkan bool. Pemilihan eksplisit dilakukan dengan parameter dtype pada saat array dibuat, dan pilihan itu menetapkan representasi seluruh elemen sejak awal.

Pemilihan tipe juga berlaku pada fungsi pembuatan lain, misalnya np.zeros(3, dtype=np.float32) untuk array pecahan yang lebih hemat memori. Prinsipnya satu, yaitu tipe ditetapkan sejak array terbentuk, bukan diubah setelahnya tanpa biaya. Setiap perubahan tipe selalu menghasilkan array baru, karena ukuran elemen di memori ikut berubah.

python
import numpy as np

a = np.array([1, 2, 3], dtype=np.int8)
b = np.array([1, 2, 3], dtype=np.int64)
c = np.array([1.5, 2.5], dtype=np.float32)
print(a.dtype, b.dtype, c.dtype)
text Keluaran
int8 int64 float32

Ketiga array di atas memuat nilai yang sama, tetapi representasinya berbeda. int8 memakai 1 byte per elemen dan int64 memakai 8 byte, sedangkan float32 memakai 4 byte dengan presisi lebih rendah daripada float64. Pemilihan tipe dengan demikian merupakan pertukaran antara memori, jangkauan, dan presisi.

Konversi dengan astype#

Metode astype menghasilkan array baru dengan tipe data yang diminta. Array aslinya tidak berubah, karena konversi tipe membutuhkan buffer memori baru dengan ukuran elemen yang berbeda.

python
import numpy as np

suhu = np.array([21.5, 22.0, 23.4])
bulat = suhu.astype(np.int64)
print(bulat)
print(suhu.dtype, bulat.dtype)
text Keluaran
[21 22 23]
float64 int64

Array suhu tetap bertipe float64, sedangkan bulat adalah array baru bertipe int64. Perhatikan bahwa konversi pecahan ke bilangan bulat memotong bagian pecahannya, sehingga 21.5 menjadi 21 dan bukan 22. Konversi ke tipe yang lebih sempit juga perlu diwaspadai, karena nilai di luar jangkauan tipe tujuan berubah tanpa peringatan, misalnya 300 yang dikonversi ke int8 menjadi 44.

Metode astype menerima tipe tujuan dalam bentuk konstanta NumPy seperti np.int64 maupun nama Python seperti float. Arah konversi menentukan apa yang hilang, yaitu konversi bulat ke pecahan tidak menghilangkan informasi nilai, sedangkan konversi pecahan ke bulat selalu kehilangan bagian pecahannya. Pemilihan arah konversi dengan demikian perlu disesuaikan dengan kebutuhan pengolahan berikutnya.

Overflow integer#

Overflow terjadi ketika hasil perhitungan melebihi jangkauan tipe data. Pada bilangan bulat, NumPy tidak menghentikan eksekusi dan tidak menampilkan warning untuk operasi array. Hasilnya dilipat ke dalam jangkauan tipe, sehingga nilai yang muncul berbeda dari hasil matematika.

python
import numpy as np

a = np.array([127], dtype=np.int8)
hasil = a + 1
print(hasil)
print(hasil.dtype)
text Keluaran
[-128]
int8

Hasil matematika dari 127 + 1 adalah 128, tetapi 128 berada di luar jangkauan int8 yang berhenti di 127. NumPy melipat nilai itu ke batas bawah, sehingga keluarannya -128 dengan tipe tetap int8. Tidak ada error dan tidak ada warning pada operasi array semacam ini. Karena seluruh elemen memakai tipe yang sama, satu elemen yang meluap cukup untuk membuat hasil keseluruhan menyimpang.

Bilangan pecahan berperilaku berbeda. Melebihi batas float64 tidak melipat nilai, melainkan menghasilkan inf sesuai standar aritmetika pecahan IEEE 754. Secara bawaan NumPy menampilkan RuntimeWarning untuk operasi semacam itu, dan contoh berikut menekan warning tersebut dengan np.errstate agar keluarannya bersih.

python
import numpy as np

f = np.array([1e308, 1e308])
with np.errstate(over="ignore"):
    hasil = f * 10
print(hasil)
text Keluaran
[inf inf]

Peluapan bilangan bulat juga berperilaku berbeda antara operasi array dan operasi skalar. Operasi skalar seperti np.int8(127) + 1 menampilkan RuntimeWarning dengan pesan overflow encountered in scalar add, sedangkan operasi array yang sama berjalan tanpa peringatan apa pun. Keduanya tetap menghasilkan -128, sehingga kehati-hatian terhadap jangkauan tetap menjadi tanggung jawab penulis kode.

Kesalahan umum#

Konversi ke bilangan bulat dianggap membulatkan. Metode astype memotong bagian pecahan, sehingga 22.7 menjadi 22 dan bukan 23. Apabila pembulatan diperlukan, panggil np.round terlebih dahulu, kemudian konversi hasilnya dengan astype. Aturan pembulatan np.round sendiri adalah setengah ke genap, sehingga 21.5 dibulatkan menjadi 22 dan bukan 21.

python
import numpy as np

suhu = np.array([21.5, 22.7, 23.4])
print(suhu.astype(int))
print(np.round(suhu).astype(int))
text Keluaran
[21 22 23]
[22 23 23]

Kesalahan kedua adalah memilih int8 atau int16 semata-mata karena hemat memori tanpa memeriksa jangkauan hasil. Penjumlahan kecil seperti 127 + 1 pada int8 sudah melampaui batas dan hasilnya berubah menjadi -128 tanpa peringatan. Perkirakan nilai terburuk dari perhitungan sebelum tipe data ditetapkan, dan bila memori memang menjadi kendala, turunkan tipe secara bertahap sambil membandingkan hasil sebelum dan sesudah konversi.

Kedua kasus di atas tidak memunculkan error sama sekali, sehingga hasil yang keliru dapat lolos dari pengujian kasual. Membandingkan keluaran yang diharapkan dengan keluaran yang sebenarnya pada beberapa sampel nilai cukup untuk menangkap keduanya.

Ringkasan#

  • dtype menentukan banyaknya byte dan jangkauan nilai setiap elemen, dan seluruh elemen array memakai tipe yang sama.
  • NumPy menebak tipe dari datanya, yaitu int64 untuk bilangan bulat, float64 untuk pecahan, dan bool untuk boolean, sedangkan parameter dtype menetapkan pilihan secara eksplisit.
  • astype menghasilkan array baru, memotong bagian pecahan pada konversi ke bilangan bulat, dan dapat mengubah nilai diam-diam bila tipe tujuannya lebih sempit.
  • Overflow integer pada array tidak menampilkan peringatan dan hasilnya dilipat, sedangkan overflow pecahan menghasilkan inf.

Referensi#