Data pegawai di sebuah perusahaan hampir selalu berbentuk tabel: kolom nama berisi teks, kolom umur berisi bilangan bulat, dan kolom gaji berisi bilangan pecahan. Array NumPy biasa tidak dapat menampung kombinasi itu dalam satu array, karena seluruh elemen array wajib bertipe sama — aturan yang dibahas pada Chapter Atribut dtype. Selama ini jalan keluarnya adalah membuat tiga array terpisah dan mengurus keselarasan indeksnya secara manual.
Structured array menyelesaikan masalah tersebut: satu array, tetapi setiap elemennya adalah record yang terdiri atas beberapa field dengan tipe masing-masing. Karena satu record utuh tersimpan berdampingan di memori, structured array sangat cocok untuk data yang dibaca atau ditulis baris demi baris — misalnya record dari file biner atau database — sebelum data naik level ke pandas.
Membuat Structured Array dengan dtype Field #
Kunci utamanya adalah structured dtype: definisi skema yang disusun dari list berisi pasangan (nama_field, spesifikasi_tipe). Spesifikasi tipe memakai kode string yang sama dengan dtype biasa — U10 untuk string Unicode maksimal 10 karakter, i4 untuk integer 32-bit, dan f8 untuk float 64-bit:
import numpy as np
dtype = np.dtype([("nama", "U10"), ("umur", "i4"), ("gaji", "f8")])
pegawai = np.array(
[("Budi", 34, 8500000.0), ("Siti", 29, 9200000.0), ("Dewi", 41, 7800000.0)],
dtype=dtype,
)
print(pegawai)
print(pegawai.dtype)
print(pegawai.dtype.itemsize)Output yang Dihasilkan:
[('Budi', 34, 8500000.) ('Siti', 29, 9200000.) ('Dewi', 41, 7800000.)]
[('nama', '<U10'), ('umur', '<i4'), ('gaji', '<f8')]
52Mari kita bedah:
- Baris 3 (
np.dtype(...)): interpreter menerjemahkan list of tuples menjadi objekdtypedengan tiga field bernama. Setiap tuple menetapkan nama field dan tipe datanya. - Baris 4-7 (
np.array(...)): list of tuples dikonversi menjadi array satu dimensi berisi tiga record. NumPy mencocokkan posisi:"Budi"masuk fieldnama,34masuk fieldumur, dan seterusnya. - Baris 9 (
print(pegawai.dtype)): dtype tercetak sebagai list field; tanda<menandai urutan byte little-endian. - Baris 10 (
itemsize): satu record menempati10 x 4 + 4 + 8 = 52byte — stringU10memakai 4 byte per karakter.
Mengakses Field dan Record #
Field diakses dengan kurung siku dan nama field, mirip mengambil kolom tabel. Hasilnya adalah array biasa yang siap dihitung — bisa diagregasi, difilter, dan dijadikan dasar boolean indexing:
import numpy as np
dtype = np.dtype([("nama", "U10"), ("umur", "i4"), ("gaji", "f8")])
pegawai = np.array(
[("Budi", 34, 8500000.0), ("Siti", 29, 9200000.0), ("Dewi", 41, 7800000.0)],
dtype=dtype,
)
print(pegawai["nama"])
print(pegawai["gaji"].mean())
print(pegawai["umur"] > 30)
print(pegawai[pegawai["umur"] > 30])
print(pegawai[1])Output yang Dihasilkan:
['Budi' 'Siti' 'Dewi']
8500000.0
[ True False True]
[('Budi', 34, 8500000.) ('Dewi', 41, 7800000.)]
('Siti', 29, 9200000.0)Penjelasan tiap baris penting:
- Baris 8 (
pegawai["nama"]): hasilnya array string biasa; akses field tidak menyalin data, hanya membuat view dengan stride tertentu — perilaku yang dibahas mendalam pada Chapter View vs Copy. - Baris 9: karena
pegawai["gaji"]adalah arrayfloat64biasa, methodmean()langsung dapat dipakai. - Baris 10-11: perbandingan field menghasilkan mask boolean, dan mask tersebut memfilter record utuh — bukan hanya satu field.
- Baris 12 (
pegawai[1]): satu record dikembalikan sebagai objeknumpy.void, lalu field per record dapat diakses denganpegawai[1]["nama"].
Menyiapkan Array Kosong lalu Mengisinya #
Untuk data yang datang bertahap, kita bisa membuat wadah kosong dengan np.zeros dan structured dtype, lalu mengisi setiap field dengan operasi vektor. Nilai awalnya deterministik: string kosong, 0, dan 0.0:
import numpy as np
dtype = np.dtype([("nama", "U10"), ("umur", "i4"), ("gaji", "f8")])
staf = np.zeros(3, dtype=dtype)
print(staf)
staf["nama"] = ["Andi", "Rina", "Tono"]
staf["umur"] = [25, 31, 28]
staf["gaji"] = [6000000.0, 7500000.0, 6900000.0]
print(staf)
print(staf["umur"].sum())Output yang Dihasilkan:
[('', 0, 0.) ('', 0, 0.) ('', 0, 0.)]
[('Andi', 25, 6000000.) ('Rina', 31, 7500000.) ('Tono', 28, 6900000.)]
84Bedah singkat:
- Baris 4 (
np.zeros(3, dtype=dtype)): interpreter mengalokasikan tiga record dan mengisi seluruh byte dengan nol, sehingga tiap field bernilai nol sesuai tipenya. - Baris 6-8: penugasan per field menerima list biasa; NumPy melakukan konversi tipe secara otomatis ke spesifikasi field.
- Baris 10: field
umurkembali sebagai arrayint32biasa, sehinggasum()menghasilkan84.
Mengurutkan Record Berdasarkan Field #
Method sort dan fungsi np.sort menerima argumen order berupa nama field. Seluruh record ikut berpindah bersama — urutan field di dalam satu record tidak pernah tercampur:
import numpy as np
dtype = np.dtype([("nama", "U10"), ("umur", "i4"), ("gaji", "f8")])
pegawai = np.array(
[("Budi", 34, 8500000.0), ("Siti", 29, 9200000.0), ("Dewi", 41, 7800000.0)],
dtype=dtype,
)
urut = np.sort(pegawai, order="gaji")
print(urut)
pegawai.sort(order="umur")
print(pegawai)Output yang Dihasilkan:
[('Dewi', 41, 7800000.) ('Budi', 34, 8500000.) ('Siti', 29, 9200000.)]
[('Siti', 29, 9200000.) ('Budi', 34, 8500000.) ('Dewi', 41, 7800000.)]Dua hal patut dicatat: np.sort mengembalikan array baru, sedangkan method pegawai.sort(...) mengurutkan array di tempat (in place). Untuk beberapa kriteria sekaligus, order menerima list, misalnya order=["umur", "gaji"].
Kapan Structured Array Layak Dipakai #
Structured array bukan pengganti pandas, melainkan lapisan penyimpanan yang lebih dekat ke memori. Perbandingan singkat:
| Pendekatan | Kekuatan | Batasan |
|---|---|---|
| Structured array | record utuh rapat di memori; baca/tulis baris cepat; tanpa dependensi | operasi per kolom bergerak melompati memori (stride) |
| Beberapa array terpisah | operasi kolom paling cepat (contiguous) | sinkronisasi indeks antar-array menjadi tanggung jawab kita |
| pandas DataFrame | kaya fitur analisis tabel | dependensi tambahan; overhead memori lebih besar |
Bab berikutnya, Record Array, menyempurnakan pola akses ini: field dapat dibaca lewat atribut seperti pegawai.nama tanpa tanda kurung siku.