İçeriğe geç

Grubbs, Dixon ve Generalized ESD: Outlier Tespitini Hipotez Testine Çevirmek

Klasik istatistiksel hipotez testleri ile outlier tespiti: Grubbs test (tek outlier), Dixon Q-test (küçük örneklem), Generalized ESD (çoklu outlier) — p-değer, formüller, scipy implementasyonu, ve hangi test ne zaman.

Şükrü Yusuf KAYA
26 dakikalık okuma
Orta
Grubbs, Dixon ve Generalized ESD: Outlier Tespitini Hipotez Testine Çevirmek
🧪 Heuristikten hipoteze
Önceki iki derste 'z-score 3'ten büyükse outlier' veya 'IQR'ın 1.5 katı dışındaysa outlier' gibi eşik tabanlı kararlar kullandık. Bu derste resmi istatistiksel hipotez testlerine geçeceğiz: 'bu gözlem outlier mı, p-değeri nedir?' Compliance ağır sektörler (banking, healthcare, regulatory) bu seviyede formal kararlar bekler.

Hipotez Testi Çerçevesi#

Bir outlier testi temelde şu cümleyi soruyor:
H0 (null hipotez): "Tüm gözlemler aynı normal dağılımdan geliyor (outlier yok)." H1 (alternatif hipotez): "En az bir gözlem farklı bir dağılımdan geliyor (outlier var)."
Test istatistiği hesaplanır → kritik değer veya p-değeri ile karşılaştırılır → H0 reddedilir veya reddedilemez.
Önemli not: Hipotez testleri "outlier ne kadar büyük" sorusunu cevaplamaz; "outlier istatistiksel anlamlı mı" sorusunu cevaplar. Production'da bu önemli bir ayrım.

Grubbs Test (Maximum Normalized Residual)#

Frank Grubbs, 1969. Bir veri kümesindeki tek bir outlier'ı test eder.

Varsayım#

Veri normal dağılır.

Test İstatistiği#

Burada (\bar{x}) = mean, s = standart sapma. G büyükse outlier var.

Kritik Değer#

Verilen anlamlılık α (genelde 0.05) ve örneklem boyutu n için kritik değer:
Eğer G > G_crit ise H0 reddedilir → "outlier var" (en aşırı uçtaki gözlem outlier).

Tek-yönlü vs İki-yönlü#

  • İki-yönlü (yukarıda): en aşırı sapan (max veya min) outlier mı sorusu
  • Tek-yönlü: sadece "max outlier mı" veya "min outlier mı"

Çoklu outlier — Iterative Grubbs#

Grubbs aslında tek outlier testidir. Birden fazla varsa:
  1. Grubbs uygula → en aşırı outlier'ı bul
  2. Outlier'ı veriden çıkar
  3. Tekrar Grubbs uygula
  4. H0 reddedilemez olunca dur
Uyarı: Iterative Grubbs çoklu test düzeltmesi yapmaz; bu nedenle ESD daha tercih edilir (aşağıda).
python
import numpy as np
from scipy import stats
 
def grubbs_test(x, alpha=0.05):
"""
Grubbs test — tek outlier için (two-sided).
 
Returns:
is_outlier_present: True/False
outlier_index: int or None
G: test istatistiği
G_critical: kritik değer
"""
x = np.asarray(x, dtype=float)
n = len(x)
if n < 3:
raise ValueError("Grubbs için en az 3 gözlem gerek")
 
mu = np.mean(x)
s = np.std(x, ddof=1) # sample std
abs_dev = np.abs(x - mu)
max_idx = np.argmax(abs_dev)
G = abs_dev[max_idx] / s
 
# Kritik değer (two-sided)
t_val = stats.t.ppf(1 - alpha / (2 * n), n - 2)
G_crit = ((n - 1) / np.sqrt(n)) * np.sqrt(
t_val**2 / (n - 2 + t_val**2)
)
 
is_outlier = G > G_crit
return {
'is_outlier_present': is_outlier,
'outlier_index': int(max_idx) if is_outlier else None,
'outlier_value': float(x[max_idx]) if is_outlier else None,
'G': float(G),
'G_critical': float(G_crit),
}
 
# Örnek
np.random.seed(42)
data = np.random.normal(loc=50, scale=5, size=30)
data = np.append(data, 100) # bir outlier ekle
 
result = grubbs_test(data, alpha=0.05)
print(f"Outlier var mı: {result['is_outlier_present']}")
print(f"G = {result['G']:.3f}, G_crit = {result['G_critical']:.3f}")
print(f"Outlier değer: {result['outlier_value']}")
Grubbs test implementasyonu

Dixon Q-Test (Küçük Örneklem)#

W. J. Dixon, 1950. Küçük örneklemler (3 ≤ n ≤ ~30) için tasarlanmış. Kimya laboratuvar deneylerinde çok yaygın.

Test İstatistiği (n=3-7 için)#

Sıralı veriden (x_{(1)} \le x_{(2)} \le \cdots \le x_{(n)}):
  • En küçük gözlemin outlier olup olmadığını test: (Q_{10} = \frac{x_{(2)} - x_{(1)}}{x_{(n)} - x_{(1)}})
  • En büyük gözlemin outlier olup olmadığını test: (Q_{10} = \frac{x_{(n)} - x_{(n-1)}}{x_{(n)} - x_{(1)}})
Daha büyük n için farklı formüller var (Q11, Q21, Q22). scipy doğrudan desteklemiyor;
outliers
paketi veya kendi implementasyonun.

Kritik Değer Tablosu (α=0.05, n=3-10)#

nQ_crit (α=0.05)
30.941
40.765
50.642
60.560
70.507
80.468
90.437
100.412
Q > Q_crit ise outlier.

Kullanım Senaryosu#

Dixon özellikle:
  • Laboratuvar deneyleri (5-7 tekrarlı ölçüm)
  • Kalite kontrol (10 örneklik partiler)
  • Analitik kimya (ASTM standartlarında zorunlu)
Büyük veride Dixon yerine Grubbs veya ESD tercih edilir.
python
def dixon_q_test(x, alpha=0.05):
"""
Dixon Q-test (Q10) — small sample outlier test.
Sadece n=3-7 için lookup table dahili; n=8-30 için kabaca extend ettim.
"""
Q_TABLE = {
3: 0.941, 4: 0.765, 5: 0.642, 6: 0.560,
7: 0.507, 8: 0.468, 9: 0.437, 10: 0.412,
15: 0.338, 20: 0.300, 25: 0.277, 30: 0.260,
}
x_sorted = np.sort(x)
n = len(x_sorted)
if n < 3 or n > 30:
raise ValueError("Dixon Q-test için 3 <= n <= 30 önerilir")
 
q_lower = (x_sorted[1] - x_sorted[0]) / (x_sorted[-1] - x_sorted[0])
q_upper = (x_sorted[-1] - x_sorted[-2]) / (x_sorted[-1] - x_sorted[0])
 
# En yakın tabloya hangisi n'e karşılık?
closest_n = min(Q_TABLE.keys(), key=lambda k: abs(k - n))
q_crit = Q_TABLE[closest_n]
 
return {
'q_lower': float(q_lower),
'q_upper': float(q_upper),
'q_critical': float(q_crit),
'lower_outlier': q_lower > q_crit,
'upper_outlier': q_upper > q_crit,
'lower_value': float(x_sorted[0]) if q_lower > q_crit else None,
'upper_value': float(x_sorted[-1]) if q_upper > q_crit else None,
}
 
# Kimya lab örneği — 7 ölçüm
ölçümler = [9.95, 10.01, 10.03, 10.05, 10.07, 10.10, 11.50]
result = dixon_q_test(ölçümler)
print(f"Q (üst): {result['q_upper']:.3f}, Q_crit: {result['q_critical']:.3f}")
print(f"Üst outlier: {result['upper_outlier']}")
print(f"Outlier değer: {result['upper_value']}")
Dixon Q-test — küçük örneklem outlier testi

Generalized ESD (Extreme Studentized Deviate)#

Bernard Rosner, 1983. Grubbs'un çoklu outlier'a sağlam uzantısı.

Avantajı#

Grubbs'u iteratif uygulamak çoklu test problemine yol açar (her testte α hatası birikir). ESD bu problemi çözer:
  • Önceden maximum outlier sayısı (r) belirtirsin
  • Test r kez koşar
  • Her adımda farklı kritik değer kullanır (çoklu test düzeltmesi içerir)

Algoritma#

INPUT: data x, max outlier sayısı r, anlamlılık α FOR k = 1 to r: 1. Mean ve std hesapla 2. R_k = max |x_i - mean| / std (eski Grubbs G) 3. En büyük R_k olan gözlemi işaretle ve veriden çıkar 4. Kritik değer λ_k hesapla (Rosner formülü) 5. R_k > λ_k ise bu gözlem outlier END FOR
Kritik değer formülü:
python
def generalized_esd(x, max_outliers=10, alpha=0.05):
"""
Generalized ESD — multiple outliers in normal data.
"""
x = np.asarray(x, dtype=float).copy()
n_init = len(x)
outlier_indices = [] # orijinal indislerle
original_indices = np.arange(n_init)
 
R_values = []
lambda_values = []
 
for k in range(1, max_outliers + 1):
if len(x) < 3:
break
mu = np.mean(x)
s = np.std(x, ddof=1)
abs_dev = np.abs(x - mu)
max_idx_local = np.argmax(abs_dev)
R_k = abs_dev[max_idx_local] / s
 
n = len(x)
p = 1 - alpha / (2 * (n - k + 1))
t_val = stats.t.ppf(p, n - k - 1)
lambda_k = ((n - k) * t_val) / np.sqrt(
(n - k - 1 + t_val**2) * (n - k + 1)
)
 
R_values.append(R_k)
lambda_values.append(lambda_k)
 
if R_k > lambda_k:
# Bu gözlem outlier — orijinal indis ekle
outlier_indices.append(int(original_indices[max_idx_local]))
 
# Bu gözlemi çıkar
mask = np.arange(len(x)) != max_idx_local
x = x[mask]
original_indices = original_indices[mask]
 
return {
'outlier_indices': outlier_indices,
'R_values': R_values,
'lambda_values': lambda_values,
'n_outliers': len(outlier_indices),
}
 
# Örnek
np.random.seed(42)
data = np.concatenate([
np.random.normal(50, 5, 100),
[80, 90, 100, 15, 10], # 5 outlier
])
 
result = generalized_esd(data, max_outliers=10, alpha=0.05)
print(f"Toplam outlier: {result['n_outliers']}")
print(f"Outlier indisleri: {result['outlier_indices']}")
print(f"Outlier değerler: {data[result['outlier_indices']]}")
Generalized ESD — multi-outlier test

Üç Test Yan Yana#

ÖzellikGrubbsDixon QGeneralized ESD
Outlier sayısı1 (iteratif ile çok)1Çoklu (öncesi belirli)
Örneklem boyutun ≥ 33 ≤ n ≤ ~30n ≥ 10
Dağılım varsayımıNormalNormalNormal
Çoklu test düzeltmesiYok (yanlış)YokVar
Tipik kullanımTek outlier şüphesiLab/kalite kontrolProduction AD
HesaplamaHızlıÇok hızlıOrta
Pythonscipy + customcustomcustom veya
pyod.models.gmm

Pratik Karar#

  • n < 30 ve tek outlier şüphesi: Dixon Q
  • n ≥ 30 ve tek outlier şüphesi: Grubbs
  • Birden fazla outlier mümkün: ESD (max sayıyı tahmin et)
  • Normal dağılım varsayımı bozulmuş: Önce log/Box-Cox transform; ya da non-parametric yöntemlere (IQR, percentile) dön

P-Değer Kullanırken Dikkat#

Hipotez testleri p-değer üretir. Bunu üretimde kullanırken:

Tuzak 1: Çoklu Test Cehennemi#

1000 feature üzerinde tek tek Grubbs uygularsan, α=0.05 ile yaklaşık 50 false positive beklersin. Bonferroni veya Benjamini-Hochberg ile düzelt.

Tuzak 2: P-Hacking#

Birkaç farklı eşik dene, en düşük p-değeri ver. Yasaktır. Test öncesi α'yı sabitle.

Tuzak 3: P-Değer ≠ Outlier "Büyüklüğü"#

P=0.001 outlier, p=0.01 outlier'dan 10 kat daha büyük değildir. P-değer sadece "yanlış pozitif riski" söyler.

Tuzak 4: Anlamlı ≠ Önemli#

İstatistiksel anlamlı bir outlier, iş açısından önemli olmayabilir. Bir transaction p=0.001 ile outlier ama tutar 50 TL ise compliance açısından önemsiz olabilir.
📜 Compliance ipucu
BDDK ve EBA denetimlerinde 'modelimiz outlier tespit ediyor' demek yetmez; istatistiksel test ile desteklemen istenebilir. Bu durumda Grubbs veya ESD'nin formal raporu (test istatistiği, kritik değer, p-değer) bir compliance artefakt olarak değerlidir. Modül 30'da (Explainable AD) bu tür dokümantasyonu detaylı işleyeceğiz.

Modern AD'de Hipotez Testleri Hala Geçerli mi?#

Kısa cevap: evet, ama sınırlı.

Hala değerli yerleri#

  • Compliance / regulatory raporlama
  • Küçük örneklem (lab, kalite kontrol)
  • Tek-feature tarama (örn. her sensör için bağımsız test)
  • Modern ML modellerinin bypass edemediği baseline

Sınırları#

  • Yüksek-boyut zayıflığı: çoklu test cehennemi
  • Non-normal dağılım problemleri
  • Bağlam-bağımlılığı yok: müşteri profili gibi covariate'leri kullanamaz
  • Adversarial: attacker testin matematiğini öğrenip atlatabilir
Modern AD pipeline'ında klasik testler genellikle birinci geçit: hızlı, yorumlanabilir, ucuz. ML modelleri ikinci geçit: derin, bağlam-bağımlı, güçlü ama yavaş ve daha az yorumlanabilir.
👉 Bir sonraki ders
Ders 2.4 — Chebyshev, EVT, POT. Normal varsayımı geçerli olmayan, uç kuyruklu (Pareto-tail) verilerde outlier modellemek için Extreme Value Theory (EVT) ve Peak Over Threshold (POT) yöntemleri. Banking, network, telekom datasında baş aktör.

Sık Sorulan Sorular

Doğrudan yok, ama `pyod.models.gmm` veya `outliers` PyPI paketi var. Çoğu data scientist Grubbs'u 30 satırda kendi yazıyor (yukarıdaki gibi). scipy.stats.t kritik değeri verir, geri kalan formüller direkt.

Yorumlar & Soru-Cevap

(0)
Yorum yazmak için giriş yap.
Yorumlar yükleniyor...

İlgili İçerikler