Pada pembagian np.array([1.0]) / np.array([0.0]), NumPy tidak melempar exception. Hasilnya inf plus satu RuntimeWarning, lalu eksekusi berlanjut seolah tidak terjadi apa-apa. Perilaku longgar ini nyaman saat eksplorasi data, tetapi kurang pas untuk perhitungan yang hasilnya dipakai mengambil keputusan — satu nan yang tidak terdeteksi bisa ikut tercetak di laporan akhir.

Bab ini membahas kendali di tingkat global: np.geterr() untuk membaca kebijakan error floating-point yang sedang berlaku, np.seterr() untuk mengubahnya dengan salah satu dari empat mode (ignore, warn, raise, call), dan pola aman penggunaannya. Kebijakan di lingkup lokal dengan np.errstate sudah diperkenalkan pada NaN dan Infinity; di sini fokusnya kebijakan yang berlaku lintas fungsi dan modul.

Perilaku Bawaan: Warning, Bukan Exception #

python
import numpy as np
import warnings

with warnings.catch_warnings(record=True) as terekam:
    warnings.simplefilter("always")
    pembilang = np.array([1.0, 0.0, 2.0])
    penyebut = np.array([0.0, 0.0, 0.0])
    hasil = pembilang / penyebut
print("hasil:", hasil)
for w in terekam:
    print(w.category.__name__, "->", w.message)

Output yang Dihasilkan:

text
hasil: [inf nan inf]
RuntimeWarning -> divide by zero encountered in divide
RuntimeWarning -> invalid value encountered in divide

Mari kita bedah:

  1. Baris 4-9: blok warnings.catch_warnings merekam peringatan agar bisa dicetak sebagai output teruji — tanpa itu, RuntimeWarning hanya muncul di stderr dan tidak terlihat lewat print.
  2. Baris 10 (hasil): 1.0/0.0 menghasilkan inf, 0.0/0.0 menghasilkan nan; program tidak berhenti di baris mana pun.
  3. Baris 11-12: dua RuntimeWarning tercatat — divide by zero untuk pembagian dengan nol, invalid value untuk operasi tak terdefinisi.

Intinya: mode bawaan NumPy adalah warn. Interpreter memberi tahu lewat peringatan, menghasilkan nilai khusus (inf/nan), lalu melanjutkan. Mengapa nilai-nilai itu menular ke hasil agregasi sudah dibahas pada NaN dan Infinity.

Membaca dan Mengubah Kebijakan: geterr dan seterr #

np.geterr() mengembalikan kamus kebijakan per jenis kesalahan: divide (pembagian nol), over (overflow), under (underflow), dan invalid (operasi tak terdefinisi). np.seterr() mengubah kebijakan itu dan — penting — mengembalikan kebijakan sebelumnya, sehingga perubahan selalu bisa dibatalkan.

ModePerilaku saat kesalahan terjadi
ignoreTanpa pesan; hasil inf/nan tetap dikembalikan
warn (bawaan)RuntimeWarning dicetak; hasil inf/nan tetap
raiseFloatingPointError dilempar; eksekusi berhenti di baris itu
callFungsi handler dipanggil; eksekusi berlanjut
python
import numpy as np

print(np.geterr())
saved = np.seterr(divide='ignore', invalid='ignore')
print(np.float64(1.0) / np.float64(0.0), np.float64(0.0) / np.float64(0.0))
np.seterr(**saved)
print(np.geterr())

Output yang Dihasilkan:

text
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
inf nan
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}

Mari kita urai:

  1. Baris 2: kebijakan bawaan — divide, over, dan invalid bernilai warn, sedangkan under sudah ignore karena underflow jarang memengaruhi hasil secara berarti.
  2. Baris 3 (saved = ...): np.seterr mengembalikan kebijakan lama dan kita simpan; baris 4 kemudian memaksa pembagian nol dan 0.0/0.0 berjalan tanpa satu pun pesan — inf dan nan tetap dihasilkan, hanya pengumumannya yang ditiadakan.
  3. Baris 5 (np.seterr(**saved)): kebijakan bawaan dikembalikan persis seperti semula — inilah pola simpan-restore yang wajib untuk kebijakan global.

Kebijakan raise untuk Data Kritis #

Bila hasil perhitungan menentukan — rekonsiliasi keuangan, laporan yang dikirim otomatis, pipeline tanpa pengawasan manusia — satu nan yang lolos jauh lebih mahal daripada program yang berhenti lebih awal. Mode all='raise' membuat setiap kesalahan floating-point melempar FloatingPointError tepat di baris penyebabnya:

python
import numpy as np

saved = np.seterr(all='raise')
try:
    print(np.array([1.0]) / np.array([0.0]))
except FloatingPointError as e:
    print("FloatingPointError:", e)
finally:
    np.seterr(**saved)
print("policy divide:", np.geterr()['divide'])

Output yang Dihasilkan:

text
FloatingPointError: divide by zero encountered in divide
policy divide: warn

Pembacaannya: pembagian 1.0/0.0 tidak lagi menghasilkan inf, tetapi melempar FloatingPointError yang pesannya sama dengan RuntimeWarning versi warn. Blok try/except menangkap error itu sehingga skrip tetap berjalan dan pesannya tercetak sebagai bukti, sedangkan finally menjamin kebijakan lama dikembalikan apa pun yang terjadi di dalam try.

Mode call: Handler Kustom #

Mode call menyerahkan penanganan kesalahan kepada fungsi milik kita: NumPy memanggil handler setiap kali kesalahan terjadi, lalu melanjutkan eksekusi seperti mode ignore. Handler didaftarkan lewat np.seterrcall:

python
import numpy as np

def laporkan(jenis, flag):
    print("handler:", jenis, "|", flag)

sebelumnya = np.seterrcall(laporkan)
saved = np.seterr(divide='call')
print(np.float64(1.0) / np.float64(0.0))
np.seterr(**saved)
np.seterrcall(sebelumnya)
print("divide:", np.geterr()['divide'])

Output yang Dihasilkan:

text
handler: divide by zero | 1
inf
divide: warn

Mari kita urai:

  1. Baris 6 (np.seterrcall): mendaftarkan laporkan sebagai handler dan mengembalikan callback sebelumnya — disimpan agar bisa dikembalikan di akhir.
  2. Baris 8: saat pembagian nol terjadi, handler dipanggil dengan dua argumen: string jenis kesalahan (divide by zero) dan kode flag floating-point. Hasil operasinya tetap inf, dan program berlanjut.
  3. Baris 9-10: kebijakan dan callback dikembalikan ke keadaan awal; baris 11 membuktikan divide kembali ke warn.

Pola ini cocok untuk pencatatan kejadian (log) ke berkas atau penghitung statistik kesalahan, yaitu ketika kesalahan perlu diketahui tetapi tidak boleh menghentikan proses batch.

Recap errstate: Kebijakan Lokal dengan with #

Sebagai recap singkat dari NaN dan Infinity: np.errstate memberi kebijakan yang hanya berlaku di dalam blok with, lalu dikembalikan secara otomatis tanpa simpan-restore manual. Untuk perlindungan yang lingkupnya lokal, itu tetap pilihan paling aman; np.seterr berguna ketika kebijakan harus menjangkau fungsi-fungsi pustaka yang kodenya tidak kita ubah. Kedua pendekatan bisa digabung — misalnya fungsi validasi kecil yang mempertajam kebijakan hanya selama badannya berjalan, pola yang melengkapi strategi pada Penanganan Data Numerik yang Tidak Valid:

python
import numpy as np

def rasio_tervalidasi(a, b):
    with np.errstate(divide='raise', invalid='raise'):
        return a / b

print(rasio_tervalidasi(np.array([10.0, 30.0]), np.array([2.0, 4.0])))
try:
    print(rasio_tervalidasi(np.array([0.0]), np.array([0.0])))
except FloatingPointError as e:
    print("FloatingPointError:", e)

Output yang Dihasilkan:

text
[5.  7.5]
FloatingPointError: invalid value encountered in divide

Rasio normal dihitung seperti biasa ([5. 7.5]), sedangkan 0.0/0.0 di dalam fungsi memicu FloatingPointError berpesan invalid value encountered in divide. Di luar fungsi, kebijakan global tidak pernah berubah — pembaca kode bisa memercayai bahwa fungsi tersebut membersihkan keadaannya sendiri.

Pedoman ringkasnya: biarkan bawaan warn saat eksplorasi, pakai np.errstate untuk lingkup lokal, dan pakai np.seterr(all='raise') hanya dengan pola simpan-restore pada bagian yang memproses data kritis.