데이터를 분석할 때 이상치(Outlier) 는 반드시 마주치는 문제입니다.
특히 병원 환자 데이터처럼 복잡하고 개인 차이가 큰 데이터를 다룰 때는,
수치만 보고 단순히 제거하는 것은 매우 위험합니다. 🚨
이번 글에서는 단변량(univariable) 이상치 처리부터
전문가 협의 후 유지해야 하는 경우,
그리고 다변량(multivariable) 이상치 탐지까지
실제 현장에서 쓸 수 있는 깊이로 정리합니다. 🚀
1. 이상치란 무엇인가요? 🤔
이상치(Outlier) 는 대부분의 데이터 패턴과 크게 동떨어진 값을 의미합니다.
발생 원인은 다양합니다.
원인 | 설명 |
입력 오류 | 오타, 센서 오류 등 |
극단적 사건 | 금융 위기, 감염병 폭발 등 |
희귀 이벤트 | 드문 특수 사례 (ex. 100세 이상 고령자) |
개인 특성 | 중증 질환자, 특수 환자군 |
👉 중요한 점 : 이상치는 무조건 제거할 대상이 아닙니다.
때로는 핵심 인사이트가 숨겨진 곳이기도 합니다.
2. 단변량(Univariable) 이상치 탐지 방법 📈
(1) 수치적 탐지
- Z-Score 방법
- 평균에서 몇 표준편차 떨어져 있는지 계산합니다.
- 일반적으로 |Z| > 3이면 이상치로 간주합니다.
- 정규분포 가정이 약하면 주의해야 합니다.
import numpy as np
import pandas as pd
from scipy.stats import zscore
# 예시 데이터 (혈압 데이터)
bp = np.array([120, 125, 130, 128, 122, 118, 250]) # 250은 이상치
# 데이터프레임 변환
df = pd.DataFrame({'bp': bp})
# Z-Score 계산
df['z_score'] = zscore(df['bp'])
# 이상치 여부
df['outlier'] = df['z_score'].abs() > 3
print(df)
- IQR 방법
- IQR 방법은 정규성을 가정하지 않고도 이상치를 탐지할 수 있습니다.
- 사분위수(Q1, Q3)를 이용하여 IQR = Q3 - Q1 계산
- 이상치 하한 = Q1 - 1.5 × IQR
- 이상치 상한 = Q3 + 1.5 × IQR
# IQR 계산
Q1 = df['bp'].quantile(0.25)
Q3 = df['bp'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 이상치 여부
df['outlier_IQR'] = (df['bp'] < lower_bound) | (df['bp'] > upper_bound)
print(df)
(2) 시각적 탐지
- Boxplot (상자그림) 📦

- Scatter Plot (산점도) 🎯

- Histogram, Density Plot 📊

👉 빠르게 분포를 확인하고, 비정상적인 꼬리(Tail)나 도트(Outlier Point)를 식별할 수 있습니다.
3. 단변량 이상치 처리 방법 ✍️
(1) 제거(Remove)
- 명백한 입력 오류나 잘못된 데이터는 제거합니다.
(2) 대체(Imputation)
- 중앙값(Median)이나 KNN Imputer 등으로 대체합니다.
- 그룹별 대체도 가능합니다 (예: 연령대별 중앙값).
(3) 변환(Transformation)
- 로그(Log), 제곱근(Sqrt) 변환으로 이상치 영향을 완화합니다.
(4) 강건 모델 사용
- 트리 기반 모델(Random Forest, XGBoost 등)을 사용하면 이상치 영향이 적습니다.
4. 전문가 협의 후 “정상”으로 판정된 이상치 처리 방법 🏥
실제 병원 환자 데이터를 분석할 때,
수치적으로는 이상치처럼 보여도
전문가 협의 결과 정상으로 판정되는 경우가 많습니다.
예시
- 나이 95세 + 혈압 170 → 수치로는 이상하지만 고령 환자군에서는 정상 범위.
처리 방법 | 설명 |
데이터 유지 | 해당 환자 데이터를 삭제하지 않고 그대로 사용합니다. |
메타데이터 추가 | “전문가 검토 완료” 플래그를 추가합니다. |
Feature 확장 | 고령 여부, 중증 기저질환 여부 등의 변수를 추가합니다. |
모델 설계 조정 | 고령/질병군을 고려한 분석 설계(subgroup 분석)도 고려합니다. |
✅ 중요한 것은 맥락(Context)을 반영하는 것입니다.
5. 다변량(Multivariable) 이상치 탐지 방법 🔎
Univariable 기준으로 튀어 보이는 값도,
Multivariable 관계 안에서는 정상적일 수 있습니다.
상황 설정
- 환자 데이터 : 나이, 혈압, 혈당
- 90세 고령 환자 혈압 170, 혈당 150
- Univariable로 보면 이상치처럼 보임
- 전문가 협의 결과 90세 고령 환자에서는 자연스러운 수준
→ 의학적으로 정상
“변수 간 관계를 무시하면 진짜 이상치를 놓칠 수 있습니다.”
⭐ 주요 다변량 이상치 탐지 방법
(1) Mahalanobis Distance
- 평균 벡터, 공분산 행렬을 고려하여 거리 계산
- 거리값이 크면 다변량 이상치로 간주
- 통계적으로 카이제곱 분포를 사용해 임계값 설정
from scipy.spatial.distance import mahalanobis
from numpy.linalg import inv
# 예시 데이터 (혈압, 혈당)
data = np.array([
[120, 90],
[125, 92],
[130, 95],
[128, 93],
[122, 91],
[118, 89],
[250, 300] # 이상치
])
df_mv = pd.DataFrame(data, columns=['bp', 'glucose'])
# 평균과 공분산 계산
mean_vec = df_mv.mean().values
cov_mat = np.cov(df_mv.values, rowvar=False)
inv_covmat = inv(cov_mat)
# Mahalanobis 거리 계산
df_mv['mahalanobis'] = df_mv.apply(lambda x: mahalanobis(x, mean_vec, inv_covmat), axis=1)
# 거리 기준 이상치 여부 (임계값은 데이터 상황에 따라 결정)
threshold = 3 # 예시
df_mv['outlier'] = df_mv['mahalanobis'] > threshold
print(df_mv)
(2) Robust Mahalanobis Distance
- 이상치에 강건한(Robust) 공분산 추정을 사용
- 데이터가 오염된 경우에도 안정적인 이상치 탐지 가능
import numpy as np
import pandas as pd
from sklearn.covariance import MinCovDet
from scipy.spatial.distance import mahalanobis
# 예시 데이터 (혈압, 혈당)
data = np.array([
[120, 90],
[125, 92],
[130, 95],
[128, 93],
[122, 91],
[118, 89],
[250, 300] # 이상치
])
df = pd.DataFrame(data, columns=['bp', 'glucose'])
# Robust 공분산 추정 (MCD)
mcd = MinCovDet().fit(df)
# Robust Mahalanobis Distance 계산
distances = mcd.mahalanobis(df)
# 결과 저장
df['robust_mahalanobis'] = distances
# 거리 기준 이상치 판단 (카이제곱 분포 기준 임계값 사용 가능)
threshold = np.percentile(distances, 97.5) # 예: 상위 2.5% 거리
df['outlier'] = df['robust_mahalanobis'] > threshold
print(df)
(3) 머신러닝 기반
- 🌲 Isolation Forest
- 이상치는 다른 데이터보다 무작위 분할로 더 빨리 고립됩니다.
* 랜덤으로 변수와 임계값 선택하여 데이터를 둘로 나누고, 또 랜덤으로 변수와 임계값 선택하여 나누고 반복 - 고차원 데이터에서도 잘 작동합니다.
- ✅ 설명
- contamination은 이상치 비율을 미리 알려주는 하이퍼파라미터입니다.
- -1로 나온 샘플이 이상치입니다.
- 🚫 주의
- contamination 설정이 너무 높거나 낮으면 성능이 떨어질 수 있습니다.
- ✅ 설명
from sklearn.ensemble import IsolationForest
# Isolation Forest 모델 훈련
iso_forest = IsolationForest(contamination=0.1, random_state=42)
df['anomaly_score'] = iso_forest.fit_predict(df[['bp', 'glucose']])
# -1: 이상치, 1: 정상
df['is_outlier'] = df['anomaly_score'] == -1
print(df)
🔍 One-Class SVM
- 정상 데이터 주위에 경계를 학습하고, 그 바깥을 이상치로 간주합니다.
- 비선형 경계도 학습할 수 있습니다.
- 고차원, 복잡한 분포 데이터에 적합합니다.
- ✅ 설명
- nu: 이상치 비율에 대한 상한 (0 ~ 1)
- gamma: 커널 함수의 스케일 (auto로 데이터 크기에 맞게 설정)
- 🚫 주의
- 스케일이 다른 변수들은 반드시 StandardScaler나 RobustScaler로 정규화한 뒤 사용해야 합니다.
- ✅ 설명
from sklearn.svm import OneClassSVM
# One-Class SVM 모델 훈련
oc_svm = OneClassSVM(kernel='rbf', nu=0.1, gamma='auto')
df['ocsvm_score'] = oc_svm.fit_predict(df[['bp', 'glucose']])
# -1: 이상치, 1: 정상
df['is_outlier_ocsvm'] = df['ocsvm_score'] == -1
print(df)
💡 Autoencoder (Reconstruction Error)
- Autoencoder는 입력을 압축했다가 복원하는 신경망입니다.
* 원본 데이터를 요약(압축)하고 요약한 걸 가지고 다시 복원하는 과정에서 데이터 속에 숨어있는 중요 특징을 자동 학습하는 신경망 - 정상 데이터는 복원 에러가 작고, 이상치는 복원 에러가 큽니다.
- 복잡하고 고차원인 데이터에도 적용 가능합니다.
- ✅ 설명
- 정상 데이터는 재구성 오류(MSE)가 작음.
- 이상치는 재구성 오류가 큽니다.
- 오류가 큰 샘플을 이상치로 탐지합니다.
- 🚫 주의
- 학습 데이터에 심각한 이상치가 많으면 Autoencoder도 훈련이 왜곡될 수 있습니다.
- 가능하면 “클린한” 학습 데이터셋을 사용하는 것이 좋습니다.
- ✅ 설명
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense
# 데이터 정규화 (Autoencoder는 반드시 정규화 필요)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df[['bp', 'glucose']])
# Autoencoder 모델 구성
input_dim = X_scaled.shape[1]
encoding_dim = 2
input_layer = Input(shape=(input_dim,))
encoder = Dense(encoding_dim, activation="relu")(input_layer)
decoder = Dense(input_dim, activation="linear")(encoder)
autoencoder = Model(inputs=input_layer, outputs=decoder)
# 컴파일 및 학습
autoencoder.compile(optimizer='adam', loss='mse')
autoencoder.fit(X_scaled, X_scaled, epochs=100, batch_size=2, verbose=0)
# Reconstruction Error 계산
reconstructed = autoencoder.predict(X_scaled)
mse = np.mean(np.power(X_scaled - reconstructed, 2), axis=1)
# 이상치 여부 판단 (예: 상위 5% 이상을 이상치로 간주)
threshold = np.percentile(mse, 95)
df['reconstruction_error'] = mse
df['is_outlier_autoencoder'] = df['reconstruction_error'] > threshold
print(df)
6. Multivariable 이상치 처리 방법 ✍️
단계 | 방법 |
1 | Mahalanobis Distance 또는 ML 기법으로 다변량 이상치 탐지 |
2 | 이상치 후보를 분리하여 전문가 협의 |
3 | 진짜 이상치 vs 정상적 특성 구분 |
4 | 제거, 변환, 별도 관리 등 상황에 맞는 전략 선택 |
7. 실무 적용 체크리스트 🛠️
체크포인트 | 설명 |
원인 분석 | 단순 입력 오류인지, 특수 사례인지 구분합니다. |
맥락 고려 | 고령, 중증질환 등 특수 상황을 반영합니다. |
문서화 | 이상치 탐지 및 처리 방법을 명시하고 재현 가능하게 합니다. |
성능 비교 | 이상치 처리 전후 모델 성능 변화를 검토합니다. |
전문가 협의 | 분석 방향성 결정 전에 반드시 전문가 의견을 듣습니다. |
✨ 마무리
이상치는 제거 대상이 아니라, 이해하고 다루어야 할 존재입니다.
✅ 단변량 이상치 탐지는 기본,
✅ 전문가 협의로 맥락 반영,
✅ 다변량 구조까지 고려하는 것,
이것이 진짜 실무형 이상치 처리입니다. 🚀
“데이터를 정리하는 것은 세상을 더 잘 이해하려는 노력입니다.” 🌍
'데이터분석' 카테고리의 다른 글
📊 의료 논문에서 꼭 나오는 통계 지표 9가지 완전 정복 (0) | 2025.05.20 |
---|---|
[생존분석] RMST vs. AFT 모델 완전 정리 (2) | 2025.05.16 |
[데이터분석] 📚 결측치 처리와 인과추론, 함께 이해하기 (0) | 2025.04.26 |
[데이터분석]🧹 결측, 그 빈칸을 채우는 법 (3) | 2025.04.25 |
[메타 분석] (Meta-analysis) 완벽 정리 가이드📚 (0) | 2025.04.21 |