결측 자료의 진단과 단순 대체

Woodward 14.9+14.10 — MCAR/MAR/MNAR 분류와 단순 대체 방법의 한계

Woodward Ch.14.9 (결측 자료 진단) 과 14.10 (단순 대체) 을 정리한다. 결측 메커니즘 3 분류 (MCAR, MAR, MNAR), 단순 대체 방법 (Listwise deletion, Mean imputation, LOCF) 의 한계, 진단 도구를 자세히 다룬다.

Experimentation
Fundamentals
저자

Kwangmin Kim

공개

2026년 05월 08일

1 도입 — 결측은 자료의 일부

거의 모든 실제 자료에 결측 이 있다. 임상시험의 drop-out, 조사의 무응답, A/B 테스트의 비활성 사용자. 결측 처리가 분석 결과를 좌우한다.

2 결측 메커니즘 3 분류 (Rubin 1976)

정의: 결측 메커니즘

자료가 결측되는가의 분류.

  • MCAR (Missing Completely At Random): 결측이 모든 변수와 무관
  • MAR (Missing At Random): 결측이 관측된 변수에 의존 (결측값 자체에는 무관)
  • MNAR (Missing Not At Random): 결측이 결측값 자체에 의존

2.1 MCAR — 가장 단순

2.1.1 정의

\[ P(\text{결측} \mid X, Y) = P(\text{결측}) \]

결측이 어떤 변수와도 무관. 단순한 무작위 누락.

2.1.2 사례

  • 연구 보조원의 실수 로 자료 누락
  • 자료 입력 오류 로 임의 누락
  • 기술적 문제 (파일 손상)

2.1.3 처리

  • Listwise deletion (결측 행 제거) 가 비편향
  • 표본 크기만 줄어듦
  • 완전 자료 분석 가능

2.2 MAR — 가장 흔함

2.2.1 정의

\[ P(\text{결측} \mid X, Y) = P(\text{결측} \mid X) \]

결측이 관측된 다른 변수 에 의존. 결측값 자체에는 무관.

2.2.2 사례

  • 노년층의 BMI 측정 누락 (연령 정보로 예측 가능)
  • 우울증 환자의 일부 척도 항목 미응답 (다른 척도로 추정)
  • 모바일 사용자가 데스크톱 자료 결측 (디바이스로 알 수 있음)

2.2.3 처리

  • Multiple Imputation (다음 글) 권장
  • Listwise deletion 은 편향
  • 변수 정보 활용 필수

2.3 MNAR — 가장 어려움

2.3.1 정의

\[ P(\text{결측} \mid X, Y) = P(\text{결측} \mid Y) \]

결측이 결측값 자체에 의존. 직접 모델링 불가.

2.3.2 사례

  • 고소득자가 소득 미응답 (소득 자체가 결측 결정)
  • 심한 우울증 환자의 측정 누락 (척도 점수 자체가 결정)
  • 부정 결과 사용자의 자료 누락 (outcome 자체)

2.3.3 처리

  • 민감도 분석 으로 다양한 가정 검토
  • 모델 기반 접근 (Selection model, Pattern-mixture model)
  • 일반적으로 어려움
직관 — 3 분류의 임상 비유

임상시험 drop-out 시나리오:

  • MCAR: 환자가 우연히 (이사 등) 추적 실패. 결측이 다른 변수와 무관.
  • MAR: 연령 많은 환자가 더 자주 추적 실패 (관측된 연령으로 설명 가능).
  • MNAR: 처치 효과 없는 환자가 자료 측정 거부 (결측이 outcome 자체에 의존).

A/B 테스트:

  • MCAR: 사용자가 기술적 오류 로 자료 미수집. 무작위.
  • MAR: 모바일 사용자 가 데스크톱 메트릭 결측 (디바이스 변수로 설명).
  • MNAR: 불만족 사용자 가 자료 사용 동의 거부 (만족도 자체가 결측 결정).

대부분 자료가 MAR 또는 MAR + MNAR 혼합. MCAR 은 드물고, 순수 MNAR 도 드물다.

3 결측 진단

3.1 시각적 진단

3.1.1 Missing Pattern Plot

결측 패턴 시각화. 각 행은 자료 점, 각 열은 변수. 결측 셀 표시.

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    'age': [25, 30, np.nan, 45, 50],
    'bmi': [22, np.nan, 24, 26, np.nan],
    'income': [50000, 60000, 70000, np.nan, 90000]
})

# Missing pattern
print(df.isnull())
# fig, ax = plt.subplots()
# ax.imshow(df.isnull(), cmap='binary')

3.1.2 결측률

missing_rate = df.isnull().mean()
print(missing_rate)

3.2 형식 진단

3.2.1 Little’s MCAR Test

Little’s MCAR Test (Little 1988)

결측이 MCAR 인지 검정. \(H_0\): MCAR.

큰 p 값 → MCAR 가능 (단, 약한 증거). 작은 p 값 → MCAR 아님 (MAR 또는 MNAR).

# pyampute 또는 R 의 BaylorEdPsych 패키지
# 자체 구현 복잡

3.3 MAR vs MNAR 구분

이 둘은 근본적으로 검증 불가능. 결측값을 모르므로 결측이 결측값 자체에 의존하는지 외부 정보 없이는 판단 X.

해법: 민감도 분석. 다양한 가정 (MAR vs 다양한 MNAR 시나리오) 하의 결과 비교.

4 단순 대체 방법

4.1 Method 1 — Listwise Deletion (Complete Case)

결측이 있는 행을 완전 제거.

df_complete = df.dropna()

4.1.1 장점

  • 단순
  • MCAR 에서 비편향

4.1.2 단점

  • MAR 에서 편향
  • 표본 크기 손실
  • 정보 활용 X

4.1.3 권장

MCAR 가 강하게 의심 + 결측률 5 % 미만에서만 사용.

4.2 Method 2 — Mean Imputation

Mean Imputation

결측을 변수 평균 으로 대체.

df['bmi'].fillna(df['bmi'].mean(), inplace=True)

4.2.1 단점 (모두 심각)

  • 분산 과소 추정 (평균 분산 0)
  • 상관 관계 약화
  • MAR 에서 편향

4.2.2 권장

거의 사용하지 말 것. Multiple imputation 이 항상 더 나음.

4.3 Method 3 — LOCF (Last Observation Carried Forward)

LOCF

종단 자료에서 마지막 관측값 으로 후속 결측 대체.

예: 환자의 4 주차 측정 결측2 주차 측정값 사용.

4.3.1 사례

임상시험의 drop-out 처리에 자주 사용.

4.3.2 단점

  • 변화 무시 (시간에 따른 변화 가정)
  • 편향 가능 (drop-out 이 처치 효과와 연관)
  • FDA 등 규제 기관도 비판

4.3.3 권장

대안 (MI) 가 가능하면 피하라. 단순함이 장점이지만 정확성 부족.

4.4 Method 4 — Hot Deck Imputation

비슷한 자료점의 값으로 대체.

# 비슷한 연령·성별의 BMI 평균으로 대체
df['bmi'].fillna(df.groupby(['age_group', 'sex'])['bmi'].transform('mean'), inplace=True)

4.4.1 장점

  • 분포 보존 (mean imputation 보다 나음)
  • 상호 의존성 일부 반영

4.4.2 단점

  • 변동성 여전히 과소 추정
  • “비슷한” 정의 주관적

4.5 Method 5 — Regression Imputation

다른 변수로 회귀 모형 → 결측 예측.

from sklearn.linear_model import LinearRegression

# BMI 결측을 다른 변수로 예측
mask = df['bmi'].isnull()
X_train = df.loc[~mask, ['age', 'sex']]
y_train = df.loc[~mask, 'bmi']
X_test = df.loc[mask, ['age', 'sex']]

model = LinearRegression().fit(X_train, y_train)
df.loc[mask, 'bmi'] = model.predict(X_test)

4.5.1 장점

  • 변수 관계 활용
  • MAR 에서 부분적 편향 보정

4.5.2 단점

  • 예측 불확실성 무시 (한 값으로 대체)
  • 분산 과소 추정

5 단순 대체의 통합 한계

직관 — 단순 대체의 근본 문제

단순 대체 (single imputation) 의 모든 방법은 결측의 불확실성을 무시.

비유: 모르는 답을 추측한그 추측이 정답인 것처럼 처리. 추측의 불확실성 이 분석 결과에 반영되지 않음.

결과:

  • 추정값 은 그럴듯할 수 있음
  • 표준 오차과소 추정 (불확실성 무시 때문)
  • CI너무 좁음 → false positive 증가

해법: Multiple Imputation (다음 글). 결측을 여러 번 다르게 대체하여 불확실성 반영.

6 결측률과 처리 권장

결측률 권장
< 5 % Listwise deletion (MCAR 시) 또는 MI
5 ~ 20 % Multiple Imputation
20 ~ 50 % MI + 민감도 분석
> 50 % 변수 또는 분석 재고

7 A/B 테스트의 결측

7.1 흔한 결측 패턴

패턴 메커니즘 처리
노출 안 됨 정의상 분석 제외 OK (무시)
클릭 없음 정의상 0 OK (0 처리)
매출 없음 정의상 0 OK (0 처리)
이탈 사용자 MAR 또는 MNAR 신중
일부 segment 정보 결측 MAR MI

7.2 권장 절차

1. 결측률 확인
2. 패턴 분석 (그룹·시간·변형별)
3. MCAR 가정 검정 (Little's test)
4. 단순 분석 (complete case) + MI 보고
5. 결과 비교 → 일관 여부

8 결측 메커니즘 진단의 자세한 방법

8.1 진단 1 — Missing pattern 시각화

자료 매트릭스의 결측 패턴 을 행렬 시각화. 패턴이 다음 형태일 수 있음:

  • Univariate: 한 변수만 결측
  • Monotone: 변수 순서로 결측 누적 (longitudinal 자료)
  • Arbitrary: 무작위 결측 패턴
import pandas as pd
import numpy as np

df = pd.DataFrame({
    'age': [25, 30, np.nan, 45, 50],
    'bmi': [22, np.nan, 24, 26, np.nan],
    'income': [50000, 60000, 70000, np.nan, 90000]
})

# 결측 매트릭스
print("결측 패턴:")
print(df.isnull().astype(int))

8.2 진단 2 — Visualization

missingno 패키지가 결측 시각화 표준:

# import missingno as msno
# msno.matrix(df)  # 매트릭스 시각화
# msno.heatmap(df)  # 결측 변수 간 상관
# msno.dendrogram(df)  # 결측 계층 클러스터

8.3 진단 3 — Logistic regression of missingness

각 변수의 결측 여부 (이진) 를 다른 변수로 예측:

from sklearn.linear_model import LogisticRegression

# bmi 결측 vs age 의 관계
df['bmi_missing'] = df['bmi'].isnull().astype(int)
mask = ~df['age'].isnull()
X = df.loc[mask, ['age']]
y = df.loc[mask, 'bmi_missing']

if len(np.unique(y)) > 1:
    model = LogisticRegression()
    model.fit(X, y)
    print(f"Coefficient: {model.coef_[0][0]:.4f}")
    # 큰 coefficient → MAR (age 가 결측 예측)

이 절차로 MCAR vs MAR 부분 검정. MNAR 은 이 방법으로 구분 불가.

9 Listwise Deletion 의 자세한 분석

9.1 MCAR 에서의 비편향성

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression

np.random.seed(42)
n = 1000

# 진짜 모형
X = np.random.normal(0, 1, n)
Y = 2 + 3*X + np.random.normal(0, 1, n)

# MCAR 결측 (무작위 30 %)
mcar_missing = np.random.random(n) < 0.30
Y_mcar = Y.copy()
Y_mcar[mcar_missing] = np.nan

# Listwise — 비편향
df_mcar = pd.DataFrame({'X': X, 'Y': Y_mcar}).dropna()
beta_mcar = np.cov(df_mcar['X'], df_mcar['Y'])[0,1] / np.var(df_mcar['X'])
print(f"MCAR + Listwise: β1 = {beta_mcar:.4f} (진짜: 3.0)")
# 약 3.0 (비편향)

9.2 MAR 에서의 편향

# MAR 결측 — X 가 큰 경우 더 자주 결측
prob_missing = 1 / (1 + np.exp(-X))  # X 큼 → 결측 확률 큼
mar_missing = np.random.random(n) < prob_missing
Y_mar = Y.copy()
Y_mar[mar_missing] = np.nan

# Listwise — 편향
df_mar = pd.DataFrame({'X': X, 'Y': Y_mar}).dropna()
beta_mar = np.cov(df_mar['X'], df_mar['Y'])[0,1] / np.var(df_mar['X'])
print(f"MAR + Listwise: β1 = {beta_mar:.4f} (진짜: 3.0)")
# 일반적으로 3.0 보다 작음 (큰 X 의 자료 손실로 편향)

9.3 함의

MAR 에서 listwise deletion 은 편향 위험. MI 권장.

10 Mean Imputation 의 함정

10.1 분산 과소 추정

# Mean imputation 의 분산 영향
Y_mean_imputed = Y_mar.copy()
mean_observed = np.nanmean(Y_mar)
Y_mean_imputed[np.isnan(Y_mean_imputed)] = mean_observed

print(f"원 자료 분산: {np.var(Y, ddof=1):.4f}")
print(f"결측 후 분산 (관측): {np.nanvar(Y_mar, ddof=1):.4f}")
print(f"Mean imputation 후 분산: {np.var(Y_mean_imputed, ddof=1):.4f}")
# 결측 후 분산보다 mean imputation 분산이 더 작음

10.2 상관 약화

# Mean imputation 이 X-Y 상관 약화
corr_orig = np.corrcoef(X, Y)[0,1]
df_listwise = pd.DataFrame({'X': X, 'Y': Y_mar}).dropna()
corr_listwise = np.corrcoef(df_listwise['X'], df_listwise['Y'])[0,1]

X_mi = X.copy()
Y_mi = Y_mar.copy()
Y_mi[np.isnan(Y_mi)] = mean_observed
corr_mi = np.corrcoef(X_mi, Y_mi)[0,1]

print(f"진짜 상관: {corr_orig:.4f}")
print(f"Listwise 상관: {corr_listwise:.4f}")
print(f"Mean imputation 상관: {corr_mi:.4f}")
# Mean imputation 상관이 약화 (결측 자료의 X 정보 무시)

이 시뮬레이션이 Mean imputation 의 위험 정량화.

11 A/B 테스트의 결측 처리

11.1 시나리오 — 사용자 segment 결측

import pandas as pd
import numpy as np

# 가상 자료
np.random.seed(42)
n = 5000
df = pd.DataFrame({
    'variant': np.random.choice([0, 1], n),
    'age_group': np.random.choice(['young', 'mid', 'old'], n),
    'tenure_months': np.random.uniform(0, 60, n),
    'revenue': np.random.normal(100, 30, n)
})

# 일부 사용자의 segment 정보 결측 (MAR — 신규에서 더 자주)
mask = (df['tenure_months'] < 6) & (np.random.random(n) < 0.30)
df.loc[mask, 'age_group'] = np.nan

# 결측률
print(f"age_group 결측률: {df['age_group'].isnull().mean():.2%}")

# Listwise deletion
df_listwise = df.dropna()
print(f"Listwise 표본: {len(df_listwise)} / {n}")

# 분석 — variant 효과 (Listwise vs Multiple Imputation 비교)
from sklearn.linear_model import LinearRegression
# Listwise
model = LinearRegression()
X = pd.get_dummies(df_listwise[['variant', 'age_group']], drop_first=True)
y = df_listwise['revenue']
model.fit(X, y)
print(f"Listwise variant 효과: {model.coef_[0]:.3f}")

이 분석에서 listwise 가 신규 사용자 (tenure 짧음) 를 비례 이상 제외 → variant 효과 추정 편향 가능.

해법: Multiple Imputation (다음 글).

12 후속 — Multiple Imputation

다음 글 A-WOO14-7 은 Multiple Imputation 의 자세한 절차를 다룬다. 결측의 불확실성을 정직하게 반영하는 표준 도구.

13 관련 주제

선행 지식

후속 주제 (Phase A)

  • A-WOO14-7 Multiple Imputation

다른 카테고리 연결

Subscribe

Enjoy this blog? Get notified of new posts by email: