Data teks dari dunia nyata jarang serapi contoh buku: ada baris keterangan di bagian atas log, ada sel yang kosong karena sensor gagal membaca, dan ada angka desimal yang ditulis dengan koma mengikuti kebiasaan Indonesia (27,5). np.loadtxt berhenti bekerja pada ketidakrapian semacam ini, karena fungsinya mengasumsikan setiap baris berisi angka lengkap.

Untuk berkas dengan struktur tak rapi, NumPy menyediakan np.genfromtxt — pembaca teks yang lebih lentur: bisa memberi nama kolom, mengganti nilai kosong, melewati baris keterangan, dan mengubah format angka lewat converter. Bab ini membahas parameter-parameternya satu per satu.

names=True dan Structured Array #

Parameter names=True memberi tahu genfromtxt bahwa baris pertama berisi nama kolom, bukan data. Hasilnya bukan array biasa, melainkan structured array yang tiap barisnya menyimpan beberapa kolom dengan nama:

python
import numpy as np
from io import StringIO

teks = """hari,beras,gula
1,15000,18000
2,15200,17900
3,15300,18100"""
data = np.genfromtxt(StringIO(teks), delimiter=",", names=True)
print(data.dtype.names)
print(data["beras"])
print(data["hari"].mean())

Output yang Dihasilkan:

text
('hari', 'beras', 'gula')
[15000. 15200. 15300.]
2.0

Mari kita bedah:

  1. Baris 3-7 (teks = """..."""): string CSV dibungkus StringIO agar bisa dibaca seperti berkas tanpa benar-benar menulis ke disk — pola yang nyaman untuk contoh dan pengujian.
  2. Baris 8 (names=True): baris pertama dijadikan nama kolom; data.dtype.names mengembalikan tuple ('hari', 'beras', 'gula').
  3. Baris 9-10: kolom diakses lewat namanya — data["beras"] — bukan lewat indeks angka, sehingga kode lebih mudah dibaca.
  4. Baris 11: karena semua nilai dibaca sebagai float64 (dtype bawaan genfromtxt), agregasi seperti .mean() langsung bisa dijalankan.

Menangani Nilai Kosong #

Sel kosong pada CSV adalah kasus paling umum di data lapangan. Secara bawaan, genfromtxt mengisi sel kosong dengan nan untuk kolom numerik — perilaku yang sehat karena kekosongan memang tidak boleh diam-diam dianggap nol. Bila kosong memang berarti nol, kita bisa menentukannya lewat filling_values:

python
import numpy as np
from io import StringIO

teks = """hari,gula
1,18000
2,
3,18100"""
data = np.genfromtxt(StringIO(teks), delimiter=",", names=True)
print(data["gula"])
data2 = np.genfromtxt(StringIO(teks), delimiter=",", names=True,
                      filling_values=0)
print(data2["gula"])

Output yang Dihasilkan:

text
[18000.    nan 18100.]
[18000.     0. 18100.]

Penjelasan tiap bagian:

  1. Baris 4: baris kedua data sengaja dibiarkan tanpa nilai setelah koma — sel gula kosong.
  2. Baris 7: pembacaan pertama menghasilkan nan pada posisi yang kosong; satu nan cukup untuk membuat agregasi ikut nan, jadi sengaja tidak disembunyikan.
  3. Baris 8-9 (filling_values=0): pembacaan kedua mengganti setiap sel kosong dengan 0; parameter ini juga bisa berupa dict per kolom, mis. filling_values={"gula": 0}.

Melewati Baris Keterangan #

Log hasil instrumentasi sering diawali beberapa baris keterangan sebelum tabelnya dimulai. Parameter skip_header melewati sejumlah baris pertama; parameter pasangannya skip_footer memotong baris dari akhir:

python
import numpy as np
from io import StringIO

teks = """# log sensor stasiun 3
# Jakarta, pagi
hari,suhu
1,27.5
2,28.1"""
data = np.genfromtxt(StringIO(teks), delimiter=",", names=True,
                     skip_header=2)
print(data)

Output yang Dihasilkan:

text
[(1., 27.5) (2., 28.1)]

Mari kita telaah:

  1. Baris 4-5: dua baris pertama adalah keterangan yang diawali # — di sinilah skip_header=2 diperlukan, karena genfromtxt tidak menganggap baris itu komentar secara bawaan.
  2. Baris 6-8: setelah dua baris keterangan dilewati, baris hari,suhu menjadi nama kolom dan dua baris berikutnya menjadi data.
  3. Baris keluaran: hasilnya structured array dua baris; setiap baris dicetak sebagai tuple bernama kolom.

converters untuk Format Angka Lokal #

Angka Indonesia lazim menulis desimal dengan koma: 27,5. Pembaca bawaan tidak mengenal format itu, tetapi parameter converters memungkinkan kita menyuntikkan fungsi pengubah per kolom:

python
import numpy as np
from io import StringIO

teks = """suhu
27,5
28,1
26,9"""
data = np.genfromtxt(StringIO(teks), skip_header=1,
                     converters={0: lambda s: float(s.replace(",", "."))})
print(data)
print(data.dtype)

Output yang Dihasilkan:

text
[27.5 28.1 26.9]
float64

Penjelasan tiap bagian:

  1. Baris 6 (converters={0: ...}): dict ini memetakan indeks kolom ke fungsi pengubah; kolom 0 diolah oleh lambda yang mengganti koma dengan titik sebelum float() mengubahnya menjadi bilangan.
  2. Baris 6 (catatan penting): fungsi converter menerima setiap sel dalam bentuk str, sehingga s.replace(",", ".") bekerja langsung pada teks.
  3. Baris 8-9: hasilnya array float64 biasa — setelah konversi, data siap dihitung tanpa jejak format lokal.

Beberapa parameter genfromtxt lain yang sering dipakai:

  • usecols=(0, 2) — membaca sebagian kolom saja, sama seperti pada loadtxt.
  • autostrip=True — membuang spasi berlebih di sekeliling tiap sel.
  • max_rows=n — membatasi jumlah baris data yang dibaca, berguna untuk mengintip berkas besar.