Pencilan

Mendeteksi pencilan, menginvestigasi penyebabnya, dan mempertimbangkan apakah dipertahankan atau dibuang.

Daftar isi

Pencilan (outlier) adalah nilai yang jauh menyimpang dari pola data lainnya, dan penanganannya menentukan kejujuran analisis. Topik ini menghitung batas pencilan dengan aturan IQR pada data konsumsi listrik harian, memperlihatkannya melalui boxplot yang merangkum kuartil dalam satu kotak, lalu membedakan dua penyebab yang berbeda, yaitu salah ketik dan peristiwa asli yang ekstrem. Bagian akhirnya membahas kapan pencilan dipertahankan dan kapan dikoreksi.

Aturan IQR untuk mendeteksi pencilan#

Aturan yang paling umum menggunakan kuartil. Kuartil pertama (Q1) memisahkan 25% data terbawah, kuartil ketiga (Q3) memisahkan 25% teratas, dan rentang antarkuartil (interquartile range, IQR) adalah Q3 dikurangi Q1. Sebuah nilai ditandai pencilan apabila berada di bawah Q1−1,5×IQRQ_1 - 1{,}5 \times IQR atau di atas Q3+1,5×IQRQ_3 + 1{,}5 \times IQR. Angka 1,5 adalah konvensi yang digunakan hampir semua perangkat lunak statistika, termasuk boxplot pada matplotlib.

Data contohnya adalah konsumsi listrik harian satu rumah selama tiga puluh hari dalam kilowatt-jam (kWh). Salah satu harinya tercatat 120,0 kWh, angka yang tidak masuk akal untuk rumah kecil dan patut diduga salah ketik. Kode berikut menghitung kuartil, IQR, dan kedua batasnya, lalu menyaring nilai yang melampaui batas itu.

python batas-iqr.py
import numpy as np

# konsumsi listrik harian satu rumah selama 30 hari (kWh)
konsumsi = [9.1, 8.4, 7.8, 10.2, 9.6, 8.8, 11.4, 9.9, 8.2, 10.5,
            9.3, 7.9, 12.1, 10.8, 9.5, 8.6, 10.1, 9.8, 8.9, 11.2,
            9.4, 10.6, 8.5, 12.4, 9.7, 10.3, 9.2, 11.8, 10.9, 120.0]

q1 = np.percentile(konsumsi, 25)
q3 = np.percentile(konsumsi, 75)
iqr = q3 - q1
batas_bawah = q1 - 1.5 * iqr
batas_atas = q3 + 1.5 * iqr

print(f"Q1 = {q1:.2f}, Q3 = {q3:.2f}, IQR = {iqr:.2f}")
print(f"batas bawah = {batas_bawah:.2f}, batas atas = {batas_atas:.2f}")

pencilan = [v for v in konsumsi if v < batas_bawah or v > batas_atas]
print("pencilan terdeteksi:", pencilan)
text Keluaran
Q1 = 8.95, Q3 = 10.75, IQR = 1.80
batas bawah = 6.25, batas atas = 13.45
pencilan terdeteksi: [120.0]

Kuartilnya nyaris tidak tersentuh oleh nilai 120,0 karena kuartil hanya melihat posisi data. IQR sebesar 1,80 kWh menyisakan batas 6,25 sampai 13,45 kWh, dan hanya nilai 120,0 yang melampaui batas atas tersebut. Nilai tertinggi lainnya, 12,4 kWh, masih jauh di dalam batas. Aturan ini menandai kandidat pencilan, tetapi tidak pernah memutuskan penyebabnya, dan itulah pekerjaan investigasi berikutnya.

Boxplot untuk melihat pencilan#

Boxplot merangkum kuartil dalam satu gambar. Kotaknya membentang dari Q1 sampai Q3 dengan garis median di tengah, kumisnya (whisker) memanjang ke nilai ekstrem yang masih berada di dalam batas 1,5 kali IQR, dan titik yang berada di luar kumis digambar terpisah sebagai pencilan. Bentuk ini lazim digunakan ketika banyak kelompok dibandingkan sekaligus, karena setiap kelompok cukup digambarkan dengan satu kotak.

Contoh berikut menggambar boxplot untuk tiga versi data konsumsi listrik yang sama, yaitu versi dengan salah ketik 120,0, versi yang sudah dikoreksi menjadi 12,0, dan versi dengan nilai asli 26,0 dari bulan kedua. Ketiganya menggunakan 29 hari data yang polanya sama, sehingga satu-satunya perbedaan ada pada satu nilai terakhir. Perbandingan ini menempatkan salah ketik dan nilai asli pada satu gambar yang sama.

python boxplot-konsumsi.py
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

# tiga versi 30 hari konsumsi listrik (kWh): salah ketik, koreksi, dan nilai asli
harian = [9.1, 8.4, 7.8, 10.2, 9.6, 8.8, 11.4, 9.9, 8.2, 10.5,
          9.3, 7.9, 12.1, 10.8, 9.5, 8.6, 10.1, 9.8, 8.9, 11.2,
          9.4, 10.6, 8.5, 12.4, 9.7, 10.3, 9.2, 11.8, 10.9]
versi = {
    "salah ketik (120.0)": harian + [120.0],
    "koreksi (12.0)": harian + [12.0],
    "nilai asli (26.0)": harian + [26.0],
}

plt.figure(figsize=(8, 4.5))
plt.boxplot(list(versi.values()), tick_labels=list(versi.keys()))
plt.title("Konsumsi listrik harian pada tiga versi data")
plt.ylabel("Konsumsi (kWh)")
plt.savefig("boxplot-konsumsi.png", dpi=120)
print("Grafik tersimpan: boxplot-konsumsi.png")
text Keluaran
Grafik tersimpan: boxplot-konsumsi.png

Boxplot konsumsi listrik pada tiga versi data
Boxplot konsumsi listrik pada tiga versi data

Gambar di atas memperlihatkan efek skala. Pada versi dengan salah ketik, seluruh kotak tertekan menjadi garis tipis di dasar gambar karena sumbu tegak memuai hingga 120 kWh. Setelah dikoreksi, kotaknya terbaca lebar dan tidak ada titik di luar kumisnya, sedangkan versi dengan nilai asli 26,0 menampilkan satu titik di atas kumis, yaitu bentuk khas pencilan yang sah. Perbandingan tiga versi pada skala yang sama membuat efek satu nilai salah ketik terlihat seketika.

Dua penyebab yang berbeda#

Pencilan yang sudah terdeteksi wajib diinvestigasi sebelum disentuh. Langkah pertamanya adalah menelusuri catatan sumber, misalnya lembar pencatatan manual, berkas mentah dari sensor, atau riwayat perubahan data. Pada data contoh, catatan sumber memperlihatkan dua cerita yang berbeda untuk dua pencilan. Nilai 120,0 kWh ternyata salah ketik dari 12,0 kWh, sedangkan nilai 26,0 kWh pada bulan kedua sungguh terjadi, yaitu saat kunjungan keluarga membuat pendingin ruangan menyala seharian penuh. Kode berikut membandingkan dampak keduanya terhadap mean, median, dan hasil deteksi IQR.

python investigasi-konsumsi.py
import pandas as pd

# 29 hari dengan pola harian yang sama pada dua bulan berbeda (kWh)
harian = [9.1, 8.4, 7.8, 10.2, 9.6, 8.8, 11.4, 9.9, 8.2, 10.5,
          9.3, 7.9, 12.1, 10.8, 9.5, 8.6, 10.1, 9.8, 8.9, 11.2,
          9.4, 10.6, 8.5, 12.4, 9.7, 10.3, 9.2, 11.8, 10.9]

# bulan pertama: tercatat 120.0, padahal catatan sumbernya 12.0 (salah ketik)
bulan_salah = pd.Series(harian + [120.0])
# bulan pertama setelah nilai salah ketik dikoreksi
bulan_koreksi = pd.Series(harian + [12.0])
# bulan kedua: nilai 26.0 asli, AC menyala seharian penuh saat kunjungan keluarga
bulan_ekstrem = pd.Series(harian + [26.0])


def ringkas(s: pd.Series) -> str:
    q1 = s.quantile(0.25)
    q3 = s.quantile(0.75)
    iqr = q3 - q1
    bawah, atas = q1 - 1.5 * iqr, q3 + 1.5 * iqr
    pencilan = list(s[(s < bawah) | (s > atas)])
    return f"mean = {s.mean():.2f}, median = {s.median():.2f}, pencilan = {pencilan}"


print("bulan salah ketik :", ringkas(bulan_salah))
print("bulan dikoreksi   :", ringkas(bulan_koreksi))
print("bulan nilai asli  :", ringkas(bulan_ekstrem))
text Keluaran
bulan salah ketik : mean = 13.50, median = 9.75, pencilan = [120.0]
bulan dikoreksi   : mean = 9.90, median = 9.75, pencilan = []
bulan nilai asli  : mean = 10.36, median = 9.75, pencilan = [26.0]

Dampak salah ketik terlihat jelas pada mean, yang melonjak dari 9,90 menjadi 13,50 kWh hanya karena satu nilai, sedangkan median tetap di 9,75 kWh. Setelah dikoreksi, mean kembali wajar dan pencilannya hilang. Nilai asli 26,0 kWh juga menaikkan mean menjadi 10,36 kWh, tetapi kali ini kenaikan itu merupakan informasi, bukan cacat. Median terbukti tahan terhadap kedua jenis pencilan, sedangkan mean selalu mengikuti nilai ekstremnya. Selisih 3,60 kWh pada mean akibat satu salah ketik jauh lebih besar daripada kenaikan 0,46 kWh akibat peristiwa aslinya, sehingga ketelitian investigasi langsung berdampak pada keakuratan laporan.

Mempertahankan atau membuang pencilan#

Keputusan mengikuti hasil investigasi, bukan selera. Pencilan yang terbukti cacat pencatatan dikoreksi apabila nilai benarnya diketahui dari sumber, atau ditandai dan dikecualikan apabila nilai benarnya tidak dapat dilacak. Pencilan yang asli dipertahankan karena ia bagian dari data, dan peristiwa jarang seperti konsumsi 26,0 kWh sering menjadi temuan paling menarik bagi analisis efisiensi listrik.

Menghapus pencilan asli demi memperindah angka menyembunyikan informasi. Mean yang turun setelah pencilan dibuang terlihat lebih rapi, tetapi laporan itu tidak lagi mewakili bulan yang sebenarnya terjadi. Apabila analisis memang hanya berlaku untuk kondisi normal, lakukan dua versi perhitungan, satu dengan pencilan dan satu tanpa, lalu laporkan keduanya dengan alasan masing-masing. Keterbukaan semacam ini menjaga kepercayaan terhadap seluruh angka dalam laporan.

Setiap keputusan dicatat dalam dokumentasi analisis. Catatan minimalnya memuat nilai lama, nilai baru atau status pengecualiannya, penyebab yang ditemukan, dan sumber yang membuktikannya. Dokumentasi semacam ini membuat analisis dapat dipertanggungjawabkan ketika pembaca laporan menanyakan mengapa satu hari konsumsinya tidak dihitung, dan mencegah keputusan yang sama dibuka ulang tanpa dasar pada analisis berikutnya.

Ringkasan#

  • Aturan IQR menandai pencilan sebagai nilai di bawah Q1−1,5×IQRQ_1 - 1{,}5 \times IQR atau di atas Q3+1,5×IQRQ_3 + 1{,}5 \times IQR, dengan kuartil yang dihitung dari data.
  • Boxplot menggambar kuartil dan pencilan sekaligus, sehingga nilai yang mencurigakan terlihat tanpa membaca tabel.
  • Pencilan diinvestigasi melalui catatan sumber, yaitu salah ketik dikoreksi, sedangkan nilai asli dipertahankan dan dijelaskan.
  • Median lebih tahan terhadap pencilan daripada mean, sehingga keputusan penanganan pencilan mengubah angka laporan.

Referensi#