Chapter NaN dan Infinity menunjukkan betapa mudahnya satu nan meracuni hasil agregasi: sum, mean, sampai std ikut menjadi nan tanpa pesan error. Membuang nan lebih dulu lalu menghitung memang bisa, tetapi NumPy menyediakan jalan yang lebih pendek: keluarga fungsi nan-aware yang mengabaikan nan saat menghitung.

Nama fungsinya mengikuti pola yang mudah ditebak — awalan nan ditempelkan pada fungsi agregasi biasa: np.nansum, np.nanmean, np.nanmedian, np.nanmin, np.nanmax, dan np.nanstd. Bab ini membandingkan keduanya berdampingan, membuktikan kesetaraan hasilnya, dan menutup dengan jebakan yang jarang disangka: array yang isinya nan semua.

Perbandingan Langsung dengan Versi Biasa #

python
import numpy as np

a = np.array([80.0, np.nan, 90.0, 70.0, np.nan, 85.0])
print("sum   :", a.sum(), "|", np.nansum(a))
print("mean  :", a.mean(), "|", np.nanmean(a))
print("median:", np.median(a), "|", np.nanmedian(a))
print("min   :", a.min(), "|", np.nanmin(a))
print("max   :", a.max(), "|", np.nanmax(a))
print("std   :", a.std(), "|", np.nanstd(a))

Output yang Dihasilkan:

text
sum   : nan | 325.0
mean  : nan | 81.25
median: nan | 82.5
min   : nan | 70.0
max   : nan | 90.0
std   : nan | 7.39509972887452

Setiap pasangan dihitung dari array yang sama — enam elemen, dua di antaranya nan:

Pasangan fungsiVersi biasaVersi nan-aware
sum → nansumnan325.0
mean → nanmeannan81.25
median → nanmediannan82.5
min → nanminnan70.0
max → nanmaxnan90.0
std → nanstdnan7.39509972887452

Mari kita bedah:

  1. Baris 3: array penilaian enam elemen; posisi ke-1 dan ke-4 tidak terisi (nan).
  2. Baris 4 (a.sum() vs np.nansum(a)): versi biasa terkontaminasi nan, sedangkan nansum menjumlahkan hanya empat nilai valid (80 + 90 + 70 + 85 = 325.0).
  3. Baris 5-6: nanmean membagi total valid dengan banyaknya nilai valid (325/4 = 81.25), bukan dengan a.size; median juga dihitung atas empat nilai valid saja.
  4. Baris 9 (nanstd): simpangan baku dihitung terhadap mean versi nan-aware, dengan pembagi 4 (ddof bawaan 0, sama seperti std).

Kesetaraan dengan Subset Valid #

Fungsi nan-aware tidak menyimpan kejutan: hasilnya sama dengan menghitung atas subset yang valid secara manual. Kita bisa membuktikannya:

python
import numpy as np

a = np.array([80.0, np.nan, 90.0, 70.0, np.nan, 85.0])
print(np.nanmean(a) == a[~np.isnan(a)].mean())
print(np.nanstd(a) == a[~np.isnan(a)].std())

Output yang Dihasilkan:

text
True
True

Penjelasan singkat:

  1. Baris 4 (a[~np.isnan(a)]): seleksi boolean dari Chapter Boolean Indexing mengambil empat nilai valid saja.
  2. Baris 4-5: mean dan std atas subset valid itu dibandingkan langsung dengan nanmean dan nanstd — keduanya identik (True).

Artinya, memilih antara dua cara ini soal keterbacaan, bukan kebenaran. Untuk array besar, fungsi nan-aware juga lebih hemat karena tidak perlu membuat array perantara hasil seleksi. Untuk statistik lain seperti quantile, pola subset-valid yang sama berlaku — lihat Chapter mean, median, dan percentile.

Mengabaikan NaN per Baris dan Kolom #

Pada array dua dimensi, parameter axis bekerja seperti agregasi biasa (dibahas pada Chapter Operasi Berdasarkan Axis):

python
import numpy as np

suhu = np.array([
    [27.5, np.nan, 28.1],
    [26.9, 27.3, np.nan],
])
print(np.nanmean(suhu, axis=0))
print(np.nanmean(suhu, axis=1))

Output yang Dihasilkan:

text
[27.2 27.3 28.1]
[27.8 27.1]

Mari kita baca:

  1. Baris 7 (axis=0): rata-rata per kolom, mengabaikan nan di kolom masing-masing. Kolom 0 punya dua nilai valid (27.5 dan 26.9 → 27.2), sedangkan kolom 1 dan 2 tinggal satu nilai valid sehingga rata-ratanya nilai itu sendiri.
  2. Baris 8 (axis=1): rata-rata per baris; tiap baris kehilangan satu pembacaan, dan nanmean tetap menghasilkan angka dari dua nilai yang tersisa.

Jebakan Data yang Semuanya NaN #

Perilaku nan-aware pada data yang valid-nya nol elemen berbeda-beda antar fungsi, dan di sinilah letak jebakannya:

python
import numpy as np
import warnings

kosong = np.array([np.nan, np.nan])
print("nansum  :", np.nansum(kosong))
with warnings.catch_warnings(record=True) as terekam:
    warnings.simplefilter("always")
    rata = np.nanmean(kosong)
for w in terekam:
    print(w.category.__name__, "->", w.message)
print("nanmean :", rata)

Output yang Dihasilkan:

text
nansum  : 0.0
RuntimeWarning -> Mean of empty slice
nanmean : nan

Mari kita telaah:

  1. Baris 5 (np.nansum(kosong)): jumlah dari nol nilai valid dikembalikan sebagai 0.0 — konvensi jumlah kosong — bukan nan.
  2. Baris 6-8: nanmean atas rata-rata kosong tidak punya nilai yang bisa dikembalikan secara bermakna, sehingga hasilnya nan dan NumPy mengirim RuntimeWarning.
  3. Baris 9-10: loop mencetak kategori dan pesan tiap peringatan yang tertangkap — Mean of empty slice — sehingga pesan itu bisa diverifikasi sebagai output teruji.

Sebagai penutup, keluarga ini mencakup hampir semua agregasi yang dibahas pada section Agregasi dan Statistika — nansum, nanprod, nancumsum, dan nancumprod juga tersedia. Aturannya satu: versi nan-aware mengabaikan nan, tetapi tetap memerlukan minimal satu nilai valid agar hasilnya bermakna.

Posisi Ekstrem: nanargmax dan nanargmin #

Pasangan np.argmax dan np.argmin pada Chapter min, max, argmin, dan argmax mencari posisi (indeks) nilai ekstrem, dan keduanya tidak NaN-aware: pada array yang mengandung nan, indeks yang dikembalikan justru menunjuk ke elemen nan itu sendiri. Versi nan-aware-nya, np.nanargmax dan np.nanargmin, mengabaikan nan terlebih dahulu, lalu mengembalikan indeks nilai ekstrem yang sesungguhnya.

python
import numpy as np

penjualan = np.array([500, np.nan, 650, 200, 480])
print("argmax    :", np.argmax(penjualan))
print("nanargmax :", np.nanargmax(penjualan))
print("argmin    :", np.argmin(penjualan))
print("nanargmin :", np.nanargmin(penjualan))
print("nilai terbesar:", penjualan[np.nanargmax(penjualan)])
try:
    np.nanargmax(np.array([np.nan, np.nan]))
except ValueError as e:
    print("ValueError:", e)

Output yang Dihasilkan:

text
argmax    : 1
nanargmax : 2
argmin    : 1
nanargmin : 3
nilai terbesar: 650.0
ValueError: All-NaN slice encountered

Mari kita baca:

  1. Baris 3 (np.argmax): hasilnya 1, indeks elemen nan — perbandingan yang tersentuh nan ikut terkontaminasi, sehingga pencarian maksimum berhenti di situ alih-alih di nilai 650.
  2. Baris 4-6: np.nanargmax mengabaikan nan dan mengembalikan indeks 2 (nilai 650), sedangkan np.nanargmin mengembalikan indeks 3 (nilai 200). Indeks itu bisa langsung dipakai mengambil nilainya: 650.0.
  3. Baris 8-10: bila semua elemen nan, tidak ada indeks yang bermakna, maka kedua fungsi ini melempar ValueError berpesan All-NaN slice encountered — perilaku yang ditangkap try/except agar skrip tetap berjalan.