Σ
SDCalc
중급개념·12 min

로버스트 통계: MAD, IQR, 이상치 내성 방법

중앙값 절대 편차(MAD)와 사분위수 범위(IQR)를 포함한 로버스트 통계 완벽 가이드. 이상치 내성 산포 측도를 언제 사용할지 예시와 Python 코드로 설명합니다.

왜 로버스트 통계인가?

표준편차는 강력한 산포 측도이지만 치명적인 약점이 있습니다: 이상치에 대한 극단적 민감성. 극단값 하나만으로도 SD가 급격히 부풀어 전형적인 변동에 대해 잘못된 그림을 제공할 수 있습니다.

로버스트 통계는 이상치의 영향에 저항하는 산포 측도를 제공하며, 측정 오류, 데이터 입력 실수, 또는 실제 극단 사례가 흔한 현실 데이터에서 필수적입니다.

예시: 이상치의 영향

데이터: 10, 12, 11, 13, 12, 11, 100 (이상치 하나) 표준편차: 32.4 (이상치에 지배됨) MAD: 1.0 (이상치를 무시함) IQR: 1.5 (이상치를 무시함)

붕괴점

통계량의 “붕괴점(breakdown point)”은 통계량이 무의미해지기 전에 극단값이 될 수 있는 데이터의 비율입니다. SD의 붕괴점은 0%(이상치 하나로도 무너짐)이고, MAD와 IQR의 붕괴점은 50%—데이터의 절반이 이상치여도 여전히 작동합니다.

중앙값 절대 편차 (MAD)

MAD는 가장 로버스트한 산포 측도입니다. 중앙값으로부터의 절대 편차의 중앙값을 계산합니다:

MAD 공식

MAD = median(|xᵢ - median(x)|)
1

중앙값 구하기

데이터의 중앙값을 계산합니다.
2

편차 계산

각 값에서 중앙값을 빼고 절대값을 취합니다.
3

MAD 구하기

이 절대 편차의 중앙값을 계산합니다.

MAD를 σ 추정에 활용: 정규분포 데이터에서 MAD ≈ 0.6745 × σ입니다. MAD에서 SD를 추정하려면 1.4826을 곱합니다:

MAD에서 SD 추정

σ̂ = 1.4826 × MAD

왜 1.4826인가?

이 환산 계수는 정규분포에서 MAD와 SD의 관계에서 유래합니다. 데이터가 정규분포일 때 환산된 MAD가 진짜 표준편차의 불편 추정량이 되도록 보장합니다.

사분위수 범위 (IQR)

IQR은 데이터의 중간 50%의 퍼짐—25번째와 75번째 백분위수 사이의 범위—를 측정합니다:

IQR 공식

IQR = Q3 - Q1 = 75번째 백분위수 - 25번째 백분위수

IQR은 이해하기 쉽고, 상자 그림에서 시각화하기 쉬우며, 일반적인 “1.5×IQR 규칙”으로 이상치 탐지의 기초가 되므로 널리 사용됩니다.

IQR에서 σ 추정: 정규분포 데이터에서 IQR ≈ 1.35 × σ입니다. IQR에서 SD를 추정하려면:

IQR에서 SD 추정

σ̂ = IQR / 1.35 ≈ 0.7413 × IQR

로버스트 측도 비교

표준편차

모든 데이터 사용 · 정규분포에서 가장 효율적 · 이상치에 매우 민감 · 붕괴점: 0%

MAD

가장 로버스트한 측도 · 중앙값 사용(평균 아님) · 이상치에 영향 없음 · 붕괴점: 50%

IQR

이해하기 쉬움 · 상자 그림에 사용 · 극단 50% 무시 · 붕괴점: 25%

로버스트 통계를 사용할 때

  • 탐색적 분석: 이상치의 존재 여부를 모를 때 로버스트 측도로 시작
  • 데이터 품질 문제: 데이터에 오류나 측정 문제가 있을 수 있을 때
  • 두꺼운 꼬리 분포: 극단값이 예상될 때 (금융 수익률, 보험 청구)
  • 소규모 표본: 적은 관측으로 인해 이상치의 영향이 과도할 때
  • 이상치 탐지: SD로 이상치를 탐지하는 것은 순환 논리; IQR이나 MAD를 대신 사용

구현 예시

Python
import numpy as np
from scipy import stats

def mad(data):
    """Median Absolute Deviation"""
    median = np.median(data)
    return np.median(np.abs(data - median))

def scaled_mad(data):
    """MAD scaled to estimate SD (for normal data)"""
    return 1.4826 * mad(data)

def iqr(data):
    """Interquartile Range"""
    return np.percentile(data, 75) - np.percentile(data, 25)

# Compare on data with outlier
data = [10, 12, 11, 13, 12, 11, 100]
print(f"SD: {np.std(data, ddof=1):.2f}")
print(f"MAD: {mad(data):.2f}")
print(f"Scaled MAD: {scaled_mad(data):.2f}")
print(f"IQR: {iqr(data):.2f}")