Structured array pada Chapter Structured Array memakai dtype yang kita definisikan langsung di dalam pemanggilan np.array. Ketika skema data menjadi lebih kompleks — field bertumpuk, lebar byte tertentu, atau dipakai bersama beberapa fungsi — skema tersebut layak dirancang sebagai custom dtype yang berdiri sendiri, lalu dipakai ulang di banyak tempat.
Bab ini merangkum perangkat pembuatannya: dua cara menuliskan dtype, field bersarang, pembacaan array dari buffer byte mentah, serta pembuatan array dari fungsi dan perulangan terindeks. Semuanya berkisar pada satu gagasan: array adalah penampung byte, dan dtype adalah cara kita menafsirkan byte itu.
Menyusun dtype: Bentuk Tuple dan Dict #
Cara paling lazim adalah list of tuples seperti sebelumnya. NumPy juga menerima bentuk dict yang menulis nama, format, dan opsional offset byte secara eksplisit — keduanya menghasilkan dtype yang identik:
import numpy as np
dt_tuple = np.dtype([("kode", "U4"), ("stok", "i4"), ("harga", "f8")])
dt_dict = np.dtype({"names": ["kode", "stok", "harga"],
"formats": ["U4", "i4", "f8"]})
print(dt_tuple == dt_dict)
print(dt_tuple.fields)
print(dt_tuple.itemsize)Output yang Dihasilkan:
True
{'kode': (dtype('<U4'), 0), 'stok': (dtype('int32'), 16), 'harga': (dtype('float64'), 20)}
28Mari kita bedah:
- Baris 3-5: dua penulisan menghasilkan dtype yang sama — perbandingan
==mengembalikanTrue. Bentuk tuple ringkas; bentuk dict lebih panjang tetapi bisa mengaturoffsetdanitemsizemanual. - Baris 7 (
dt_tuple.fields): dict ini memetakan nama field ke pasangan(tipe, offset_byte). Fieldkodemenempati offset 0,stokoffset 16, danhargaoffset 20. - Baris 8 (
itemsize):4 karakter x 4 byte + 4 + 8 = 28byte per record, disusun rapat tanpa padding.
Field Bersarang #
Nilai sebuah field boleh berupa dtype lain yang juga memiliki field — struktur menyerupai record di dalam record:
import numpy as np
dt = np.dtype([("produk", "U8"), ("dimensi", [("panjang", "f4"), ("lebar", "f4")])])
kardus = np.array([("Buku", (21.0, 14.5)), ("Laptop", (33.5, 24.0))], dtype=dt)
print(kardus)
print(kardus["dimensi"]["panjang"])
print(kardus["dimensi"].shape)Output yang Dihasilkan:
[('Buku', (21. , 14.5)) ('Laptop', (33.5, 24. ))]
[21. 33.5]
(2,)Penjelasan singkat:
- Baris 3: field
dimensibertipe dtype bersarang dengan dua subfieldf4; dalam satu record, nilai ini menempati dua float 32-bit berurutan. - Baris 5 (
print(kardus)): nilai bersarang dicetak sebagai tuple di dalam tuple record. - Baris 6-7: akses berantai
kardus["dimensi"]["panjang"]mengekstrak satu subfield dari seluruh record, menghasilkan arrayfloat32satu dimensi.
Membaca Buffer Byte dengan frombuffer #
Custom dtype paling terasa gunanya saat menerima data biner dari luar — hasil baca file, soket jaringan, atau perpustakaan C. Fungsi np.frombuffer menafsirkan buffer byte sesuai dtype tanpa menyalin data. Untuk keperluan uji, buffer “dari luar” itu kita tiru dengan tobytes():
import numpy as np
dt = np.dtype([("id", "i4"), ("suhu", "f4")])
sumber = np.array([(1, 27.5), (2, 28.0), (3, 29.5)], dtype=dt)
buf = sumber.tobytes()
print(len(buf))
hasil = np.frombuffer(buf, dtype=dt)
print(hasil)
print(hasil["suhu"].mean())Output yang Dihasilkan:
24
[(1, 27.5) (2, 28. ) (3, 29.5)]
28.333334Bedah baris demi baris:
- Baris 4 (
sumber.tobytes()): tiga record dikonversi menjadi 24 byte mentah —3 x (4 + 4)— persis seperti buffer yang akan dikirim perangkat atau ditulis file biner. - Baris 6 (
np.frombuffer(buf, dtype=dt)): interpreter membagi buffer per 8 byte dan menafsirkan 4 byte pertama sebagaii4serta 4 byte berikutnya sebagaif4, tanpa menyalin memori. - Baris 8: hasilnya array structured biasa, sehingga agregasi per field langsung berfungsi;
mean()menghasilkan28.333334karenasuhubertipef4.
fromfunction dan ndenumerate #
Dua perangkat terakhir melengkapi pembuatan array. np.fromfunction mengisi array dengan memanggil fungsi atas koordinat indeks, sedangkan np.ndenumerate mengiterasi pasangan (indeks, nilai) pada array apa pun:
import numpy as np
tabel = np.fromfunction(lambda i, j: (i + 1) * (j + 1), (2, 3), dtype=int)
print(tabel)
for idx, nilai in np.ndenumerate(tabel):
print(idx, nilai)Output yang Dihasilkan:
[[1 2 3]
[2 4 6]]
(0, 0) 1
(0, 1) 2
(0, 2) 3
(1, 0) 2
(1, 1) 4
(1, 2) 6Penjelasan:
- Baris 3 (
np.fromfunction(...)): fungsi dipanggil untuk setiap pasangan koordinat(i, j)— hasilnya tabel perkalian kecil1..2 x 1..3. Argumendtype=intmengatur tipe koordinat yang diteruskan ke fungsi. - Baris 6-7 (
np.ndenumerate): setiap langkah menghasilkan tuple indeks seperti(1, 2)beserta nilai elemennya, terurut sesuai layout memori. - Pola
for idx, nilai in np.ndenumerate(a)menggantikan kombinasi dua looprangeyang rentan salah indeks — versi lanjutannya dengannp.nditerdibahas pada Chapter Array Iteration.
Dengan perangkat di atas, array tidak lagi terbatas pada bentuk “angka berderet”: skema datanya bisa dirancang mengikuti domain masalah, sekaligus tetap bisa dibaca dari dan ke buffer byte mentah.