IQR, Tukey's Fences and Adjusted Boxplot: Outlier Detection in Skewed Data
Interquartile Range (IQR), Tukey's fences (k=1.5 / k=3), boxplot anatomy, and adjusted boxplot with medcouple for skewed data — robust alternatives where z-score fails.
Şükrü Yusuf KAYA
22 min read
Beginner📊 Quartile'lar — istatistiğin görsel dili
Boxplot 1977'de John Tukey tarafından yayımlandı. Hâlâ her veri bilimcinin ilk gördüğü grafiklerden biri. Bu derste 'boxplot çizmek'i değil, 'IQR ile sistematik outlier tespiti yapmak'ı öğreneceğiz. Üstelik klasik IQR'ın skewed (asimetrik) veride neden başarısız olduğunu ve buna çözümü (adjusted boxplot + medcouple) konuşacağız.
Burada:
- Q1 (alt çeyrek) — verinin %25'lik dilim sınırı
- Q3 (üst çeyrek) — verinin %75'lik dilim sınırı
IQR, dağılımın yayılımını ölçer ama outlier'lara karşı çok dirençli. Bir
veri kümesinin %25'i bozulsa bile IQR değişmez (sadece çeyrekler dışında
kalanlar etkilenir).
Q1, Q2 (median), Q3 nasıl hesaplanır?#
data = sorted([1, 2, 3, 4, 5, 6, 7, 8, 9]) # n = 9 # Q1 = sorted[0.25 * (n-1)] = sorted[2] = 3 # Q2 = median = sorted[4] = 5 # Q3 = sorted[0.75 * (n-1)] = sorted[6] = 7 # IQR = 7 - 3 = 4
NumPy ile:
import numpy as np q1, q3 = np.percentile(data, [25, 75]) iqr = q3 - q1
Bu sınırların dışındaki gözlemler mild outlier sayılır.
Dış Sınırlar (Outer Fences) — "Extreme Outliers"#
Bu sınırların dışındakiler extreme outlier.
k=1.5 Sihirli mi?#
Hayır. Tukey deneysel olarak seçti — normal dağılımda %0.7 false positive
verir. Eğer veride beklenen outlier oranı çok düşükse (örn. %0.1) k=3 daha
güvenli; daha gevşek tespit için k=1.0 da kullanılır.
| k değeri | False positive (normal) | Tipik kullanım |
|---|---|---|
| 1.0 | %3 | Agresif tespit |
| 1.5 | %0.7 | Tukey standart |
| 2.0 | %0.05 | Konservatif |
| 3.0 | %0.0002 | Sadece extreme outlier |
python
import numpy as np def iqr_outliers(x, k=1.5): """ Tukey's fences ile outlier tespiti. Returns: outlier indices array, ve sınırlar (low, high) """ q1, q3 = np.percentile(x, [25, 75]) iqr_value = q3 - q1 lower = q1 - k * iqr_value upper = q3 + k * iqr_value is_outlier = (x < lower) | (x > upper) return np.where(is_outlier)[0], (lower, upper) # Örneknp.random.seed(42)data = np.concatenate([ np.random.normal(50, 5, 100), # normal [80, 85, 90, 15, 10], # outlier'lar]) idx, bounds = iqr_outliers(data, k=1.5)print(f"Outlier sayısı: {len(idx)}")print(f"Sınırlar: ({bounds[0]:.1f}, {bounds[1]:.1f})")print(f"Outlier değerler: {data[idx]}")IQR-based outlier tespiti
Boxplot Anatomi#
Boxplot tüm bu çeyrek bilgisini tek bir görsele sığdırır:
┌───────────┐ │ │ <-- Q3 (üst kenar) │ │ ├───── ─────┤ <-- Q2 (median, ortadaki çizgi) │ │ │ │ └───────────┘ <-- Q1 (alt kenar) │ │ <-- whisker (Tukey upper = Q3 + 1.5*IQR'a kadar) │ ● <-- outlier (whisker dışı)
Hızlı yorum yöntemi:
- Kutu büyük → veri yayılımı geniş
- Kutu küçük → sıkışmış
- Median kutunun ortasında değil → asimetrik (skewed) dağılım
- Bir whisker uzun, diğeri kısa → asimetri var
- Outlier noktalar → IQR fence'i dışında
python
import matplotlib.pyplot as pltimport numpy as np # Üç farklı dağılım simüle etnp.random.seed(42)normal_data = np.random.normal(50, 10, 1000)skewed_data = np.random.exponential(scale=20, size=1000) + 30 # right-skewedheavy_tail = np.concatenate([np.random.normal(50, 5, 950), np.random.normal(50, 30, 50)]) # heavy tail fig, axes = plt.subplots(1, 3, figsize=(15, 5))axes[0].boxplot(normal_data, vert=True)axes[0].set_title('Normal Dağılım') axes[1].boxplot(skewed_data, vert=True)axes[1].set_title('Sağa Skewed (exponential)') axes[2].boxplot(heavy_tail, vert=True)axes[2].set_title('Heavy-tail') plt.tight_layout()plt.savefig('reports/boxplots_comparison.png', dpi=120)plt.show()Üç farklı dağılım için boxplot
IQR'ın Skewed Veride Problemi#
Klasik IQR + Tukey's fences simetrik dağılım varsayar. Veri right-skewed
(sağa eğik) ise — finansal tutarlar, gecikme süreleri, gelir dağılımları —
klasik fence:
- Alt sınır gereksiz dar olur → çoğu normal değer "outlier" işaretlenir
- Üst sınır gerçek outlier'ları yakalamaz
Somut Örnek: Maaş Dağılımı#
Türkiye'de aylık net maaş right-skewed: medyan ~25.000 TL ama
top-%1 milyonların üstünde. Klasik IQR:
salaries = ... # gerçek dağılım q1, q3 = 18000, 35000 iqr = 17000 upper_fence = 35000 + 1.5*17000 = 60500
60.500 TL üstündeki herkes "outlier" — ama yöneticiler, mühendisler, doktorlar
zaten 80-150K TL kazanır. Bu outlier değil dağılımın doğal sağ kuyruğu.
Çözüm: Adjusted Boxplot + Medcouple#
Hubert ve Vandervieren (2008): asimetri varsa fence'i ayarla.
Medcouple asimetri (skewness) ölçüsü; -1 ile +1 arası:
Burada m = median. MC > 0 sağa skewed; MC < 0 sola skewed; MC = 0 simetrik.
Adjusted Boxplot Fences#
MC > 0 ise (right-skewed):
MC < 0 ise (left-skewed):
python
from statsmodels.stats.stattools import medcouple def adjusted_boxplot_outliers(x, k=1.5): """ Hubert-Vandervieren adjusted boxplot. Skewed veride klasik IQR'a göre çok daha az false positive verir. """ q1, q3 = np.percentile(x, [25, 75]) iqr_v = q3 - q1 mc = medcouple(x) if mc >= 0: lower = q1 - k * np.exp(-4 * mc) * iqr_v upper = q3 + k * np.exp(3 * mc) * iqr_v else: lower = q1 - k * np.exp(-3 * mc) * iqr_v upper = q3 + k * np.exp(4 * mc) * iqr_v is_outlier = (x < lower) | (x > upper) return np.where(is_outlier)[0], (lower, upper, mc) # Maaş örneğinp.random.seed(42)salaries = np.concatenate([ np.random.lognormal(mean=10, sigma=0.5, size=900), # ~22-180K TL [400000, 500000, 800000], # gerçek outlier'lar (CEO seviyesi)]) # Klasik IQRidx_iqr, b_iqr = iqr_outliers(salaries, k=1.5)print(f"Klasik IQR: {len(idx_iqr)} outlier") # Adjusted boxplotidx_adj, (lo, hi, mc) = adjusted_boxplot_outliers(salaries, k=1.5)print(f"Adjusted boxplot: {len(idx_adj)} outlier (MC={mc:.3f})")Adjusted boxplot — skewed veri için
💎 Pratik Pearl
Production'da feature'larının distribution'ına bak. Skewness > 1 ise ((|\text{MC}| > 0.3) eşdeğer), klasik IQR yerine adjusted boxplot kullan. Banking ekiplerinde bu basit değişiklik aylık ~%15 false positive azaltır.
Hangi Yöntem Ne Zaman?#
| Yöntem | Tercih edilen veri | Tipik kullanım |
|---|---|---|
| Z-Score | Normal dağılım, az outlier | Hipotez testleri, klasik istatistik |
| Modified Z (MAD) | Az kirli, normal-benzeri | Genel-amaçlı robust |
| IQR / Tukey's fences | Simetrik veya hafif skewed | EDA, dashboard |
| Adjusted Boxplot (medcouple) | Belirgin skewed (| MC| > 0.3) | Finansal, log-normal veri |
Karar Akışı#
[Veri normal mi?] ↓ ├── Evet, kesin → Z-score │ ├── Yakın → Modified Z (MAD) │ └── Hayır, skewed ↓ ├── Hafif → Tukey's IQR (k=1.5) │ └── Belirgin → Adjusted Boxplot
Production Notu: Mutlaka Robust Başla#
Production AD pipeline'larında default seçim: modified z-score + adjusted
boxplot. Klasik z-score'a yalnızca compliance gerektiğinde (örn. mahkeme
delili için) ya da downstream test (Grubbs) zorunlu olduğunda dön.
Modül 2.6 hands-on lab'ında NYC Taxi verisinde 5 farklı detektörün PR-AUC
karşılaştırmasını yapacağız: z-score, modified z, IQR, adjusted boxplot, Grubbs
(bir sonraki ders). Hangisinin en güçlü olduğunu görselleştirerek göstereceğiz.
👉 Bir sonraki ders
Ders 2.3 — Grubbs Test, Dixon Q-Test, Generalized ESD. Z-score ve IQR'ı hipotez testine çeviriyoruz: 'bu gözlem outlier mı, p-değeri nedir?' Klasik istatistik kapısı. Çoklu outlier için Generalized ESD prosedürü.
Frequently Asked Questions
Evet. matplotlib `plt.boxplot(whis=1.5)` parametresiyle değiştirilebilir. `whis=3` daha konservatif, `whis=1.0` daha agresif. seaborn da `whis` parametresi sunar. Adjusted boxplot için seaborn'da doğrudan desteği yok — Python'da elle medcouple ekleyerek özel boxplot çizebilirsin.
Yorumlar & Soru-Cevap
(0)Yorum yazmak için giriş yap.
Yorumlar yükleniyor...
Related Content
Module 0: Course Framework & Workshop Setup
Who Is an Anomaly Detection Engineer? Differences from Fraud, SRE, Quality Engineer Roles and the Turkey Salary Landscape
Start LearningModule 0: Course Framework & Workshop Setup
Course Philosophy: Why This Path, Why This Order — The Anomaly Detection Learning River
Start LearningModule 0: Course Framework & Workshop Setup