Beralih dari data kosong ke data salah: pembacaan 41.0 derajat di tengah hari-hari bersuhu 22-25 derajat adalah nilai ekstrem yang menuntut perlakuan tersendiri. Chapter Mendeteksi Outlier di section Data Cleaning sudah memperkenalkan aturan IQR pada satu Series kecil tanpa kekosongan. Chapter ini menaikkan taruhannya: menerapkan aturan yang sama pada data dua kolom sensor selama 14 hari yang sekaligus mengandung lubang NaN, membentuk boolean mask untuk menandai outlier, dan menyaring barisnya.

Menghitung Batas IQR #

Aturan IQR (interquartile range) menetapkan tiga langkah: hitung IQR = Q3 − Q1, lalu tetapkan batas bawah = Q1 − 1.5 × IQR dan batas atas = Q3 + 1.5 × IQR. Nilai di luar kedua batas ditandai sebagai outlier. Karena IQR dibangun dari kuartil (statistik berbasis posisi, bukan rata-rata), satu nilai ekstrem tidak menarik batasnya — inilah keunggulan utama IQR. Skrip berikut menghitung batas untuk dua kolom sekaligus:

python
"""Menerapkan aturan IQR pada dua kolom sensor dan menyaring baris outliernya."""
import pandas as pd

gudang = pd.DataFrame(
    {
        "tanggal": [f"2026-01-{d:02d}" for d in range(1, 15)],
        "suhu_utara": [
            22.5, 23.1, None, 24.0, 24.8, None, None,
            25.5, 41.0, 25.2, 12.0, 23.8, 24.1, None,
        ],
        "suhu_selatan": [
            24.2, 23.8, 24.0, 24.4, 25.0, 24.8, 25.1,
            24.6, 23.9, 24.3, 24.7, 24.1, 24.5, 24.9,
        ],
    }
)

print("Batas IQR dan jumlah outlier per kolom:")
for kolom in ["suhu_utara", "suhu_selatan"]:
    kol = gudang[kolom]
    q1 = kol.quantile(0.25)
    q3 = kol.quantile(0.75)
    iqr = q3 - q1
    bawah = q1 - 1.5 * iqr
    atas = q3 + 1.5 * iqr
    mask = (kol < bawah) | (kol > atas)
    print(
        f"{kolom}: Q1={round(q1, 4)} Q3={round(q3, 4)} IQR={round(iqr, 4)} "
        f"batas=[{round(bawah, 4)}, {round(atas, 4)}] outlier={int(mask.sum())}"
    )

q1 = gudang["suhu_utara"].quantile(0.25)
q3 = gudang["suhu_utara"].quantile(0.75)
iqr = q3 - q1
mask_utara = (gudang["suhu_utara"] < q1 - 1.5 * iqr) | (
    gudang["suhu_utara"] > q3 + 1.5 * iqr
)

print()
print("Baris Sensor Utara yang terdeteksi outlier:")
print(gudang.loc[mask_utara, ["tanggal", "suhu_utara"]])
print()

print("Baris lengkap yang lolos filter (tanpa outlier):")
bersih = gudang[~mask_utara]
print(f"Jumlah baris tersisa: {len(bersih)} dari {len(gudang)}")

Output yang Dihasilkan:

text
Batas IQR dan jumlah outlier per kolom:
suhu_utara: Q1=23.275 Q3=25.1 IQR=1.825 batas=[20.5375, 27.8375] outlier=2
suhu_selatan: Q1=24.125 Q3=24.775 IQR=0.65 batas=[23.15, 25.75] outlier=0

Baris Sensor Utara yang terdeteksi outlier:
       tanggal  suhu_utara
8   2026-01-09        41.0
10  2026-01-11        12.0

Baris lengkap yang lolos filter (tanpa outlier):
Jumlah baris tersisa: 12 dari 14

Mari kita telaah intinya:

  1. Baris 21-22 (kol.quantile(0.25) dan kol.quantile(0.75)): quantile() melewati NaN secara bawaan, sehingga Q1 dan Q3 Sensor Utara dihitung dari sepuluh nilai terisi; rinciannya ada pada pandas.Series.quantile .
  2. Baris 23-25 (IQR dan batas): Sensor Utara menghasilkan Q1 23.275, Q3 25.1, IQR 1.825, sehingga batasnya [20.5375, 27.8375]; Sensor Selatan yang stabil menghasilkan IQR 0.65 dengan batas [23.15, 25.75].
  3. Baris 26 (mask boolean): dua perbandingan digabung operator | dengan tanda kurung di sekelilingnya; hasilnya True hanya pada baris di luar batas.
  4. Baris 41 (gudang.loc[mask_utara, ...]): mask dipakai menyaring DataFrame; baris yang muncul persis dua, yaitu 9 Januari bernilai 41.0 dan 11 Januari bernilai 12.0.

Kolom Sensor Selatan mendemonstrasikan sisi lain aturan IQR: tanpa nilai ekstrem, mask-nya kosong dan outlier=0. Satu aturan yang sama bisa diterapkan ke banyak kolom tanpa mengubah ambang secara manual, karena batas dihitung ulang dari distribusi masing-masing kolom.

Menyaring dan Menandai Baris #

Mask outlier adalah gerbang untuk semua keputusan lanjutan. Menyaring dengan gudang[mask_utara] menampilkan baris yang dicurigai, sedangkan negasinya gudang[~mask_utara] menyimpan baris yang lolos — pada data kita tersisa 12 dari 14 baris. Pola menyimpan mask ke variabel (bukan menulis perbandingan panjang berulang kali) memudahkan dokumentasi: satu mask bisa dipakai untuk memfilter, menghitung sum(), dan menandai baris di laporan.

Kapan Memilih IQR #

Aturan IQR cocok sebagai alat deteksi bawaan karena tiga alasan: perhitungannya hanya butuh quantile() (versi DataFrame untuk banyak kolom sekaligus ada di pandas.DataFrame.quantile ), batasnya tidak terdistorsi oleh outlier itu sendiri, dan maknanya mudah dijelaskan ke pembaca laporan (kuartil atas plus satu setengah rentang kuartil). Kelemahannya, IQR membutuhkan data yang cukup banyak agar kuartilnya stabil; pada puluhan baris, kuartil bisa berubah drastis hanya karena satu nilai tambahan. Alternatif berbasis rata-rata — z-score — memiliki karakter berbeda dan jebakan tersendiri yang kita bedah pada chapter berikutnya.

Dengan mask IQR di tangan, pertanyaan berpindah dari “mana yang ekstrem” menjadi “apa yang boleh dilakukan pada nilai ekstrem”. Chapter Winsorization dan Clipping menjawabnya dengan clip().