Data yang dikumpulkan dari dunia nyata hampir selalu tidak lengkap, misalnya sensor yang gagal membaca atau responden yang tidak mengisi satu pertanyaan, dan NumPy menyediakan penanda khusus beserta keluarga fungsi untuk mengolahnya. Topik ini membahas np.nan sebagai penanda data hilang, dampaknya pada agregasi biasa, keluarga fungsi nan-aware seperti np.nanmean dan np.nansum, cara menghitung banyaknya data hilang, serta dua strategi pengisian nilai yang hilang tersebut.
NaN sebagai penanda data hilang#
NaN, singkatan dari Not a Number, adalah nilai khusus pada tipe pecahan yang dipakai sebagai penanda data hilang. Konstantanya tersedia sebagai np.nan, dan array yang memuatnya otomatis bertipe pecahan, karena bilangan bulat NumPy tidak memiliki representasi NaN sama sekali. Pemilihan np.nan sebagai penanda bukan sekadar konvensi, melainkan mekanisme bawaan standar IEEE 754 yang diadopsi perangkat lunak data secara luas, sehingga nilai yang sama dikenali oleh pandas, matplotlib, dan sebagian besar pustaka sains data.
Data hilang muncul dari berbagai arah, yaitu baris CSV yang tidak lengkap saat diimpor, sensor yang gagal membaca pada jam tertentu, atau gabungan dua tabel yang tidak sepenuhnya cocok. Menandainya dengan NaN jauh lebih aman daripada mengarang nilai pengganti seperti nol, karena NaN menyatakan ketiadaan informasi, bukan pengukuran sebesar nol. Oleh sebab itu, langkah pertama penanganannya adalah mendeteksi posisinya, bukan langsung menghapus atau mengisinya.
import numpy as np
suhu = np.array([28.5, np.nan, 30.2, 27.9, np.nan, 31.0])
print(suhu.dtype)
print(np.isnan(suhu))
print(np.isnan(suhu).sum())float64
[False True False False True False]
2Fungsi np.isnan menguji setiap elemen dan menghasilkan array boolean, sedangkan penjumlahan boolean pada array itu menghitung banyaknya data hilang, yaitu 2 dari 6 pembacaan. Pola np.isnan(data).sum() adalah cara baku menghitung kelengkapan data sebelum analisis dilanjutkan.
Dampak NaN pada agregasi biasa#
Fungsi agregasi biasa seperti sum, mean, dan max menyebarkan NaN ke hasilnya. Perilaku ini disengaja, karena hasil dari sebagian data yang hilang sebaiknya tidak diam-diam dilaporkan sebagai hasil penuh. Konsekuensinya, satu nilai hilang di tengah ribuan pembacaan sudah cukup membuat seluruh ringkasan menjadi tidak berguna, dan kondisi itu tidak memunculkan error sehingga mudah lolos dari pemeriksaan.
import numpy as np
suhu = np.array([28.5, np.nan, 30.2, 27.9, np.nan, 31.0])
print(suhu.sum())
print(suhu.mean())
print(suhu.max())nan
nan
nanKetiga agregasi menghasilkan nan meskipun empat dari enam nilainya lengkap. Pada dataset nyata, keluaran semacam ini adalah tanda bahwa data perlu diolah dengan fungsi yang mengabaikan NaN atau diisi terlebih dahulu.
Desain ini berbeda dari fungsi nan-aware, yaitu NumPy tidak diam-diam melompati NaN pada fungsi biasa, melainkan menyebarkannya agar kelengkapan data terbaca dari hasilnya. Dengan demikian, keluaran nan dapat dibaca sebagai sinyal untuk memeriksa data, bukan sebagai error yang menghentikan program. Perilaku ini berlaku seragam pada keluarga agregasi lainnya, sehingga pengalaman dengan mean langsung berlaku pada std dan var.
Keluarga fungsi nan-aware#
Keluarga fungsi nan-aware menjalankan agregasi yang sama sambil mengabaikan setiap posisi NaN. Anggotanya mengikuti nama fungsi aslinya, yaitu np.nanmean untuk rata-rata, np.nansum untuk penjumlahan, np.nanstd untuk simpangan baku, dan np.nanmax untuk nilai maksimal. Fungsinya juga menerima parameter axis, sehingga ringkasan per baris atau per kolom pada data dua dimensi tetap dapat dilakukan meskipun sebagian selnya kosong.
import numpy as np
suhu = np.array([28.5, np.nan, 30.2, 27.9, np.nan, 31.0])
print(np.nanmean(suhu))
print(np.nansum(suhu))
print(np.nanstd(suhu))
print(np.nanmax(suhu))29.4
117.6
1.2509996003196806
31.0np.nanmean menghitung 117.6 dibagi 4, bukan dibagi 6, karena hanya empat nilai yang ikut dihitung. Pembagi yang menyesuaikan banyaknya nilai sah inilah pembedanya dengan strategi pengisian. Pada strategi pengisian dengan nol, pembagi tetap mencakup seluruh elemen, sehingga rata-rata tertarik ke bawah oleh nilai hilang yang diperlakukan sebagai nol.
import numpy as np
pembacaan = np.array([
[24.1, 25.0, np.nan],
[23.8, np.nan, 26.4],
[np.nan, 24.9, 25.7],
[24.6, 25.2, np.nan],
])
print(np.nanmean(pembacaan, axis=0))
print(np.isnan(pembacaan).sum(axis=0))[24.16666667 25.03333333 26.05 ]
[1 1 2]Agregasi dengan axis=0 merangkum per kolom, yaitu per posisi sensor pada contoh ini, dan setiap kolom masih memiliki nilai sah sehingga hasilnya dapat dihitung. Keluaran kedua melaporkan banyaknya data hilang per kolom, yaitu 1, 1, dan 2, sehingga keandalan setiap kolom dapat dibandingkan. Sebagai catatan perilaku batas, np.nansum pada data yang seluruhnya NaN menghasilkan 0.0, sedangkan np.nanmean tidak memiliki jawaban yang bermakna dan menampilkan peringatan, sebagaimana ditunjukkan pada bagian kesalahan umum.
Mengisi NaN dengan nilai tertentu#
Selain diabaikan, NaN dapat diganti dengan nilai tertentu. Fungsi np.nan_to_num mengganti setiap NaN menjadi 0.0 secara bawaan, dan sekaligus menangani nilai inf bila ada. Cara ini praktis untuk data yang akan dijumlahkan, tetapi berbahaya untuk rata-rata karena mengganti data hilang dengan nol memang menarik rata-rata ke bawah. Alternatif yang lebih hati-hati adalah mengganti NaN dengan rata-rata nilai sahnya sendiri, sebuah teknik yang biasa disebut imputasi (imputation).
import numpy as np
suhu = np.array([28.5, np.nan, 30.2, 27.9, np.nan, 31.0])
dgn_nol = np.nan_to_num(suhu)
print(dgn_nol)
print(dgn_nol.mean())[28.5 0. 30.2 27.9 0. 31. ]
19.599999999999998Dua posisi NaN menjadi 0.0, dan rata-ratanya jatuh dari 29.4 menjadi sekitar 19.6. Angka itu salah secara makna, karena suhu yang tidak terbaca bukan berarti suhu nol derajat. Imputasi dengan rata-rata nilai sah menghilangkan masalah tersebut.
import numpy as np
suhu = np.array([28.5, np.nan, 30.2, 27.9, np.nan, 31.0])
rata = np.nanmean(suhu)
isi = np.where(np.isnan(suhu), rata, suhu)
print(isi)
print(isi.mean())[28.5 29.4 30.2 27.9 29.4 31. ]
29.400000000000002np.where memilih nilai isian pada posisi NaN dan nilai asli pada posisi lain, sehingga hasilnya tetap memuat enam pembacaan tanpa menggeser rata-ratanya, yang tetap berada di sekitar 29.4. Bentuk lain yang setara adalah np.nan_to_num(suhu, nan=rata), yaitu fungsi yang sama dengan nilai isian yang ditentukan.
Kesalahan umum#
Menguji NaN dengan perbandingan sama dengan. Nilai NaN tidak sama dengan dirinya sendiri menurut standar IEEE 754, sehingga perbandingan apa pun yang melibatkannya menghasilkan False.
import numpy as np
suhu = np.array([28.5, np.nan, 30.2])
print(suhu == np.nan)
print(np.isnan(suhu))[False False False]
[False True False]Pembandingan suhu == np.nan gagal total karena tidak ada elemen yang dianggap sama, sedangkan np.isnan mengenali posisi NaN dengan benar. Aturan yang sama berlaku pada kondisi boolean seperti data[data == np.nan], yang selalu menghasilkan array kosong.
Memanggil fungsi nan-aware pada data yang seluruhnya NaN. Tidak ada nilai sah yang tersisa, sehingga np.nanmean menampilkan RuntimeWarning dengan pesan Mean of empty slice dan hasilnya nan.
import warnings
import numpy as np
data = np.array([np.nan, np.nan])
with warnings.catch_warnings(record=True) as tangkap:
warnings.simplefilter("always")
hasil = np.nanmean(data)
print(str(tangkap[0].message))
print(hasil)Mean of empty slice
nanPeringatan itu ditangkap melalui modul warnings agar pesannya dapat dibaca tanpa gangguan jalur keluaran lain, dan pola semacam ini berguna saat pengujian perilaku batas. Perbaikannya adalah memeriksa kelengkapan terlebih dahulu dengan np.isnan(data).all(), lalu menangani kasus kosong secara eksplisit sebelum agregasi dipanggil.
Ringkasan#
np.nanadalah penanda data hilang pada tipe pecahan, dannp.isnan(data).sum()adalah cara baku menghitung banyaknya data hilang.- Agregasi biasa menyebarkan NaN sehingga hasilnya menjadi
nan, sedangkan keluarga nan-aware sepertinp.nanmean,np.nansum,np.nanstd, dannp.nanmaxmengabaikannya beserta parameteraxis. np.nan_to_nummengganti NaN dengan 0.0 secara bawaan dan dapat menggeser rata-rata, sedangkan imputasi dengan rata-rata nilai sah melaluinp.wheremenjaganya tetap stabil.- Perbandingan
==tidak pernah mengenali NaN, dan fungsi nan-aware pada data yang seluruhnya NaN memunculkanRuntimeWarningbeserta hasilnan.
Referensi#
- numpy.nanmean — NumPy — Dokumentasi rata-rata yang mengabaikan NaN, termasuk parameter axis dan perilaku pada data kosong.
- numpy.nan_to_num — NumPy — Dokumentasi penggantian NaN dan nilai tak hingga dengan angka tertentu.
- numpy.isnan — NumPy — Dokumentasi fungsi pengujian elemen NaN pada array.
- Constants — NumPy
— Daftar konstanta khusus NumPy, termasuk
np.nanbeserta definisinya.