Selain format .npy yang membawa metadata, NumPy memungkinkan kita menulis isi array sebagai byte mentah lewat ndarray.tofile() dan membacanya kembali dengan np.fromfile(). Format semacam ini berguna ketika lawan bicara adalah sistem lain yang sudah sepakat memakai tata letak byte tertentu — misalnya data hasil instrumen, berkas dari perangkat lunak C atau Fortran, atau protokol tertulis.

Harga yang harus dibayar: tanpa metadata, tidak ada yang mengingatkan dtype, bentuk, maupun urutan byte-nya. Bab ini mendemonstrasikan ketiga jebakan itu, lalu menutup dengan perbandingan ukuran biner versus teks sebagai bahan keputusan format.

tofile dan fromfile: Byte Mentah Tanpa Metadata #

tofile() menulis byte array apa adanya; fromfile() membaca byte tersebut menurut dtype yang kita sebutkan. Bentuk array tidak ikut tersimpan — hasil pembacaan selalu 1-D:

python
import numpy as np

m = np.arange(6, dtype=np.int32).reshape(2, 3)
m.tofile("matriks.bin")
c = np.fromfile("matriks.bin", dtype=np.int32)
print(c, c.shape)
print(c.reshape(2, 3))

Output yang Dihasilkan:

text
[0 1 2 3 4 5] (6,)
[[0 1 2]
 [3 4 5]]

Mari kita bedah:

  1. Baris 3-4 (tofile("matriks.bin")): dua belas byte ditulis — persis 6 elemen × 4 byte per int32 — tanpa header apa pun.
  2. Baris 5 (np.fromfile(..., dtype=np.int32)): pembacaan mengembalikan array 1-D berbentuk (6,); informasi reshape(2, 3) milik array asal tidak tercatat di berkas.
  3. Baris 6-7: bentuk asli dapat dipulihkan hanya karena kita, pembacanya, masih ingat bentuknya — di sinilah letak tanggung jawab yang berpindah ke kita.

Dtype yang Salah, Isi yang Melenceng #

Karena tidak ada metadata, fromfile menataskan byte sesuai dtype yang diberikan, tanpa bisa memeriksa kebenarannya. Berkas int32 yang dibaca sebagai int64 akan dipasangkan dua elemen lama menjadi satu elemen baru:

python
import numpy as np

b = np.array([1, 2, 3], dtype=np.int32)
b.tofile("angka.bin")
print(np.fromfile("angka.bin", dtype=np.int64))

Output yang Dihasilkan:

text
[8589934593]

Penjelasan tiap bagian:

  1. Baris 3-4: tiga elemen int32 (12 byte) disimpan.
  2. Baris 5 (fromfile(..., dtype=np.int64)): pembaca menuntut 8 byte per elemen; dari 12 byte yang tersedia hanya satu elemen penuh yang terbentuk.
  3. Baris keluaran: nilai 8589934593 adalah gabungan byte 1 dan 2 dalam satu bilangan 64-bit — data terlihat sah, padahal isinya sudah lenceng. Kesalahan semacam ini tidak memunculkan error apa pun, sehingga paling berbahaya dikenali.

Endian: Urutan Byte Antar Platform #

Urutan byte (endianness) juga tidak dijaga oleh tofile. Array dengan dtype big-endian >i4 yang dibaca seolah little-endian <i4 menghasilkan nilai yang dirotasi byte-nya:

python
import numpy as np

a = np.arange(3, dtype=">i4")
a.tofile("besar.bin")
print(np.fromfile("besar.bin", dtype=">i4"))
print(np.fromfile("besar.bin", dtype="<i4"))

Output yang Dihasilkan:

text
[0 1 2]
[       0 16777216 33554432]

Mari kita telaah:

  1. Baris 3 (dtype=">i4"): tanda > berarti big-endian — byte terpenting disimpan lebih dulu, kebiasaan umum pada arsitektur dan berkas instrumen lama.
  2. Baris 5: pembacaan dengan dtype yang cocok mengembalikan data persis aslinya.
  3. Baris 6: pembacaan dengan <i4 menghasilkan 16777216 untuk angka 1 — nilainya 256 × 256 × 256, efek pemutaran urutan byte. Dtypes yang mencantumkan endian eksplisit adalah cara komunikasi yang benar dengan data lintas platform.
Sumber dataYang hilang tanpa metadataAkibat bila diabaikan
bentuk arraytidak tersimpanhasil selalu 1-D, perlu reshape manual
dtypetidak tersimpandata terbaca melenceng tanpa error
endiannesstidak tersimpannilai tertukar byte antar platform

Perbandingan Ukuran Biner versus Teks #

Kapan memilih byte mentah atau .npy, dan kapan memilih teks? Ukuran berkas adalah salah satu faktornya. Untuk seribu nilai float64, kita bandingkan tiga format sekaligus:

python
import numpy as np
import os

data = np.arange(1000, dtype=np.float64)
data.tofile("data.bin")
np.savetxt("data.csv", data, fmt="%.2f", delimiter=",")
np.save("data.npy", data)
print("bin :", os.path.getsize("data.bin"), "byte")
print("csv :", os.path.getsize("data.csv"), "byte")
print("npy :", os.path.getsize("data.npy"), "byte")

Output yang Dihasilkan:

text
bin : 8000 byte
csv : 6890 byte
npy : 8128 byte

Penjelasan tiap bagian:

  1. Baris 4-6: array seribu float64 disimpan dalam tiga format — byte mentah, teks dua desimal, dan .npy.
  2. Baris 7-9: byte mentah tepat 8000 byte (1000 × 8); teksnya 6.890 byte karena angka kecil lebih pendek dari representasi binernya; .npy hanya menambah 128 byte header di atas data biner.
  3. Kesimpulan ukuran: untuk data float, teks dan biner tidak selisih jauh pada angka ramping; teks membengkak justru pada angka dengan banyak desimal, sedangkan biner berukuran tetap terlepas dari nilainya.