Skip to main content

결측치 대체

결측치 대체(Imputation)는 누락된 값을 합리적인 값으로 채우는 과정입니다. 단순 삭제는 정보 손실을, 부적절한 대체는 편향을 초래합니다. 데이터의 결측 패턴을 이해하고 적절한 대체 전략을 선택하는 것이 중요합니다.

학습 목표

  • 결측치의 유형(MCAR, MAR, MNAR)을 구분할 수 있다
  • SimpleImputer로 기본적인 대체를 수행할 수 있다
  • KNNImputer로 유사 데이터 기반 대체를 수행할 수 있다
  • IterativeImputer로 다변량 대체를 수행할 수 있다
  • 결측 패턴에 따라 적절한 전략을 선택할 수 있다

왜 중요한가

대부분의 ML 모델은 결측치를 처리할 수 없습니다. 결측치를 무조건 삭제하면 데이터가 크게 줄어들고, 평균으로 대체하면 분산이 줄어듭니다. Pandas 결측치 문서에서 기본 탐지와 처리를 배웠다면, 여기서는 sklearn의 고급 대체 전략을 다룹니다.

결측치 유형

유형설명예시대처
MCAR완전 무작위 결측설문지 실수로 빈칸삭제 또는 단순 대체
MAR조건부 무작위 결측고소득자가 소득 미응답조건부 대체
MNAR비무작위 결측심각한 질환자가 건강 설문 미응답도메인 지식 필요
import numpy as np
import pandas as pd
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer

np.random.seed(42)

# 예시 데이터 생성
n = 200
df = pd.DataFrame({
    'salary': np.random.normal(4500, 800, n),
    'experience': np.random.uniform(1, 20, n),
    'performance': np.random.normal(75, 10, n),
    'education_years': np.random.choice([12, 14, 16, 18], n)
})

# 결측치 삽입
df.loc[np.random.choice(n, 30, replace=False), 'salary'] = np.nan
df.loc[np.random.choice(n, 20, replace=False), 'performance'] = np.nan
df.loc[np.random.choice(n, 10, replace=False), 'education_years'] = np.nan

print("결측치 현황:")
print(df.isnull().sum())
print(f"\n완전한 행: {df.dropna().shape[0]}/{n}")

SimpleImputer — 기본 대체

# 평균 대체
mean_imputer = SimpleImputer(strategy='mean')
df_mean = pd.DataFrame(
    mean_imputer.fit_transform(df),
    columns=df.columns
)

# 중앙값 대체
median_imputer = SimpleImputer(strategy='median')
df_median = pd.DataFrame(
    median_imputer.fit_transform(df),
    columns=df.columns
)

# 최빈값 대체 (범주형에 적합)
mode_imputer = SimpleImputer(strategy='most_frequent')

# 상수 대체
const_imputer = SimpleImputer(strategy='constant', fill_value=-1)

# 대체 전후 비교
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].hist(df['salary'].dropna(), bins=20, alpha=0.5, label='원본', color='#4a9eca')
axes[0].hist(df_mean['salary'], bins=20, alpha=0.5, label='평균 대체', color='#e6a23c')
axes[0].set_title('salary: 평균 대체')
axes[0].legend()

axes[1].hist(df['salary'].dropna(), bins=20, alpha=0.5, label='원본', color='#4a9eca')
axes[1].hist(df_median['salary'], bins=20, alpha=0.5, label='중앙값 대체', color='#4a9e4a')
axes[1].set_title('salary: 중앙값 대체')
axes[1].legend()

plt.tight_layout()
plt.show()
전략적합한 상황장점단점
평균정규분포, 이상치 없음단순, 빠름이상치에 민감, 분산 축소
중앙값편향 분포, 이상치 있음이상치에 강건분산 축소
최빈값범주형 데이터범주형에 적합연속형에 부적절
상수결측 자체가 정보인 경우의도 명확임의적

KNNImputer — 유사 데이터 기반 대체

KNN Imputer는 결측값이 있는 행과 유사한 k개의 이웃을 찾아, 이웃들의 값으로 대체합니다.
# KNN Imputer
knn_imputer = KNNImputer(n_neighbors=5, weights='distance')
df_knn = pd.DataFrame(
    knn_imputer.fit_transform(df),
    columns=df.columns
)

# 대체 결과 비교
print("원본 기술통계:")
print(df.describe().round(1))
print("\nKNN 대체 기술통계:")
print(df_knn.describe().round(1))

# 시각화
fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(df['salary'].dropna(), bins=20, alpha=0.5, label='원본', color='#4a9eca')
ax.hist(df_knn['salary'], bins=20, alpha=0.5, label='KNN 대체', color='#e6a23c')
ax.set_title('salary: KNN 대체 결과')
ax.legend()
plt.tight_layout()
plt.show()
KNNImputer는 거리 기반이므로, 스케일이 다른 변수들은 사전에 스케일링을 해야 합니다. Pipeline에서 스케일링과 함께 사용하는 것이 권장됩니다.

IterativeImputer — 다변량 대체

IterativeImputer는 각 결측 피처를 다른 피처들로 모델링하여 대체합니다. MICE(Multiple Imputation by Chained Equations) 알고리즘을 구현합니다.
# IterativeImputer
iter_imputer = IterativeImputer(max_iter=10, random_state=42)
df_iter = pd.DataFrame(
    iter_imputer.fit_transform(df),
    columns=df.columns
)

# 세 가지 방법 비교
methods = {
    '평균': df_mean,
    'KNN': df_knn,
    'Iterative': df_iter
}

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, (name, df_imp) in zip(axes, methods.items()):
    # 대체된 값만 추출
    mask = df['salary'].isnull()
    ax.hist(df['salary'].dropna(), bins=20, alpha=0.5, label='원본', color='#4a9eca')
    ax.hist(df_imp.loc[mask, 'salary'], bins=10, alpha=0.7, label='대체값', color='#e6a23c')
    ax.set_title(f'{name} 대체')
    ax.legend()

plt.tight_layout()
plt.show()

전략 선택 가이드

결측 비율권장 전략
< 5%삭제 또는 SimpleImputer (평균/중앙값)
5~20%KNNImputer 또는 IterativeImputer
20~50%IterativeImputer + 결측 표시 피처
> 50%피처 삭제 고려, 또는 도메인 전문가 상담
# 결측 표시 피처 생성
from sklearn.impute import SimpleImputer

# 결측 여부를 별도 피처로 생성
df['salary_missing'] = df['salary'].isnull().astype(int)
print(f"결측 표시 피처 생성 완료: salary_missing")
print(df['salary_missing'].value_counts())

AI/ML에서의 활용

  • 파이프라인 통합: sklearn Pipeline에 Imputer를 포함하여 학습/예측 시 일관된 대체를 보장합니다
  • 결측 패턴 활용: 결측 여부 자체를 피처로 추가하면 모델 성능이 향상될 수 있습니다
  • 교차검증 안전: Pipeline 내에서 대체하면 데이터 누수를 방지합니다
  • 모델별 선택: 트리 기반 모델은 결측에 강건하여 단순 대체로도 충분한 경우가 많습니다
트리 기반 모델은 결측치를 내부적으로 처리할 수 있지만, 1) 다른 모델(선형, SVM 등)과 비교할 때 동일한 데이터가 필요하고, 2) 전처리 파이프라인의 일관성을 위해, 3) 결측 비율이 높으면 성능에 영향을 줄 수 있기 때문에 명시적 대체를 권장합니다.

체크리스트

  • MCAR, MAR, MNAR의 차이를 설명할 수 있다
  • SimpleImputer의 네 가지 전략을 적용할 수 있다
  • KNNImputer로 유사 데이터 기반 대체를 수행할 수 있다
  • IterativeImputer로 다변량 대체를 수행할 수 있다
  • 결측 비율에 따라 적절한 전략을 선택할 수 있다

다음 문서