결측 데이터 시각화 — Pattern Matrix 와 Missingness 상관 (Buisson Ch.6.1)

md.pattern 의 두 축: Amount of Missing Data 와 Correlation of Missingness

Buisson (2021) Ch.6 의 첫 번째 절을 자세히 정리한다. mice 패키지의 md.pattern() 행렬을 두 관점 (양 / 상관) 으로 분해해 분석하는 방법, Min/Max sensitivity test 로 결측 영향 평가, Tampa-Tacoma 면접 사례로 missingness 상관의 두 극단 비교, AirCnC 데이터의 중간 패턴 진단을 단계별로 시연한다.

Experimentation
Causal Inference
저자

Kwangmin Kim

공개

2026년 05월 08일

1 정의

정의: 결측 시각화의 두 축

결측 데이터 처리 1 단계 (Buisson, 2021, Ch.6). md.pattern() 행렬을 다음 두 관점에서 분석:

  1. Amount of Missing Data (양): 변수별 결측 수, 비율, 영향도. “Listwise deletion 가능한가?”
  2. 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% vs 10% vs 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 분석

Buisson 의 절차

가장 결측 많은 변수 (Neuro) 의 결과 영향 점검:

Step 1: 두 가상 데이터셋 생성
   - min_data: 결측을 Neuro 의 최솟값 (0) 으로 대체
   - max_data: 결측을 Neuro 의 최댓값 (10) 으로 대체

Step 2: 그 변수의 가장 중요한 관계 회귀
   - bkg_amt ~ neuro

Step 3: 세 회귀 결과 비교
   - 원본 (결측 행 제외)
   - min_data
   - max_data

해석:

  • 세 회귀의 계수가 비슷 → 결측의 영향이 제한적, listwise drop 가능
  • 세 회귀의 계수가 매우 다름 (특히 부호 다름) → 영향 큼, 처리 필요
직관 — Min/Max 가 worst-case 시나리오

이 절차의 통찰:

만약 결측 값이 모두 최솟값 또는 최댓값이라면 → 가장 극단적 가능성.

원본과 두 극단 사이에 있으면 → 진짜 결과는 그 사이.

비유: 분석 결과의 신뢰구간을 결측 처리에 적용. 어디까지 결과가 흔들릴 수 있는가.

만약 세 결과가 모두 “양의 효과” 면 → 결측이 무엇이든 결론 일치 → 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: 한 변수의 결측이 다른 변수의 결측과 함께 발생 하는 패턴.

두 극단:

  1. 완전 상관 (corr = 1): 한 변수 결측 → 다른 변수도 반드시 결측. 한 원인이 모두 결측 일으킴.
  2. 무 상관 (corr = 0): 변수 결측 사이 독립. 각 변수의 결측이 별개 메커니즘.

실제 데이터는 두 극단 사이.

직관 — 왜 상관 점검이 중요한가

만약 결측이 강하게 상관되어 있다면:

  • 한 변수의 결측 메커니즘 발견 → 다른 변수도 같은 메커니즘으로 설명 가능
  • CD 가 더 단순 (한 missingness 노드)
  • 처리도 통합 (한 보조 변수로 여러 결측 보정)

만약 결측이 무 상관이라면:

  • 각 변수의 결측을 별도 분석
  • 각각 별도 메커니즘 가설
  • 처리도 변수별 독립

→ 상관 점검이 분석 작업량과 CD 복잡도를 결정.

4.2 Tampa vs Tacoma — 두 면접 사례

사례 설정

두 회사 (Tampa, Tacoma) 의 면접 데이터.

각 후보가 3 차례 면접 (I1, I2, I3) 본다. 점수 기록 시스템:

  • Tampa: 첫 면접관이 모든 점수를 한꺼번에 HR 에 제출. 깜빡하면 모든 점수 결측.
  • Tacoma: 각 면접관이 자기 면접의 점수만 제출. 한 명 깜빡해도 다른 점수는 있음.
Tampa — 강한 상관
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. 한 점수 결측이면 다른 점수 모두 결측.

직관 — Tampa 의 결측 메커니즘

이 패턴이 의미하는 것:

“결측의 단일 원인” — 첫 면접관 (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. 변수 간 독립.

직관 — Russian Dolls Sequence

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 상관. 다른 변수는 거의 독립.

직관 — 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 헬스케어 — 임상 추적 결측

CONSORT 의 Loss to Follow-up

임상 시험에서 결측 패턴이 보여주는 것:

패턴 가능 의미 분석 함의
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 이 보여주는 것

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)
직관 — 보고서의 4 항목

이 보고서가 다음 단계 결정의 입력:

  1. 결측 비율 → 어떤 변수가 우선
  2. 패턴 수 → 메커니즘 다양성
  3. Listwise 손실 → 단순 처리 가능성
  4. 상관 → 메커니즘 통합 여부

이 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 의 형제 글

9.2 이전 챕터

9.3 후속 챕터

9.4 카테고리 진입점

Subscribe

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