[데이터분석]🧹 결측, 그 빈칸을 채우는 법

 

― 이대로만 해도 ‘그나마’ 안 망한다 ―

 

데이터 분석을 하다 보면 이런 말 자주 듣습니다.

 

“아 그거 결측 많아요. 그냥 평균으로 넣었어요.”
“결측치 많아서 모델이 안 돌아가요.”
“삭제했는데 성능 왜 이러죠?”

 

결측치는 분석가의 발목을 붙잡는 은근한 복병입니다.
실제 업무에선 단순히 "채운다"나 "지운다"로는 해결 안 됩니다.
결측이 왜 생겼고, 어떤 종류고, 무엇을 위해 다루는지에 따라
전략이 완전히 달라져야 합니다.

 

 


 

1️⃣ 실무에서 마주치는 결측치, 이건 알아야 합니다

 

종류 설명 예시 실무적 함의
MCAR (Missing Completely at Random) 완전히 무작위 결측 설문 중 실수로 빠뜨림 제거해도 영향 적음
MAR (Missing at Random) 특정 변수에 따라 결측 확률 다름 나이 많은 응답자가 소득을 잘 안 씀 보정 가능, 조심 필요
MNAR (Missing Not at Random) 결측 자체가 의미 있음 소득이 높거나 낮을수록 의도적으로 무응답 삭제하면 바이어스 발생

 

💡 포인트:
결측은 단순한 '빈칸'이 아니라, 분석을 왜곡할 구조적 신호입니다.
그걸 무시하면 모델도 해석도 망가집니다.

 

 


 

2️⃣ 실무에서 생각할 것:

 

‘어떻게 채울까?’보다 ‘왜 채우는가’

 

- 통계 보고용인가? → 단순 대체도 OK (e.g., mean, median)

- 예측 모델인가? → 예측 성능 중심 (모델 기반 대체)

- 인과 추정 또는 해석 중심 모델인가? → 결측이 bias를 만들 수 있음. Multiple Imputation 필수

 

❗ 평균 넣으면? → 분산 줄고, 계수 왜곡, p-value 망가지고,
결과는 믿을 수 없게 됩니다.

 

 


 

3️⃣ 실무에서 자주 쓰는 결측치 처리 전략

 

전략 사용 시점 실전 코드 주의사항
삭제(drop) 결측 비율 5% 이하, MCAR 추정 시 df.dropna(subset=['col']) 정보 손실, 표본 감소
단순 대체 통계 보고용, 단순 분포일 때 SimpleImputer(strategy='mean') 분포 왜곡, bias 가능
시계열 보간 순서 있는 시계열 데이터 df['col'].interpolate(method='time') 외삽 주의
KNN 보간 변수 간 거리로 대체 가능 시 KNNImputer(n_neighbors=5) 스케일링 필수
다중 대체 (Multiple Imputation) 해석, 인과추론 시 IterativeImputer() 또는 R mice() 수렴 확인, 다중 분석 필요

 

 


 

4️⃣ 실무자의 체크리스트 ✅

 

- 결측치가 무작위인가? (MCAR or MAR or MNAR)

- 해당 변수는 모델의 핵심 변수인가?

- 결측 처리 방식이 해석에 어떤 영향을 미칠까?

- 결측 처리가 파이프라인 어디에 위치하는가? (스케일링, 인코딩 전후)

- 모델 목적은 예측인가 해석인가?

 

 


 

🔍 결측치가 무작위인가? (MCAR or MAR or MNAR)

→ 이건 통계적 판단 + 도메인 해석의 조합이 필요합니다.

결론부터 말하면, 100% 자동으로 판단해주는 검사는 없습니다.
하지만, 실무에서는 다음 3가지 단계적 접근으로 꽤 합리적인 판단이 가능합니다.

 

✅ Step 1. MCAR 여부 판단: 무작위 결측인지 검정

👉 Little’s MCAR Test

- 데이터셋 전반에서 결측이 완전히 무작위(MCAR)인지 검정

- 귀무가설: 결측이 무작위로 발생했다

- 유의수준 이하 → MCAR 아님

 

📌 현실 팁:

- 이 테스트에서 p > 0.05 → MCAR일 수 있음

- 하지만 완전 무작위인 경우는 거의 없습니다. → 대체로 MAR 또는 MNAR임

 

✅ Step 2. MAR 여부 판단: 다른 변수와 관계 있는가?

"결측 발생이 다른 변수에 따라 달라지는가?" 를 살펴보면 MAR 여부를 추정할 수 있습니다.

 

📊 예: 결측 마스크를 만들어 분석

- 특정 변수(col2)에 따라 결측 발생(col1_missing)이 차이가 나면 → MAR 가능성↑

- col1_missing ~ col2 + col3 로 로지스틱 회귀해도 좋습니다

 

🧠 실무 감각 팁

- 결측률이 특정 연령대, 성별, 지역 등에서 집중 → MAR 가능성

- 결측이 데이터 수집 방식, 응답 행태와 연결 → MAR 가능성

df['col1_missing'] = df['col1'].isnull().astype(int)
sns.boxplot(x='col1_missing', y='col2', data=df)

 

✅ Step 3. MNAR 여부 탐지: 결측 자체가 의미 있는가?

MNAR은 가장 까다롭습니다.
결측된 값이 클수록, 또는 작을수록 결측이 더 잘 발생하는 경우입니다.

 

📌 방법

- 분석가의 추론, 도메인 지식, 수집 맥락 파악이 핵심입니다

- 예: 소득 높은 사람이 소득 공개를 꺼린다 → 높은 값일수록 결측

- 예: 체중이 급격히 줄어든 환자가 탈락했다 → MNAR 가능

 

🧠 실무 감각 팁

- 결측 자체를 더미 변수로 만들어 분석에 포함
→ 결측인 사람과 아닌 사람 간 outcome 차이가 유의하다면
→ MNAR일 가능성 있음

df['missing_flag'] = df['col'].isnull().astype(int)
sns.histplot(data=df, x='outcome', hue='missing_flag')

 

 


 

🧩 해당 변수가 모델의 핵심 변수인가?

 

구분 설명 처리 전략 실무 예시
핵심 변수 종속변수와 강한 관련성, 해석에 필수 결측 제거보단 보존 우선. 모델 기반 대체 고려 소득, 나이, 질병 진단 여부 등
주요 아님 예측 기여도 낮고, 보조 변수 수준 결측 비율 높다면 삭제 고려 가능 보조 설명 변수, 설문 부가 질문 등

 

🛠️ 실무 팁:
변수 중요도는 모델 feature importance, VIF, 상관분석, 또는 현업과의 협의를 통해 결정하는 게 좋습니다.

 

🔍 “핵심 변수일수록, 그 값이 비어 있다면 그 행(row)을 아예 제외해야 하지 않나?”

👉 MCAR(무작위 결측)이라면, 제거해도 bias 없으나

MAR/MNAR(체계적 결측)이라면, 제거는 위험하며 selection bias(선택 편항) 발생 가능

👉 분석 목적이 예측/성능 중심이라면, 제거해도 괜찮으나

분석 목적이 인과 해석 중심이라면, 결측 자체가 인과 경로에 포함될 수 있어 보존이 우선임
(즉, 인과 추론에서 Missingness(결측)이 하나의 변수처럼 작용할 수 있음!)

 

 


 

🎯 결측 처리 방식이 해석에 어떤 영향을 미치는가?

 

처리 방식 해석에 미치는 영향 해석 중심 분석 시 권장 여부
삭제 표본 수 감소 → 통계적 검정력 낮아짐
단순 대체 (평균/중앙값) 분산 감소 → 계수 및 p-value 왜곡
모델 기반 대체 (KNN 등) 공분산 구조 왜곡 가능 ⚠️ 사용 시 주의
Multiple Imputation (다중 대체) 표준 오차, 신뢰구간 보존 가능 ✅ 권장
결측 자체를 변수화 결측 자체의 효과 반영 가능 ✅ 유용함 (특히 MNAR 추정 시)

 

📌 모델 해석이 중요한 경우,
반드시 결측 처리 이후 회귀계수, 표준오차가 어떻게 변했는지 비교해야 합니다.
단순히 AUC만 높다고 좋은 게 아닙니다.

 

 


 

🔄 결측 처리가 파이프라인 어디에 위치하는가?

 

처리 위치 적절 시점 이유
데이터 분할 전 처리 ❌ 비추천 테스트 데이터 정보 누출 가능성 (data leakage)
데이터 분할 후, 학습 데이터만으로 imputer 학습 ✅ 권장 누수 방지, 현실 반영
스케일링/인코딩과의 순서 일반적으로 결측 처리 → 인코딩/스케일링 결측이 존재하면 인코딩/스케일링이 오류 발생
 

💡 파이프라인 구성 예시

# 파이프라인 구성 예시
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='mean')),  # 먼저 결측 처리
    ('scaler', StandardScaler())                  # 그 다음 스케일링
])
 

 

🧠 모델의 목적이 예측인가, 해석인가?

 

목적 결측 처리 전략 평가 방식 실무 사례
예측(Prediction) 성능 향상이 목적 → KNN, Iterative, 임의값 대체 등 가능 AUC, RMSE, F1 등 보험사 고객 이탈 예측
해석(Interpretation) 계수/효과 추정이 목적 → MI, indicator 사용 계수 해석, p-value, 신뢰구간 정책 효과 분석, 논문, 공공 보고

 

⚠️ 해석 중심 분석에서 예측 기반 대체법 사용 시, bias로 인해
"정책이 효과 있다"고 잘못 해석할 수 있습니다.

 

 

 


 

✨ 최종 요약: 실무에서의 결측 전략 결정 플로우

[결측치 발생]
       ↓
"이 결측이 무작위인가?" → MCAR? MAR? MNAR?
       ↓
"해당 변수 중요도는?" → 핵심이면 삭제보다 보존
       ↓
"모델 목적은?" → 예측인가? 해석인가?
       ↓
"어디서 처리할까?" → 데이터 분할 후, 파이프라인 내에서
       ↓
[적절한 전략 선택: 삭제, 단순 대체, KNN, MI 등]
       ↓
"결측 처리 이후 해석에 어떤 변화가 생겼는가?" → 회귀 계수, 오차 확인

 


 

Q1 - 결측치와 예측 성능 간의 trade-off, 어떻게 측정할까?

결측을 어떻게 다루느냐에 따라 예측 성능이 요동칩니다.
이를 정량적으로 측정하려면 이렇게 접근하세요:

 

 

🔬 Step-by-step 평가 방법

 

1. baseline 모델

→ 결측치 제거 후 학습

2. 대체 전략1~N 적용

→ 각각 대체한 후 동일 모델 학습

3. 각 모델의 성능 비교

- 회귀: RMSE, R², MAE

- 분류: Accuracy, AUC, F1 Score, PR AUC

4. 차이가 크다면?
→ "결측 처리 전략 자체가 모델 성능을 왜곡할 수 있다"는 신호

5. 검증용 외부 데이터나 교차검증 사용 권장

 

📌 실제 예시:

# 결측 처리별 성능 비교 예시
for imputer in imputers:
	X_imputed = imputer.fit_transform(X)
    model.fit(X_imputed, y)
    y_pred = model.predict(X_test)
    print(imputer.__class__.__name__, roc_auc_score(y_test, y_pred))

 

결론:
예측 성능이 중요하다면, 결측 처리도 튜닝 대상입니다.
단, 과적합 되지 않도록 교차검증은 필수입니다.

 

 


 

 

Q2 - 인과추론 및 해석이 중요한 경우, 어떤 방식이 적절할까?

해석이 목적이라면 반드시 다음 중 하나를 고려하세요:

 

✅ 1. Multiple Imputation (다중 대체)

🔁 "하나의 결측에 대해 여러 개의 그럴듯한 값들을 채워서, 결과의 불확실성까지 고려하는 방식"

(결측이 다른 변수들과 관련 있을 때(MAR), 해석의 정확성과 인과 분석이 중요할 때)

 

📌 핵심:

- 데이터를 여러 개(보통 5~10개)로 복제하고 결측값을 조금씩 다르게 채움

- 각 데이터셋에서 모델 적합 후 각각 분석

- 회귀 계수, 분산 등 결과를 모은 후 Rubin's Rule를 통해 종합된 추정값과 신뢰구간 계산

 

🔍 Rubin's Rule:

여러 개의 Multiple Imputation 분석 결과를 하나의 해석 가능한 결과로 결합

회귀계수는 평균 내고, 표준 오차(총 분산)은 각 분석의 오차의 평균과 분석 결과 간 오차를 함께 고려

(불확실성을 반영한 robust한 해석 가능)

 

📌 Python 예시: (단일 대체지만 방식 유사, R의 mice()가 더 정석적)

from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
import pandas as pd

# 예시 데이터
df = pd.read_csv("data.csv")

# 다중 대체
imp = IterativeImputer(random_state=0, max_iter=10)
df_imp = imp.fit_transform(df)

# 다시 데이터프레임으로
df_imp = pd.DataFrame(df_imp, columns=df.columns)

 

2. 결측 자체를 분석 변수로 포함 (Missing Indicator Method)

🧭 “결측 여부 자체를 정보로 본다”

즉, 어떤 값이 비어 있었다는 사실 그 자체가 의미 있다고 판단할 때 쓰는 방식

(결측이 단순 실수나 우연이 아닌 경우(MNAR), 가령 소득 낮은 사람이 소득을 숨겼거나 건강이 좋지 않아 검사를 못 받음)

 

📌 핵심:

- 원래 변수 X1을 더미화하여 X1_missing을 만들고,
X1_missing이 1이면 "이 사람은 X1이 비어 있었던 사람"

- 회귀 계수에서 X1_missing 값이 유의하면,

결측 자체가 outcome에 영향을 줄 수 있다는 뜻!

 

📌 Python 예시:

# 1. 결측 여부 변수화
df['X1_missing'] = df['X1'].isnull().astype(int)

# 2. 원래 변수 X1은 단순 대체 (예: 평균으로)
from sklearn.impute import SimpleImputer
imp = SimpleImputer(strategy='mean')
df['X1'] = imp.fit_transform(df[['X1']])

 

✅ 3. Sensitivity Analysis (결측 편향 민감도 분석)

❗ "결측이 다른 방식으로 채워졌다면 결과가 얼마나 달라졌을까?"를 평가

(결측이 MNAR일 가능성이 높거나, 어떤 결측 처리 방법을 쓸지 확신이 없을 때,

분석 결과의 신뢰성 한계까지 파악하고 싶을 때)

 

📌 핵심:

- 결측이 어떻게 생겼냐고 가정하느냐에 따라 결과가 어떻게 달라지는지 실험해보는 것

- 가정이 바뀌었을 때 결과가 뒤집힌다면, 민감도가 높다는 뜻이므로 해석에 신중

- E-value, Bias Parameter 등 사용

 

🔍 E-value:

우리가 관측하지 못한(결측된) X값들이 Y에 영향을 줬을 가능성이 있다면,

그 요인이 얼마나 강한 영향력을 가져야 현재의 효과 추정값이 무력화되는가?

E-value가 클수록: 결측이나 confounding이 있어도 결과는 비교적 robust
E-value가 작으면: 약한 confounder만 있어도 결과가 뒤집힐 수 있음 → 주의

 

🔍 Bias Parameter:

결측된 집단이 관측된 집단과 얼마나 다르다고 가정할 것인가?를 수치화한 값

예 - 소득이 결측된 사람은 저소득층일 것이라 가정, 이 차이를 수치로 bias parameter = -20(20만원 낮음)

→ bias parameter를 -10, -20, -30 등으로 바꿔보면서 분석 결과를 반복
→ 결과가 크게 흔들린다면, 결측이 결과에 미치는 영향력이 크다는 뜻

 

💡 실무 팁
- 인과추론이 목적이라면 결측치 처리에서부터 bias 통제가 시작됩니다.
- 단순한 예측 목적이라면 모델 성능만 보겠지만, 해석 목적이라면 처리 과정까지 검토해야 결과의 의미가 있습니다.

 

 


 

✨ 마무리하며

결측치는 그냥 '비어 있는 값'이 아닙니다.
데이터가 보내는 구조적 메시지일 가능성이 있습니다.

“어떻게 채울까?”보다 중요한 건
왜 비었는지, 채우면 무엇이 달라지는지 파악하는 힘입니다.

 

📌 결측치는 기술이 아니라 태도로 다뤄야 하는 영역입니다.
좋은 분석가는 '값이 비었을 때' 더 똑똑해집니다. 💡