Metode IQR mengukur jarak berdasarkan kuartil; z-score mengukurnya berdasarkan rata-rata dan standard deviation. Z-score menyatakan seberapa jauh sebuah nilai dari mean, dalam satuan standard deviation (std): z = (x − mean) / std. Konvensi yang lazim menandai nilai dengan |z| > 3 sebagai outlier, dengan ambang 2 sebagai pilihan yang lebih ketat. Chapter ini menghitung z-score dengan pandas, lalu memperlihatkan kelemahan metode ini yang tidak dimiliki IQR: sensitif terhadap outlier itu sendiri.
Menghitung Z-Score #
Skrip berikut menguji aturan |z| > 3 pada kelembapan Sensor Utara selama 30 hari penuh Januari 2026, yang memuat satu kesalahan pembacaan 89.0 pada 21 Januari:
"""Mendeteksi outlier dengan z-score dan melihat batas keandalannya."""
import pandas as pd
import numpy as np
kalender = pd.date_range("2026-01-01", periods=30, freq="D")
kelembapan = pd.Series(
[
61, 62, 60, 63, 59, 62, 61, 60, 64, 62,
61, 63, 60, 59, 62, 61, 63, 60, 62, 61,
89, 62, 60, 61, 63, 60, 59, 61, 62, 60,
],
index=kalender,
dtype="float64",
name="kelembapan",
)
mean_k = kelembapan.mean()
std_k = kelembapan.std()
z_kelep = (kelembapan - mean_k) / std_k
print("Kelembapan 30 hari: mean =", round(mean_k, 2), "std =", round(std_k, 2))
print("z-score hari 21:", round(z_kelep.loc["2026-01-21"], 2))
terflag = z_kelep.abs() > 3
print("Hari dengan |z| > 3 :", end=" ")
print(kelembapan[terflag].index.strftime("%d Jan").tolist(), "nilai", kelembapan[terflag].tolist())
print("Jumlah |z| > 2 :", int((z_kelep.abs() > 2).sum()))
print()
kalender_u = pd.date_range("2026-01-01", periods=14, freq="D")
suhu = pd.Series(
[
22.5, 23.1, np.nan, 24.0, 24.8, np.nan, np.nan,
25.5, 41.0, 25.2, 12.0, 23.8, 24.1, np.nan,
],
index=kalender_u,
name="suhu",
)
z_suhu = (suhu - suhu.mean()) / suhu.std()
print("Suhu 14 hari: mean =", round(suhu.mean(), 2), "std =", round(suhu.std(), 2))
print("z-score seluruh baris (NaN ikut menjadi NaN):")
print(z_suhu.round(2))
print("Jumlah |z| > 3 :", int((z_suhu.abs() > 3).sum()))
print("Jumlah |z| > 2 :", int((z_suhu.abs() > 2).sum()))
print("Nilai |z| > 2 :", suhu[(z_suhu.abs() > 2)].tolist())Output yang Dihasilkan:
Kelembapan 30 hari: mean = 62.1 std = 5.25
z-score hari 21: 5.13
Hari dengan |z| > 3 : ['21 Jan'] nilai [89.0]
Jumlah |z| > 2 : 1
Suhu 14 hari: mean = 24.6 std = 6.97
z-score seluruh baris (NaN ikut menjadi NaN):
2026-01-01 -0.30
2026-01-02 -0.22
2026-01-03 NaN
2026-01-04 -0.09
2026-01-05 0.03
2026-01-06 NaN
2026-01-07 NaN
2026-01-08 0.13
2026-01-09 2.35
2026-01-10 0.09
2026-01-11 -1.81
2026-01-12 -0.11
2026-01-13 -0.07
2026-01-14 NaN
Freq: D, Name: suhu, dtype: float64
Jumlah |z| > 3 : 0
Jumlah |z| > 2 : 1
Nilai |z| > 2 : [41.0]Mari kita telaah bagian-bagiannya:
- Baris 17-19 (mean, std, dan z): pandas menghitung
std()dengan pembagiddof=1(standard deviation sampel) secara bawaan; hasilnya Series z-score sepanjang data asli. Perbedaan ini penting karenanumpy.std()secara bawaan memakaiddof=0dan akan memberi angka sedikit berbeda. - Baris 21: z-score 21 Januari bernilai
5.13— jauh melampaui ambang 3. - Baris 22-24 (mask
|z| > 3):z_kelep.abs() > 3menghasilkanTruetepat satu baris; kelembapan yang terflag hanya89.0pada 21 Januari, persis kesalahan sensor yang kita tanam. - Baris 25: dengan ambang 2, jumlah baris yang terflag tetap satu; pada data ini kedua ambang sepakat.
Satu outlier tunggal pada 30 titik adalah kondisi ideal bagi z-score: mean 62.1 dan std 5.25 masih mewakili mayoritas data, sehingga nilai menyimpang terdeteksi jelas.
Jebakan Masking pada Data Kecil #
Bagian kedua skrip mengulang perhitungan yang sama pada suhu 14 hari yang sudah kita kenal — data kecil dengan dua outlier berlawanan arah dan empat lubang NaN. Hasilnya berbeda total: mean 24.6 tertarik naik oleh 41.0, std membengkak menjadi 6.97, dan akibatnya z-score kedua outlier ikut mengecil. Nilai 41.0 hanya mencapai z 2.35 dan 12.0 hanya -1.81, sehingga |z| > 3 tidak menandai satu baris pun, sementara |z| > 2 hanya menangkap 41.0. Fenomena ini disebut masking: outlier yang ada justru menyembunyikan dirinya dan saudaranya dengan menggelembungkan std.
Perhatikan juga kolom z-score pada output: setiap NaN pada suhu menjadi NaN pada z-score, dan perbandingan dengan NaN menghasilkan False sehingga baris kosong tidak pernah masuk mask. Perilaku ini sama dengan yang kita amati pada metode IQR, dan biasanya memang diharapkan.
IQR atau Z-Score #
Tabel berikut merangkum pertimbangan memilih di antara keduanya:
| Aspek | IQR | Z-Score |
|---|---|---|
| Basis perhitungan | Kuartil (statistik posisi) | Mean dan standard deviation |
| Tahan terhadap outlier | Ya | Tidak — mean dan std tercemar outlier |
| Syarat data | Cukup banyak agar kuartil stabil | Jumlah banyak, distribusi mendekati normal |
| Ambang lazim | 1.5 × IQR | ` |
Rincian parameter std() termasuk ddof tersedia pada pandas.Series.std
, dan ringkasan perilaku NaN tetap bisa dirujuk ke panduan Working with missing data
.
Dua chapter terakhir memberi kita daftar kandidat outlier, tetapi daftar saja tidak membersihkan apa pun. Chapter berikutnya melengkapi persenjataan ini dengan clip(): membatasi nilai ekstrem ke batas tertentu alih-alih membuang barisnya.