Skip to main content

결측치 처리

결측치(Missing Value)는 실무 데이터에서 피할 수 없는 문제입니다. 센서 오류, 사용자 미입력, 시스템 장애 등 다양한 이유로 발생하며, 대부분의 ML 모델은 결측치를 처리하지 못합니다. 올바른 결측치 처리는 모델 성능에 직접적인 영향을 미칩니다.

학습 목표

  • 결측치를 탐지하고 패턴을 파악할 수 있다
  • 삭제 전략(dropna)의 기준을 설정할 수 있다
  • 대체 전략(fillna)을 상황에 맞게 선택할 수 있다
  • 결측치 처리가 분석 결과에 미치는 영향을 이해한다

왜 중요한가

결측치를 무시하면 통계량이 왜곡되고, 시각화가 불완전하며, 모델 학습이 실패할 수 있습니다. 반대로 잘못된 대체 값을 사용하면 데이터 분포가 변형되어 모델 성능이 저하됩니다. 결측치의 유형(MCAR, MAR, MNAR)을 파악하고 적절한 전략을 선택하는 것이 중요합니다.

결측치 탐지

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'name': ['김', '이', '박', '최', '정'],
    'age': [25, np.nan, 28, 35, np.nan],
    'salary': [4000, 4500, np.nan, 5200, 3800],
    'dept': ['개발', '영업', '개발', None, '마케팅']
})

# 결측치 확인
print(df.isna())           # 불리언 DataFrame
print(df.isna().sum())      # 열별 결측치 수
print(df.isna().mean())     # 열별 결측치 비율

# 결측치가 있는 행/열 확인
print(df[df['age'].isna()])  # age가 결측인 행

# 전체 결측치 요약
total = df.isna().sum()
percent = df.isna().mean() * 100
missing_info = pd.DataFrame({'결측수': total, '결측률(%)': percent})
print(missing_info[missing_info['결측수'] > 0])

삭제 전략 (dropna)

# 결측치가 하나라도 있는 행 삭제
df_dropped = df.dropna()

# 특정 열 기준으로 삭제
df_dropped = df.dropna(subset=['age', 'salary'])

# 모든 값이 결측인 행만 삭제
df_dropped = df.dropna(how='all')

# 비결측 값이 최소 N개인 행만 유지
df_dropped = df.dropna(thresh=3)  # 3개 이상의 값이 있는 행만

# 열 방향 삭제
df_dropped = df.dropna(axis=1)  # 결측치가 있는 열 전체 삭제
결측치 비율이 5% 이하인 경우에만 삭제를 고려하세요. 무분별한 삭제는 데이터 편향을 초래할 수 있습니다. 특히 결측치가 특정 패턴을 따르는 경우(예: 고소득자가 소득을 미입력) 삭제하면 분석 결과가 왜곡됩니다.

대체 전략 (fillna)

# 고정값 대체
df['age'] = df['age'].fillna(0)

# 평균/중앙값 대체 (수치형)
df['salary'] = df['salary'].fillna(df['salary'].mean())
df['salary'] = df['salary'].fillna(df['salary'].median())  # 이상치에 강건

# 최빈값 대체 (범주형)
df['dept'] = df['dept'].fillna(df['dept'].mode()[0])

# 앞/뒤 값으로 대체 (시계열)
df['value'] = df['value'].ffill()   # 앞의 값으로 (forward fill)
df['value'] = df['value'].bfill()   # 뒤의 값으로 (backward fill)

# 보간 (시계열)
df['value'] = df['value'].interpolate(method='linear')

# 그룹별 대체
df['salary'] = df.groupby('dept')['salary'].transform(
    lambda x: x.fillna(x.median())
)
방법적합한 상황장점단점
평균 대체정규분포 수치형단순, 빠름분산 축소, 이상치 영향
중앙값 대체편향된 수치형이상치에 강건분산 축소
최빈값 대체범주형단순빈도 편향 강화
전후 값 대체시계열연속성 유지비시계열에 부적합
그룹별 대체그룹 패턴 있을 때정확도 높음구현 복잡

AI/ML에서의 활용

  • 전처리 파이프라인: sklearn의 SimpleImputer로 결측치 처리를 자동화합니다
  • 피처 생성: 결측 여부 자체를 is_missing 피처로 활용할 수 있습니다
  • 데이터 품질 보고: 결측치 비율로 데이터 신뢰도를 평가합니다
  • 고급 대체: KNN Imputer, IterativeImputer는 전처리 섹션에서 다룹니다
np.nan은 float 타입의 결측치이고, None은 Python 객체입니다. Pandas에서는 둘 다 결측치로 인식하지만, 수치형 열에서는 np.nan, 문자열/객체 열에서는 None이 자연스럽습니다. Pandas 2.0+에서는 pd.NA가 모든 타입의 범용 결측치로 도입되었습니다.
일부 모델(XGBoost, LightGBM)은 결측치를 자체적으로 처리할 수 있습니다. 이 경우 무리한 대체보다 결측치를 그대로 두는 것이 더 나은 성능을 보이기도 합니다.

체크리스트

  • isna().sum()으로 열별 결측치를 파악할 수 있다
  • dropna의 how, subset, thresh 파라미터를 활용할 수 있다
  • 수치형에는 평균/중앙값, 범주형에는 최빈값 대체를 적용할 수 있다
  • 시계열 데이터에 ffill/bfill/interpolate를 사용할 수 있다
  • 그룹별 대체를 수행할 수 있다

다음 문서