개발/Python

Python. IQR을 이용한 이상치 판정

워로디스 2026. 6. 19. 22:48

1. 전체 코드

def remove_outliers_iqr(df, cols=None, k=1.5):
    """
    IQR 기준으로 이상치를 제거하는 함수

    df   : pandas DataFrame
    cols : 이상치를 검사할 컬럼 목록. None이면 모든 수치형 컬럼에 적용
    k    : IQR에 곱할 계수. 일반적으로 1.5 사용
    """
    if cols is None:
        cols = df.select_dtypes(include="number").columns

    Q1 = df[cols].quantile(0.25)
    Q3 = df[cols].quantile(0.75)
    IQR = Q3 - Q1

    lower = Q1 - k * IQR
    upper = Q3 + k * IQR

    mask = ((df[cols] >= lower) & (df[cols] <= upper)).all(axis=1)

    return df[mask]

사용 예시:

df_clean = remove_outliers_iqr(df)

특정 컬럼만 검사하려면:

df_clean = remove_outliers_iqr(df, cols=["age", "income", "score"])

2. IQR이란?

IQR은 Interquartile Range, 즉 사분위 범위다.

IQR = Q3 - Q1

각 값의 의미는 다음과 같다.

Q1: 하위 25% 지점
Q2: 중앙값
Q3: 하위 75% 지점
IQR: 데이터의 가운데 50%가 퍼져 있는 범위

즉, IQR은 데이터의 중심이 아니라 퍼짐 정도를 나타낸다.

3. 왜 Q2가 아니라 Q3 - Q1을 쓰는가?

Q2는 중앙값이므로 데이터의 중심만 알려준다.

예를 들어:

A = [49, 50, 51]
B = [0, 50, 100]

두 데이터 모두 Q2는 50이다.

하지만 A는 50 근처에 좁게 모여 있고, B는 훨씬 넓게 퍼져 있다. 따라서 이상치 기준도 다르게 잡아야 한다.

이상치 판정에는 중심값뿐만 아니라, 데이터가 원래 얼마나 퍼져 있는지를 나타내는 퍼짐 기준이 필요하다. 그래서 Q2 하나가 아니라 Q3 - Q1을 사용한다.

4. IQR 계산 예시

pandas 기본 방식처럼 선형 보간을 사용하면 다음과 같다.

데이터 Q1 Q2 Q3 IQR
[49, 50, 51] 49.5 50 50.5 1
[0, 50, 100] 25 50 75 50

두 데이터 모두 중앙값은 같지만, IQR은 크게 다르다.

즉, B는 A보다 훨씬 넓게 퍼져 있으므로 이상치 기준도 더 넓게 잡힌다.

5. 이상치 판정 기준

일반적으로 IQR 방식에서는 다음 범위를 정상 범위로 본다.

Lower = Q1 - 1.5 * IQR
Upper = Q3 + 1.5 * IQR

즉, 값 x가 아래 범위 안에 있으면 정상으로 본다.

Lower <= x <= Upper

이 범위를 벗어나면 이상치 후보로 판단한다.

x < Lower 또는 x > Upper

6. IQR * 1.5의 의미

IQR * 1.5는 데이터의 퍼짐 정도를 반영해 이상치 기준을 조정하는 장치다.

IQR이 작다
→ 데이터가 좁게 모여 있음
→ 이상치 기준이 엄격해짐

IQR이 크다
→ 데이터가 넓게 퍼져 있음
→ 이상치 기준이 완화됨

예를 들어 A의 경우:

A = [49, 50, 51]

Q1 = 49.5
Q3 = 50.5
IQR = 1

Lower = 49.5 - 1.5 * 1 = 48
Upper = 50.5 + 1.5 * 1 = 52

정상 범위: 48 ~ 52

B의 경우:

B = [0, 50, 100]

Q1 = 25
Q3 = 75
IQR = 50

Lower = 25 - 1.5 * 50 = -50
Upper = 75 + 1.5 * 50 = 150

정상 범위: -50 ~ 150

즉, 데이터가 더 넓게 퍼져 있으면 이상치 기준도 더 완화된다.

7. 한 컬럼 기준으로만 적용하기

col = "score"

Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1

lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR

df_clean = df[df[col].between(lower, upper)]

between(lower, upper)는 다음 조건과 같다.

(df[col] >= lower) & (df[col] <= upper)

8. 제거 대신 클리핑하기

이상치를 삭제하지 않고 상한·하한 안으로 제한할 수도 있다.

col = "score"

Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1

lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR

df[col] = df[col].clip(lower, upper)

이 경우 상한보다 큰 값은 상한값으로, 하한보다 작은 값은 하한값으로 바뀐다.

9. 주의할 점

IQR 방식은 간단하고 직관적이지만, 모든 상황에서 이상적인 방법은 아니다.

특히 다음 경우에는 주의해야 한다.

  • 데이터가 원래 한쪽으로 치우친 분포일 때
  • 매출, 소득, 방문자 수처럼 큰 값 자체가 중요한 의미를 가질 때
  • 이상치처럼 보이는 값이 실제로는 중요한 이벤트일 때
  • 여러 컬럼에 동시에 적용하면서 행이 지나치게 많이 제거될 때

따라서 IQR 방식은 이상치를 무조건 제거하는 도구라기보다는, 이상치 후보를 찾는 기준으로 보는 것이 좋다.

10. 정리

IQR 이상치 판정은 다음 질문에 답하는 방법이다.

데이터가 원래 퍼져 있는 정도를 고려했을 때,
이 값은 지나치게 멀리 떨어져 있는가?

핵심 공식은 다음과 같다.

IQR = Q3 - Q1

Lower = Q1 - 1.5 * IQR
Upper = Q3 + 1.5 * IQR

정상 범위:
Lower <= x <= Upper

이상치 후보:
x < Lower 또는 x > Upper

Q2는 중심을 나타내지만, 이상치 판정에는 퍼짐 기준이 필요하다. 그래서 Q2만 쓰지 않고 Q3 - Q1, 즉 IQR을 사용한다.

반응형