1 정의
결측 데이터 처리 1 단계 (Buisson, 2021, Ch.6). md.pattern() 행렬을 다음 두 관점에서 분석:
- Amount of Missing Data (양): 변수별 결측 수, 비율, 영향도. “Listwise deletion 가능한가?”
- Correlation of Missingness (상관): 변수 간 결측 함께 발생 패턴. “결측 메커니즘이 한 원인에서 비롯되는가?”
두 분석이 다음 단계 (진단·처리) 의 입력.
분석가의 자연스러운 직감: “결측이 보이면 즉시 mice() 호출.”
이 경로의 함정:
- 결측의 패턴을 모른 채 처리 → 메커니즘 가설 부재
- 처리 도구의 가정 (MAR 등) 이 데이터에 맞는지 점검 안 됨
- 결과를 신뢰할지 판단 불가
대신 권장: 시각화로 5 분 동안 패턴 파악 → 가설 → 처리.
5 분의 투자가 분석 전체의 신뢰도 결정.
→ 시각화는 사치가 아닌 필수. 결측 처리의 첫 단계.
2 Pattern Matrix — md.pattern() 의 구조
2.1 행렬의 구성
age open gender bkg_amt state extra neuro
368 1 1 1 1 1 1 1 0
358 1 1 1 1 1 1 0 1
249 1 1 1 1 1 0 1 1
228 1 1 1 1 1 0 0 2
163 1 1 1 0 1 1 1 1
214 1 1 1 0 1 1 0 2
125 1 1 1 0 1 0 1 2
120 1 1 1 0 1 0 0 3
33 1 1 1 1 0 1 1 1
23 1 1 1 1 0 1 0 2
15 1 1 1 1 0 0 1 2
15 1 1 1 1 0 0 0 3
24 1 1 1 0 0 1 1 2
24 1 1 1 0 0 1 0 3
23 1 1 1 0 0 0 1 3
18 1 1 1 0 0 0 0 4
0 0 0 175 711 793 1000 2679
요소 해석:
- 왼쪽 숫자: 그 패턴을 가진 행 수
- 본문 0/1: 0 = 결측, 1 = 관측
- 오른쪽 숫자: 그 패턴에서 결측 변수 개수
- 하단 숫자: 변수별 결측 총수
- 하단 우측: 전체 결측 셀 수 (2679)
이 행렬의 한 줄이 알려주는 것:
예: 두 번째 행 358 1 1 1 1 1 1 0 1
- 358 명이 같은 패턴 (Neuro 만 결측)
- Age, Open, Gender, Bkg_amt, State, Extra 모두 관측
- 결측 변수 1 개
이 358 명은 “Neuro 응답만 빼먹은 사람들” 이라는 동질 그룹.
분석가는 자문:
- 358 명이 어떤 사람들인가? — Age, Gender 등 다른 정보로 특성화 가능
- 왜 그들만 Neuro 결측? — 도메인 가설 (예: “성격 검사 끝까지 안 함”)
- 이들의 Bkg_amt 가 평균과 다른가? — 결측이 결과에 편향 만드는지 점검
한 행이 이런 질문 줄기를 만듦. 16 개 행 = 16 개 잠재적 분석 줄기.
2.2 16 개 행 모두 본질적인가
행 수가 매우 작은 패턴 (예: 마지막 18 명 — 4 변수 모두 결측) 은:
- 통계적으로 적은 표본
- 구체적 메커니즘 분석 어려움
- 일반적으로 listwise deletion 또는 다른 처리
흔한 패턴 (행 수 100+) 만 깊이 분석하는 게 효율적. 사람의 시간이 한정됨.
기준 (제안):
- 행 수 < 10: 무시 (outlier 패턴)
- 행 수 10~100: 메커니즘 표면 점검
- 행 수 100+: 도메인 가설 + 깊이 분석
3 Amount of Missing Data — 양적 분석
3.1 변수별 결측 비율
변수 결측 수 비율
neuro 1000 50.0%
extra 793 39.7%
state 711 35.6%
bkg_amt 175 8.8%
gender 0 0%
open 0 0%
age 0 0%
→ 정렬: 결측 큰 변수부터.
분석가의 첫 결정 분기:
- 모든 변수의 결측 < 1%: Listwise deletion 안전 (편향이 작음)
- 일부 변수 1~10%: Sensitivity test 필요 (drop 가능 여부 점검)
- 일부 변수 10%+: 본격 처리 (Multiple Imputation 등)
AirCnC 의 50% 결측은 명확히 후자. 본격 처리 필수.
분석가의 휴리스틱:
| 결측 비율 | 위험 | 처리 |
|---|---|---|
| < 1% | 영향 거의 없음 | 무시 (listwise OK) |
| 1~5% | 잠재 영향, 표본 충분 | Sensitivity test |
| 5~20% | 명확한 영향 | Multiple Imputation 권장 |
| 20~50% | 큰 영향 | 본격 MI + 가정 점검 |
| > 50% | 변수 자체 의심 | 변수 제거 또는 보강 데이터 수집 |
이 임계값은 휴리스틱. 진짜 결정은 sensitivity test.
→ 비율만으로 결정 안 함. 영향도 점검 (다음 절).
3.2 Sensitivity Test — Min/Max Bound 분석
가장 결측 많은 변수 (Neuro) 의 결과 영향 점검:
Step 1: 두 가상 데이터셋 생성
- min_data: 결측을 Neuro 의 최솟값 (0) 으로 대체
- max_data: 결측을 Neuro 의 최댓값 (10) 으로 대체
Step 2: 그 변수의 가장 중요한 관계 회귀
- bkg_amt ~ neuro
Step 3: 세 회귀 결과 비교
- 원본 (결측 행 제외)
- min_data
- max_data
해석:
- 세 회귀의 계수가 비슷 → 결측의 영향이 제한적, listwise drop 가능
- 세 회귀의 계수가 매우 다름 (특히 부호 다름) → 영향 큼, 처리 필요
이 절차의 통찰:
만약 결측 값이 모두 최솟값 또는 최댓값이라면 → 가장 극단적 가능성.
원본과 두 극단 사이에 있으면 → 진짜 결과는 그 사이.
비유: 분석 결과의 신뢰구간을 결측 처리에 적용. 어디까지 결과가 흔들릴 수 있는가.
만약 세 결과가 모두 “양의 효과” 면 → 결측이 무엇이든 결론 일치 → drop 안전. 부호가 바뀌면 → 결측이 결정적 → 본격 처리.
이게 sensitivity analysis 의 단순 버전. Buisson 의 빠르고 더러운 (quick-and-dirty) 도구.
3.3 AirCnC Neuro 사례
원본 (listwise): beta = -5.9
Min imputation (Neuro=0): beta = -8.0
Max imputation (Neuro=10): beta = +2.7
부호가 바뀜. 음에서 양으로.
해석:
- Listwise: “Neuro 1점 ↑ → Bkg ↓ 5.9$” — 신경증 높을수록 적게 예약
- Max impute: “Neuro 1점 ↑ → Bkg ↑ 2.7$” — 정반대
- 두 결론이 비즈니스 의사결정을 정반대로 이끔
→ Neuro 결측을 단순 drop 할 수 없음. 본격 처리 필요.
비유: “투자 분석에서 ROI 가 +5% 또는 -5% 인데 어느 쪽인지 모름.”
- ROI +5%: 투자 진행
- ROI -5%: 투자 중단
- 모름: 의사결정 불가
같은 식으로 Neuro 의 효과 부호를 모르면 마케팅 의사결정 (신경증 높은 고객 타겟팅) 이 불가능.
분석가의 책임: 결측 처리 후 부호와 크기를 정확히 추정. Sensitivity test 가 그 책임의 첫 단계.
3.4 범주·이진 변수의 Min/Max
이진 변수:
- Min = 0, Max = 1
- 두 시나리오: best-case (모두 0) vs worst-case (모두 1)
범주 변수:
- Min/Max 의미 없음 → 가장 드문 vs 가장 흔한 카테고리로 대체
- 예: State 가 A, B, C 일 때 → 모두 A 로 vs 모두 C 로
이 변형으로 같은 sensitivity test 가능.
State 의 sensitivity test:
min_state <- ifelse(is.na(state), "C", state) # C 가 가장 드뭄
max_state <- ifelse(is.na(state), "A", state) # A 가 가장 흔함두 회귀 비교.
3.5 Sensitivity 의 결정 기준
세 결과가 “같은 비즈니스 결론” 이면 → drop OK.
“같은 비즈니스 결론” 의 의미:
- 부호 같음 (양 / 음 / 0 일치)
- 자릿수 같음 (5 vs 6, 또는 10 vs 12 — 비즈니스적으로 같은 결정)
- 신뢰구간 겹침
비즈니스 파트너에게 “이 숫자 의미가 같습니까?” 물었을 때 “Yes” 라면 drop.
AirCnC Neuro 사례에서 비즈니스 파트너에게:
- “-5.9 vs +2.7”: “이 차이가 의미가 있습니까?”
- 답: “당연히 있죠. 부호가 다르면 마케팅 전략이 정반대.”
→ Listwise deletion 불가능. 본격 처리 진입.
4 Correlation of Missingness — 상관 분석
4.1 개념 — 두 극단
Missingness Correlation: 한 변수의 결측이 다른 변수의 결측과 함께 발생 하는 패턴.
두 극단:
- 완전 상관 (corr = 1): 한 변수 결측 → 다른 변수도 반드시 결측. 한 원인이 모두 결측 일으킴.
- 무 상관 (corr = 0): 변수 결측 사이 독립. 각 변수의 결측이 별개 메커니즘.
실제 데이터는 두 극단 사이.
만약 결측이 강하게 상관되어 있다면:
- 한 변수의 결측 메커니즘 발견 → 다른 변수도 같은 메커니즘으로 설명 가능
- CD 가 더 단순 (한 missingness 노드)
- 처리도 통합 (한 보조 변수로 여러 결측 보정)
만약 결측이 무 상관이라면:
- 각 변수의 결측을 별도 분석
- 각각 별도 메커니즘 가설
- 처리도 변수별 독립
→ 상관 점검이 분석 작업량과 CD 복잡도를 결정.
4.2 Tampa vs Tacoma — 두 면접 사례
두 회사 (Tampa, Tacoma) 의 면접 데이터.
각 후보가 3 차례 면접 (I1, I2, I3) 본다. 점수 기록 시스템:
- Tampa: 첫 면접관이 모든 점수를 한꺼번에 HR 에 제출. 깜빡하면 모든 점수 결측.
- Tacoma: 각 면접관이 자기 면접의 점수만 제출. 한 명 깜빡해도 다른 점수는 있음.
ID 수 I1 I2 I3
1600 1 1 1 0
400 0 0 0 3
400 400 400
- 1600 명: 3 점수 모두 있음
- 400 명: 3 점수 모두 결측
- 결측 패턴: “전부 있음” 또는 “전부 없음” 두 가지만
상관 행렬:
I1 I2 I3
I1 1 1 1
I2 1 1 1
I3 1 1 1
모두 1.0. 한 점수 결측이면 다른 점수 모두 결측.
이 패턴이 의미하는 것:
“결측의 단일 원인” — 첫 면접관 (Murphy 등) 이 깜빡함.
이 한 사람의 행동이 모든 점수의 결측을 결정. 결측은 사실상 한 변수의 결측.
분석:
- 별도로 변수마다 결측 처리할 필요 없음
- 한 통합 변수 (“interview_data_missing”) 로 처리
- CD:
Murphy_forgot → all_scores_missing - 처리: Murphy 가 첫 면접관이었는지 (관측됨) 통제 → MAR 처리 가능
→ Tampa 같은 데이터는 단순한 결측 메커니즘. 처리 쉬움.
4.3 Tacoma — 무 상관
ID 수 I1 I2 I3
1046 1 1 1 0
262 1 0 1 1
253 1 1 0 1
71 1 0 0 2
240 0 1 1 1
55 0 0 1 2
56 0 1 0 2
17 0 0 0 3
368 397 405
다양한 패턴, 각 패턴 행 수 분포.
상관 행렬:
I1 I2 I3
I1 1 0 0
I2 0 1 0
I3 0 0 1
대각선만 1. 변수 간 독립.
Tacoma 패턴의 통찰:
Prob(I3 결측) = 405/2000 = 0.20
Prob(I2 결측) = 397/2000 = 0.20
Prob(I1 결측) = 368/2000 = 0.18
만약 독립이라면:
- Prob(I3 ∩ I2 결측) ≈ 0.20 × 0.20 = 0.04 → 80 명
- Prob(I3 ∩ I2 ∩ I1 결측) ≈ 0.20 × 0.20 × 0.18 = 0.0072 → 14.4 명
실제 데이터:
- 두 변수 동시 결측: 71 + 55 + 56 = 182 (예상 80 보다 많지만 우연 가능)
- 세 변수 동시 결측: 17 (예상 14.4 와 가까움)
이런 “Russian Dolls” sequence (작은 인형이 큰 인형 안에) 의 감소 패턴 = 독립성의 증거.
만약 강한 상관이면: 대신 단조 sequence (한 패턴이 다른 패턴 포함):
Tampa: 모두 결측 패턴이 한 변수만 결측 패턴 포함 ⟹ 단조
Tacoma: 다양한 부분 결측 ⟹ 독립
4.4 상관 행렬 시각화
Tampa: Tacoma:
I1 I2 I3 I1 I2 I3
I1 1 1 1 I1 1 0 0
I2 1 1 1 I2 0 1 0
I3 1 1 1 I3 0 0 1
색깔로 시각화하면:
- Tampa: 모두 진한 빨간색 (corr = 1)
- Tacoma: 대각선만 빨강, 나머지 흰색 (corr = 0)
비즈니스 의사결정 차이:
- Tampa: 면접관 시스템 수정 (개인화 → 분산화)
- Tacoma: 시스템 OK, 무작위 잊음만 처리
5 AirCnC 데이터의 상관 분석
AirCnC 의 16 개 패턴이 다양하게 분포 (368, 358, 249, 228, 163, 214, …).
이는 두 극단 사이.
# Python 으로 missingness 상관
miss_indicators = available_df[["bkg_amt", "state", "extra", "neuro"]].isnull()
miss_corr = miss_indicators.corr()
print(miss_corr)예상 결과 (대각선 외):
| bkg_amt | state | extra | neuro | |
|---|---|---|---|---|
| bkg_amt | 1.00 | 0.05 | 0.03 | 0.04 |
| state | 0.05 | 1.00 | 0.10 | 0.08 |
| extra | 0.03 | 0.10 | 1.00 | 0.35 |
| neuro | 0.04 | 0.08 | 0.35 | 1.00 |
→ Extra 와 Neuro 의 결측이 0.35 상관. 다른 변수는 거의 독립.
가설:
- 두 trait 가 같은 설문의 인접 섹션 → 한 응답자가 양쪽 다 안 함 경향
- “성격 검사를 중도에 그만둠” 메커니즘
도메인 점검:
- 설문 페이지 구조 확인 (둘이 같은 페이지?)
- 응답 시간 데이터 (둘 다 응답한 사람의 평균 시간 vs 일부만 응답한 사람)
이 가설이 옳다면:
- “Extra 또는 Neuro 결측” 의 통합 indicator 로 처리
- “설문 중도 포기 여부” 를 보조 변수로 사용
- CD 단순화
→ 도메인 직관 + 데이터 = 더 정확한 CD.
6 응용 — 다른 비즈니스 사례의 시각화
6.1 SaaS — 사용자 활동 결측
분석 대상: 신규 사용자의 30 일 retention.
데이터:
- 시그업 정보 (모두 관측)
- 활동 metric (일부 결측 — 비활성 사용자의 데이터 부재)
- 만족도 설문 (선택적, 50% 결측)
패턴 행렬에서 발견 가능한 통찰:
| 패턴 | 가능한 메커니즘 |
|---|---|
| 활동만 결측 | 비활성 사용자 (MNAR — 활동이 결측 일으킴) |
| 설문만 결측 | 무작위 응답 거부 (MCAR/MAR) |
| 활동 + 설문 모두 결측 | 비활성 + 응답 거부 (MNAR + 추가) |
| 활동 있음 + 설문 결측 | 활성 사용자 일부 (MAR) |
각 패턴의 빈도와 메커니즘 구분.
6.2 헬스케어 — 임상 추적 결측
임상 시험에서 결측 패턴이 보여주는 것:
| 패턴 | 가능 의미 | 분석 함의 |
|---|---|---|
| Treatment 군에서 결측 ↑ | Treatment 부작용으로 응답 회피 | MNAR — 결과에 큰 편향 |
| 시간 지날수록 결측 ↑ | 환자가 점진 이탈 | MAR (시간 통제 시) |
| 특정 baseline 특성 환자에서 결측 ↑ | 그 특성이 결측 일으킴 | MAR (특성 통제 시) |
CONSORT guideline 이 이 분석을 의무화 — flow diagram 에 randomized vs lost vs analyzed 보고.
7 코드 예시 — Python 으로 시각화 자동화
7.1 md.pattern 함수 (Python 버전)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
def md_pattern(df, missing_only=True):
"""
mice::md.pattern() 의 Python 구현.
Parameters
----------
df : pd.DataFrame
missing_only : bool
결측 있는 변수만 표시 (default True)
Returns
-------
pd.DataFrame (pattern matrix)
"""
if missing_only:
miss_cols = [c for c in df.columns if df[c].isnull().any()]
else:
miss_cols = list(df.columns)
if not miss_cols:
print("결측 없음")
return None
# 0/1 indicator (관측 = 1, 결측 = 0)
indicators = df[miss_cols].notna().astype(int)
# 패턴별 카운트
pattern_count = indicators.value_counts().reset_index()
pattern_count.columns = miss_cols + ["count"]
# 결측 변수 수
pattern_count["n_missing"] = (1 - pattern_count[miss_cols]).sum(axis=1)
# 정렬 (결측 적은 순)
pattern_count = pattern_count.sort_values(
["n_missing", "count"], ascending=[True, False]
).reset_index(drop=True)
# 변수별 결측 합계 (하단 row)
var_missing = df[miss_cols].isnull().sum().to_dict()
var_missing["count"] = df[miss_cols].isnull().sum().sum()
var_missing["n_missing"] = "-"
pattern_count = pd.concat([pattern_count, pd.DataFrame([var_missing])], ignore_index=True)
return pattern_count이 함수는 R 의 mice::md.pattern() 의 출력을 정확히 재현. 분석가가 R/Python 사이 이동하면서 같은 결과 보장.
return 값:
- DataFrame 형태 → 추가 분석 가능 (예: 큰 패턴만 추출)
- 시각화 입력으로 사용 가능
이 함수를 표준화하면 매 분석 반복 작업 절약.
7.2 시각화 — Heatmap
def plot_md_pattern(df, figsize=(10, 6)):
"""결측 패턴 heatmap 시각화."""
miss_cols = [c for c in df.columns if df[c].isnull().any()]
indicators = df[miss_cols].notna().astype(int)
# 패턴별 그룹화
pattern_count = indicators.value_counts().reset_index()
pattern_count.columns = miss_cols + ["count"]
pattern_count = pattern_count.sort_values("count", ascending=False).head(20)
# 패턴 매트릭스
matrix = pattern_count[miss_cols].values
fig, ax = plt.subplots(figsize=figsize)
sns.heatmap(
matrix,
cmap=["red", "lightblue"],
cbar=False,
ax=ax,
linewidths=0.5,
linecolor="white",
xticklabels=miss_cols,
yticklabels=[f"{c} rows" for c in pattern_count["count"]],
)
ax.set_title("Missing Data Patterns (Blue = Observed, Red = Missing)")
ax.set_xlabel("Variables")
ax.set_ylabel("Pattern (rows count)")
plt.tight_layout()
plt.savefig("md_pattern.png", dpi=80)
plt.show()
# AirCnC 시뮬레이션
np.random.seed(42)
n = 2000
df_aircnc = pd.DataFrame({
"age": np.random.normal(40, 12, n).clip(18, 80),
"gender": np.random.choice(["F", "M"], n),
"open": np.random.normal(5, 2, n).clip(0, 10),
"bkg_amt": np.random.normal(500, 100, n),
"state": np.random.choice(["A", "B", "C"], n),
"extra": np.random.normal(5, 2, n).clip(0, 10),
"neuro": np.random.normal(5, 2, n).clip(0, 10),
})
# 결측 발생
df_aircnc.loc[np.random.uniform(0, 1, n) < 0.0875, "bkg_amt"] = np.nan
df_aircnc.loc[np.random.uniform(0, 1, n) < 0.355, "state"] = np.nan
df_aircnc.loc[np.random.uniform(0, 1, n) < 0.397, "extra"] = np.nan
# Extra-Neuro 상관 결측 (둘 다 결측 경향)
neuro_extra_joint = (df_aircnc["extra"].isnull() & (np.random.uniform(0, 1, n) < 0.6))
neuro_alone = (~df_aircnc["extra"].isnull()) & (np.random.uniform(0, 1, n) < 0.35)
df_aircnc.loc[neuro_extra_joint | neuro_alone, "neuro"] = np.nan
# 시각화
plot_md_pattern(df_aircnc)Heatmap 의 첫 인상:
- 위쪽: 결측 적은 패턴 (밝음)
- 아래쪽: 결측 많은 패턴 (어두움)
- 행 길이: 그 패턴 행 수
빠른 판단:
- 가장 흔한 패턴이 “전체 관측” → 데이터 품질 양호
- 일부 변수만 빨간색이 흩어짐 → 그 변수의 결측 메커니즘 분석 우선
- 빨간색이 함께 뭉침 (같은 행에) → 결측 상관 (Tampa 같은 패턴)
5 초만 보면 데이터의 결측 구조 파악.
7.3 Sensitivity Test 자동화
import statsmodels.api as sm
from statsmodels.formula.api import ols
def sensitivity_min_max(df, target, predictor):
"""
Buisson 의 Min/Max Sensitivity Test.
결측 변수의 Min, Max 대체 후 회귀 결과 비교.
"""
# 원본
orig_df = df.dropna(subset=[target, predictor])
m_orig = ols(f"{target} ~ {predictor}", data=orig_df).fit()
# Min 대체
min_df = df.copy()
min_df[predictor] = min_df[predictor].fillna(min_df[predictor].min())
m_min = ols(f"{target} ~ {predictor}", data=min_df).fit()
# Max 대체
max_df = df.copy()
max_df[predictor] = max_df[predictor].fillna(max_df[predictor].max())
m_max = ols(f"{target} ~ {predictor}", data=max_df).fit()
return {
"orig_beta": m_orig.params[predictor],
"min_beta": m_min.params[predictor],
"max_beta": m_max.params[predictor],
"orig_se": m_orig.bse[predictor],
"min_se": m_min.bse[predictor],
"max_se": m_max.bse[predictor],
}
# AirCnC Neuro 의 Bkg_amt 효과 sensitivity
result = sensitivity_min_max(df_aircnc, "bkg_amt", "neuro")
print("=== Neuro 의 Sensitivity Test ===")
print(f" 원본: beta = {result['orig_beta']:.3f} (SE = {result['orig_se']:.3f})")
print(f" Min 대체: beta = {result['min_beta']:.3f}")
print(f" Max 대체: beta = {result['max_beta']:.3f}")
# 부호 일치 점검
signs = [np.sign(result[f"{x}_beta"]) for x in ["orig", "min", "max"]]
if len(set(signs)) == 1:
print("\n→ 부호 일치. Listwise drop 가능.")
else:
print("\n→ 부호 불일치. 본격 처리 필요.")이 함수가 분석가에게 주는 것:
- 결측 변수의 영향도 즉시 평가
- 시각적 비교 (세 숫자)
- 자동 결정 권고 (drop 또는 처리)
분석 파이프라인에서 매 결측 변수마다 호출. 시간 절약.
→ 사람의 직관 + 자동 검증의 조합.
7.4 Missingness Correlation Matrix
def missingness_correlation(df, figsize=(8, 6)):
"""변수 간 결측 함께 발생 패턴 시각화."""
miss_cols = [c for c in df.columns if df[c].isnull().any()]
miss_indicators = df[miss_cols].isnull().astype(int)
corr_matrix = miss_indicators.corr()
fig, ax = plt.subplots(figsize=figsize)
sns.heatmap(
corr_matrix,
annot=True,
cmap="RdBu_r",
center=0,
vmin=-1,
vmax=1,
fmt=".2f",
ax=ax,
)
ax.set_title("Missingness Correlation Matrix")
plt.tight_layout()
plt.savefig("missingness_corr.png", dpi=80)
plt.show()
# 강한 상관 자동 알림
print("\n=== 강한 상관 (|corr| > 0.3) ===")
for i in range(len(corr_matrix)):
for j in range(i + 1, len(corr_matrix)):
c = corr_matrix.iloc[i, j]
if abs(c) > 0.3:
print(
f" {corr_matrix.index[i]} ↔ {corr_matrix.columns[j]}: {c:.3f}"
)
missingness_correlation(df_aircnc)이 함수의 출력에서 분석가가 빠르게 보는 것:
- “Extra ↔︎ Neuro: 0.35” — 강한 상관 → 같은 결측 메커니즘 가설
- 다른 쌍은 상관 약함 → 별개 메커니즘
이 정보가 다음 단계 (메커니즘 진단) 의 입력. CD 에 결측 노드를 어떻게 그릴지 결정.
7.5 종합 — 결측 진단 보고서
def missing_data_report(df):
"""결측 데이터의 종합 진단 보고서."""
print("=" * 60)
print("결측 데이터 진단 보고서")
print("=" * 60)
# 1. 변수별 결측 비율
print("\n[1] 변수별 결측 비율")
miss_pct = df.isnull().mean() * 100
miss_pct_sorted = miss_pct[miss_pct > 0].sort_values(ascending=False)
for var, pct in miss_pct_sorted.items():
recommend = ""
if pct < 1:
recommend = "drop OK"
elif pct < 5:
recommend = "sensitivity test"
elif pct < 20:
recommend = "Multiple Imputation 권장"
elif pct < 50:
recommend = "본격 MI + 가정 점검"
else:
recommend = "변수 제거 고려"
print(f" {var}: {pct:.1f}% ({recommend})")
# 2. 결측 패턴 수
miss_cols = list(miss_pct_sorted.index)
if miss_cols:
n_patterns = df[miss_cols].isnull().drop_duplicates().shape[0]
print(f"\n[2] 고유 결측 패턴 수: {n_patterns}")
# 3. Listwise deletion 시 표본 손실
if miss_cols:
complete_n = df.dropna().shape[0]
loss_pct = (1 - complete_n / len(df)) * 100
print(f"\n[3] Listwise deletion 시 표본 손실: {loss_pct:.1f}% ({len(df) - complete_n}/{len(df)})")
# 4. 결측 상관 (높은 것만)
print("\n[4] 강한 결측 상관 (>0.3)")
miss_indicators = df[miss_cols].isnull().astype(int)
corr = miss_indicators.corr()
found_high_corr = False
for i in range(len(corr)):
for j in range(i + 1, len(corr)):
c = corr.iloc[i, j]
if abs(c) > 0.3:
print(f" {corr.index[i]} ↔ {corr.columns[j]}: {c:.3f}")
found_high_corr = True
if not found_high_corr:
print(" 없음 (결측 메커니즘이 변수마다 독립적)")
missing_data_report(df_aircnc)이 보고서가 다음 단계 결정의 입력:
- 결측 비율 → 어떤 변수가 우선
- 패턴 수 → 메커니즘 다양성
- Listwise 손실 → 단순 처리 가능성
- 상관 → 메커니즘 통합 여부
이 4 항목으로 분석가가 5 분 내에 결정:
- “비율 모두 < 5% + 손실 < 5%” → Listwise drop
- “비율 일부 > 20% + 강한 상관 있음” → MI 통합 처리
- “비율 일부 > 30% + 무 상관” → 변수별 독립 MI
→ 자동 진단 + 사람의 결정.
8 시각화 단계의 종합 결정
1. 모든 변수 결측 < 1%?
Yes → Listwise drop
No → Step 2
2. Sensitivity test 결과 부호 일치?
Yes → Listwise drop
No → Step 3
3. 결측 상관 강함 (>0.3) ?
Yes → 통합 메커니즘 가설 → Step 4
No → 변수별 독립 가설 → Step 4
4. Rubin 분류 진단 ([E-BUI6-2](./E-BUI6-2-mcar-mar-mnar-diagnosis.qmd))
이 결정 트리가 시각화의 산출물.
9 관련 주제
9.1 Ch.6 의 형제 글
- E-BUI6-0 결측 데이터 처리 overview — Ch.6 전체 흐름
- E-BUI6-2 결측 분류와 진단 (MCAR/MAR/MNAR) — 단계 2
- E-BUI6-3 다중 대체 (PMM, 정규) — 단계 3
- E-BUI6-4 보조 변수와 대체 수 결정 — 단계 4
9.2 이전 챕터
- E-BUI5-2 백도어 기준과 M-패턴 — Ch.5: Confounding
9.3 후속 챕터
- E-BUI8-0 Theory of Change 실험 설계 overview — Ch.8: 실험 설계
9.4 카테고리 진입점
- Experimentation 학습 로드맵 — 11 Phase × 7 교재 매핑