Dalam pekerjaan data sehari-hari, kita jarang mendapat array yang isinya rapi semua. Sensor bisa gagal membaca, sel di file CSV bisa kosong, dan operasi tertentu bisa menghasilkan bilangan yang tidak terdefinisi. NumPy mewakili dua keadaan istimewa itu dengan konstanta np.nan (Not a Number) untuk nilai yang tidak terdefinisi, serta np.inf untuk nilai yang melampaui jangkauan float, seperti hasil pembagian dengan nol.
Bab ini membahas dari mana kedua konstanta itu berasal, bagaimana interpreter menanganinya, dan satu perilaku yang paling sering mengejutkan pemula: satu nan saja bisa membuat seluruh hasil agregasi ikut menjadi nan. Setelah pola ini terlihat, bab-bab berikutnya — dari pemeriksaan hingga filtering — akan terasa sebagai jawaban langsung atas masalah ini.
Konstanta nan dan inf #
np.nan, np.inf, dan negasinya -np.inf dapat ditulis langsung sebagai elemen array. Keduanya adalah nilai float biasa, sehingga array penampungnya harus bertipe float:
import numpy as np
pembacaan = np.array([1.0, np.nan, np.inf, -np.inf, 27.5])
print(pembacaan)
print(type(np.nan), type(np.inf))Output yang Dihasilkan:
[ 1. nan inf -inf 27.5]
<class 'float'> <class 'float'>Mari kita bedah:
- Baris 3 (
np.array(...)): interpreter menyusun arrayfloat64berisi lima elemen;np.nan,np.inf, dan-np.infdisimpan sebagai pola bit float khusus, bukan string. - Baris 4 (
print(pembacaan)):printmenampilkan konstanta itu secara ringkas sebagainan,inf, dan-infdi antara elemen biasa. - Baris 5 (
type(...)):np.nandannp.infternyatafloatPython biasa — keduanya juga bisa dipakai tanpa NumPy, misalnya lewatfloat("nan").
Asal-usul: Pembagian dengan Nol #
Nilai nan dan inf tidak selalu ditulis manual; sering kali keduanya lahir dari perhitungan. Di sinilah NumPy berbeda dari Python biasa: Python memunculkan error untuk pembagian nol, sedangkan NumPy mengembalikan nilai khusus dan hanya memberi peringatan.
import numpy as np
try:
hasil = 1 / 0
except ZeroDivisionError as e:
print("Python biasa -> ZeroDivisionError:", e)
pembilang = np.array([1.0, 0.0])
penyebut = np.array([0.0, 0.0])
with np.errstate(divide="ignore", invalid="ignore"):
hasil = pembilang / penyebut
print("NumPy ->", hasil)Output yang Dihasilkan:
Python biasa -> ZeroDivisionError: division by zero
NumPy -> [inf nan]Penjelasan tiap bagian:
- Baris 3-6: pembagian bilangan Python
1 / 0memunculkanZeroDivisionError, karena itu blok ini dibungkustry/exceptagar script tetap berjalan. - Baris 8-9: dua array
float64disiapkan; pembagian dievaluasi elemen demi elemen, sehingga1.0 / 0.0dan0.0 / 0.0menghasilkan nilai yang berbeda. - Baris 10 (
np.errstate(...)): konteks ini meredam peringatan floating-point di dalam blokwithsaja —divide="ignore"untuk pembagian nol,invalid="ignore"untuk hasil tak terdefinisi. - Baris 11:
1.0 / 0.0menghasilkaninf, sedangkan0.0 / 0.0menghasilkannan; keduanya dikembalikan tanpa error.
| Operasi | Hasil | Alasan |
|---|---|---|
1.0 / 0.0 | inf | hasil menuju tak hingga positif |
-1.0 / 0.0 | -inf | hasil menuju tak hingga negatif |
0.0 / 0.0 | nan | tidak terdefinisi secara matematis |
np.inf - np.inf | nan | tak hingga dikurangi tak hingga tidak punya jawaban |
1.0 / np.inf | 0.0 | hasil menuju nol |
Peringatan yang Muncul di Balik Layar #
Tanpa np.errstate, operasi di atas tetap berhasil tetapi mencetak RuntimeWarning ke stderr. Kita dapat membuktikan isi peringatan itu dengan modul warnings, sehingga pesan tersebut menjadi output yang teruji:
import numpy as np
import warnings
pembilang = np.array([1.0, 0.0])
penyebut = np.array([0.0, 0.0])
with warnings.catch_warnings(record=True) as terekam:
warnings.simplefilter("always")
hasil = pembilang / penyebut
for w in terekam:
print(w.category.__name__, "->", w.message)
print(hasil)Output yang Dihasilkan:
RuntimeWarning -> divide by zero encountered in divide
RuntimeWarning -> invalid value encountered in divide
[inf nan]Mari kita telaah:
- Baris 6-8:
warnings.catch_warnings(record=True)mencegat peringatan ke dalam listtererekam, sehingga pesan peringatan bisa dicetak sebagai output yang pasti. - Baris 9-10: dua peringatan tercatat — pembagian nol untuk elemen
1.0 / 0.0, dan invalid untuk0.0 / 0.0. - Baris 11: hasilnya tetap
[inf nan]; peringatan tidak pernah mengubah nilai, hanya memberi tanda agar kita memeriksanya.
nan Tidak Sama dengan Apa Pun #
Perilaku paling penting dari nan: perbandingannya dengan apa pun — termasuk dengan nan itu sendiri — selalu False. Konsekuensinya, uji kesamaan biasa tidak bisa dipakai untuk mendeteksi nan, dan satu nan menular ke seluruh hasil agregasi:
import numpy as np
a = np.array([1.0, np.nan, 3.0])
print("nan == nan ->", np.nan == np.nan)
print("nan != nan ->", np.nan != np.nan)
print("a == nan ->", a == np.nan)
print("a.sum() ->", a.sum())
print("a.mean() ->", a.mean())Output yang Dihasilkan:
nan == nan -> False
nan != nan -> True
a == nan -> [False False False]
a.sum() -> nan
a.mean() -> nanMari kita baca hasilnya:
- Baris 4-5:
nan == nanbernilaiFalsedannan != nanbernilaiTrue— kebalikan dari intuisi kita tentang bilangan biasa. - Baris 6 (
a == np.nan): perbandingan per elemen menghasilkanFalsesemua, sehingga polaa[a == nan]tidak akan pernah menemukan satu pun elemen. - Baris 7-8:
sumdanmeanikut menjadinantanpa pesan error — satu elemen hilang cukup untuk meracuni seluruh statistik.
Perilaku inf dalam Aritmetika #
Berbeda dari nan, inf masih mengikuti sebagian aturan aritmetika. Namun kombinasi tertentu justru menghasilkan nan karena jawabannya tidak terdefinisi:
import numpy as np
x = np.array([np.inf])
with np.errstate(invalid="ignore"):
print("inf - inf :", (x - x)[0])
print("inf * 0 :", (x * 0.0)[0])
print("inf + 1000:", np.inf + 1000.0)
print("1 / inf :", 1.0 / np.inf)Output yang Dihasilkan:
inf - inf : nan
inf * 0 : nan
inf + 1000: inf
1 / inf : 0.0Penjelasan singkat:
- Baris 3 (
x = np.array([np.inf])): array berisi satu elemeninf; pengurangan dan perkaliannya dieksekusi sebagai operasi array. - Baris 4-6:
inf - infdaninf * 0menghasilkannan; kedua operasi memicu peringatan invalid, maka blok ini dibungkusnp.errstate(invalid="ignore")agar output tetap bersih. - Baris 7-8:
inf + 1000tetapinf, sedangkan1 / infmenuju0.0— dua hasil yang masuk akal secara matematis.
Pola praktisnya: inf menandai hasil yang “terlalu besar”, nan menandai hasil yang tidak bisa ditentukan. Keduanya sama-sama harus dideteksi sebelum data dipakai menghitung, dan itulah topik bab berikutnya.