결측 분류와 진단 — MCAR·MAR·MNAR 의 CD 표현과 logistic regression 진단 (Buisson Ch.6.2)

Missingness 를 CD 노드로 추가하는 방식, Rubin 분류의 시각화, AirCnC 의 진단

Buisson (2021) Ch.6 의 진단 절을 자세히 정리한다. Missingness 변수를 CD 에 추가하는 Buisson 의 컨벤션, Wrong/False values vs Missing 의 구분, Rubin 분류 (MCAR/MAR/MNAR) 의 CD 표현, logistic regression 으로 결측 indicator 회귀하는 진단 방법, “Cautious is not biased” 원칙을 AirCnC 사례로 시연한다.

Experimentation
Causal Inference
저자

Kwangmin Kim

공개

2026년 05월 08일

1 정의

정의: Missingness 의 CD 표현

결측을 분석에 포함시키기 위해, 결측 indicator 변수 를 CD 에 노드로 추가하는 Buisson 의 컨벤션 (Buisson, 2021, Ch.6).

[True_X] ──→ Observed_X (관측 가능 — 일부 결측)
              ↑
              │ (결측이 있을 때 hide)
   Missing_X ──┘

핵심:

  • True_X (미관측): 진짜 값 (모든 사람이 갖고 있음)
  • Observed_X (부분 관측): 데이터에 기록된 값 (결측 가능)
  • Missing_X (관측 가능, 0/1): 결측 발생 indicator. 다른 변수가 영향 줄 수 있음.

화살표 방향 컨벤션: True_X → Observed_X, Missing_X → Observed_X.

직관 — 왜 missingness 를 변수로 다루나

분석가의 자연스러운 직감: “결측은 그냥 NA. 변수가 아님.”

이 직감의 함정:

  • 결측 자체가 정보 — “왜 그 사람이 응답 안 했는지” 가 분석에 영향
  • 결측 메커니즘이 결과 변수와 연관 → 편향 일으킴
  • 단순 NA 무시는 그 정보 손실

대신 권장: Missing_X 를 CD 의 노드로 추가. 다른 변수처럼 다룸.

이게 Rubin 분류의 핵심 통찰. 결측을 무시 못 함.

→ Buisson 의 이 컨벤션이 결측 처리의 시각적 언어.

1.1 True_X vs Observed_X 의 분리

왜 두 노드로 분리?

분석가의 단순 모형: “X 가 일부 결측. 끝.”

Buisson 의 분리 모형:

[True_X] (모든 사람이 갖고 있음)
   ↓ (mostly equal, but occasionally hidden)
Observed_X (데이터에 기록 — NA 또는 값)
   ↑
Missing_X (NA 발생 여부)

이 분리가 가능하게 하는 것:

  • MNAR 표현: True_X → Missing_X 화살표로 “값 자체가 결측을 일으킴” 명시
  • 반사실 추론: “만약 결측이 없었다면 True_X 분포가 어떨까?”
  • MAR 보정: True_X 의 분포를 다른 관측 변수로 추정

만약 True_X 와 Observed_X 가 같다면 (단일 노드) MNAR 표현 불가. 화살표 방향이 모호.

직관 — Buisson 의 컨벤션이 왜 화살표를 True_X → Observed_X 로 그리는가

대안: Observed_X → Missing_X (관측된 값이 결측 일으킴) — 이 방향은 의미 없음. NA 는 값이 없는 상태이므로 “값이 자기 자신을 결측 시킴” 표현 어려움.

Buisson 의 컨벤션:

“True_X 가 있고, Missing_X 가 있다. Observed_X = True_X (Missing_X = 0 일 때) 또는 NA (Missing_X = 1 일 때).”

이 framework 에서 “True_X → Missing_X” 가 자연스럽게 의미를 가짐:

  • True_X 의 값이 Missing_X 에 영향 = “어떤 진짜 값을 가진 사람이 응답 회피?”
  • 예: True_Income 이 높은 사람이 Income 항목 응답 회피 → True_X → Missing_X.

→ 이 컨벤션이 MNAR 표현의 기반.

2 Wrong/False Values 의 처리

단순화 가정

Buisson 의 단순화: “값은 정확하거나 결측. 거짓말·기억 오류·입력 오류 없음.”

현실은 다름:

  • 응답자가 거짓 응답 (예: 부끄러운 행동 부정)
  • 기억 오류
  • 데이터 입력 오류

이 경우의 처리:

  • Hidden 변수: 진짜 값 (관측 불가)
  • Observed 변수: 진짜 값 + noise (관측 가능)
  • Noise 의 메커니즘에 따라 분류
[True_X]
   ↓ (with noise)
Observed_X
   ↑
Noise_X (관측 메커니즘)
직관 — False Not At Random

비유: 토피 (toupee) 사례.

  • 토피를 사는 사람 (창피해서) 종종 부정 → “안 샀다” 거짓 응답
  • 토피를 안 사는 사람이 “샀다” 거짓 응답할 가능성 거의 없음

→ Noise 가 True 값 자체에 의존. “False Not At Random” (Rubin 의 분류 응용).

비즈니스 사례:

  • 흡연 빈도 자기 보고 (낮춰 응답)
  • 음주 빈도 자기 보고 (낮춰 응답)
  • 운동 빈도 자기 보고 (높여 응답)

이 분류가 missingness 와 닮은꼴. 처리도 비슷한 도구 (sensitivity analysis, multiple imputation 응용).

이 책 (Buisson) 은 단순화로 결측만 다루지만, 분석가는 noise 도 비슷한 framework 로 사고.

3 Rubin 분류의 CD 표현

3.1 MCAR — Missing Completely At Random

CD
[Random_External_Factors]
            ↓
       Missing_X
            ↓
[True_X] ── Observed_X

   다른 변수 (Y, Z, ...) — Missing_X 에 화살표 없음

특징:

  • Missing_X 의 부모: 외부 무작위 요인뿐
  • 데이터의 어떤 변수도 Missing_X 영향 안 줌
  • True_X 자체도 Missing_X 영향 안 줌
직관 — MCAR 의 CD 의미

“Missing_X 가 다른 변수와 화살표로 연결 안 됨” 의 시각:

  • CD 에서 Missing_X 는 isolated 노드 (외부 random factor 만 입력)
  • 분석에서 다른 변수와 Missing_X 의 상관 ≈ 0

이 시각화가 MCAR 진단의 직관적 첫 인상.

3.2 MAR — Missing At Random

CD
   Y (관측됨) ──→ Missing_X
                     ↓
[True_X] ───── Observed_X

특징:

  • 데이터의 다른 변수 (예: Y) 가 Missing_X 의 부모
  • True_X 자체는 Missing_X 의 부모 아님
  • “관측된 다른 변수가 결측을 설명한다”

C-Mart 사례: “Purchased = 1 인 사람만 인터뷰” → Purchased → Missing_StatedTaste.

비즈니스 사례:

  • Mobile 사용자 (관측됨) 에게만 push 설문 → Mobile → Missing_Survey
  • 가입 1년 이상 (관측됨) 에게만 부가 정보 요청 → Tenure → Missing_BonusInfo
직관 — “다른 관측 변수가 설명” 의 의미

MAR 의 핵심:

  • True_X 의 분포가 결측에 영향 안 주지만
  • 관측 변수 Y 가 결측에 영향 줌
  • Y 를 통제하면 결측이 무작위처럼 작동

비유: 설문 회피가 디바이스 (모바일 vs 데스크톱) 에 의존. 디바이스를 알면 결측 패턴 설명 가능 → 디바이스를 회귀에 포함하면 결측 보정.

수학적으로:

\[P(\text{Missing}_X = 1 \mid \text{True}_X, Y) = P(\text{Missing}_X = 1 \mid Y)\]

True_X 가 우변에서 빠짐 → True_X 자체가 결측 영향 안 줌.

→ Y 가 통제되면 missing 이 random.

3.3 MNAR — Missing Not At Random

CD
[True_X] ──→ Missing_X    (← 핵심 화살표)
   ↓
Observed_X

특징:

  • True_X 자체가 Missing_X 의 부모
  • 다른 변수가 Missing_X 영향을 줄 수도 있지만 (mixed), True_X → Missing_X 가 결정적
  • 진짜 값에 따라 결측 확률 다름
직관 — Hidden vs Observed 화살표

Buisson 의 미묘한 강조:

“MNAR 화살표는 True_X (미관측) 에서 Missing_X 로 그린다, Observed_X 가 아니라.”

이유:

  • “Observed_X → Missing_X” 는 자기 참조: “관측된 값이 결측을 일으킴” — 의미 모호 (값이 결측이면 그 값이 없는데?)
  • “True_X → Missing_X” 는 명확: “진짜 값이 결측을 일으킴” — 의미 있음

예: “월 소득 5000만원 이상인 사람이 응답 회피” - True_Income 가 5000만원 이상이면 (값) → Missing_Income = 1 - Observed_Income 이 NA 가 됨

이 명확함을 위해 hidden 변수에서 화살표.

→ “True_X → Missing_X” 화살표는 반사실적 진술 가능: “임계값 이상 모든 값이 결측” 같은.

직관 — MNAR 의 진짜 의미

MNAR 이 분석가에게 어려운 이유:

  • 결측된 값을 모르므로 True_X → Missing_X 화살표 검증 불가
  • 다른 변수로 결측 설명 안 됨
  • 표준 도구 (mice 등) 의 가정 (MAR) 위반

해결책 (제한적):

  1. Sensitivity analysis: True_X → Missing_X 효과의 강도를 가정값으로 변화 → 결과 흔들림 점검
  2. 외부 데이터: 결측 메커니즘을 알려주는 외부 정보 (예: 다른 데이터셋의 비교)
  3. 선택 모형 (Selection Models): True_X 의 분포 + Missing_X 의 모형을 동시 추정 (가정 강함)

비즈니스 분석가는 MNAR 가정 시 보고서에 명시:

“이 분석은 MAR 가정 하 작성. MNAR 인 경우 결과 편향 가능.”

4 Wrong / False Values — Rubin 분류 응용

가짜 응답의 분류

False values 도 비슷한 분류 가능:

분류 의미
FCAR (False Completely At Random) 무작위 입력 오류 키보드 typo
FAR (False At Random) 다른 관측 변수가 거짓 응답 일으킴 모바일 입력에서 더 많은 typo (디바이스 의존)
FNAR (False Not At Random) 진짜 값이 거짓 응답 일으킴 토피 구매자가 “안 샀다” 응답

각 분류의 처리는 missingness 와 비슷.

5 AirCnC 의 진단 — Logistic Regression

5.1 Diagnostic Tool

진단 도구

각 변수의 결측 여부를 indicator (0/1) 로 만들어, 다른 모든 변수에 대해 logistic regression:

\[ P(\text{Missing}_X = 1) = \sigma(\beta_0 + \beta_Y \cdot Y + \beta_Z \cdot Z + \cdots) \]

해석:

  • 모든 \(\beta\) 가 통계적으로 0 → MCAR (다른 변수가 결측을 설명 못함)
  • 일부 \(\beta\) 가 통계적으로 0 아님 → MAR (그 변수가 결측을 설명)
  • True_X 가 결측을 설명한다는 것은 직접 검증 불가 → MNAR 은 도메인 직관으로

코드 (R):

md_extra_mod <- glm(is.na(extra) ~ .,
                    family = binomial(link = "logit"),
                    data = available_data)
summary(md_extra_mod)

코드 (Python):

import statsmodels.formula.api as smf

available_data["md_extra"] = available_data["extra"].isnull().astype(float)
md_extra_mod = smf.logit(
    "md_extra ~ age + open + neuro + gender + state + bkg_amt",
    data=available_data,
).fit()
print(md_extra_mod.summary())
진단의 한계

이 방법의 한계:

  1. MNAR 진단 불가: True_X 가 결측을 일으키는 경우, True_X 가 결측이므로 회귀에 포함 불가. MNAR 은 통계적으로 검증 불가.
  2. 회귀 가정: Logistic regression 의 가정 (선형성, 독립성) 위반 시 결과 부정확.
  3. 표본 크기: 결측이 매우 적으면 (예: 5%) 회귀의 power 부족.

대안:

  • 도메인 직관 + 통계 (조합)
  • 여러 진단 방법 결합 (logistic regression + correlation matrix + visualization)

→ 진단은 art and science. 정확한 algorithm 없음.

5.2 Extraversion 진단 결과

Buisson 의 결과
Coefficients:
              Estimate    Std. Error   z value   Pr(>|z|)
(Intercept)  -0.7234738   0.7048598   -1.026     0.305
age          -0.0016082   0.0090084   -0.179     0.858
open          0.0557508   0.0425013    1.312     0.190
neuro         0.0501370   0.0705626    0.711     0.477
genderF      -0.0236904   0.1659661   -0.143     0.886
stateB       -0.0780339   0.2000428   -0.390     0.696
stateC       -0.0556228   0.2048822   -0.271     0.786
bkg_amt      -0.0007701   0.0011301   -0.681     0.496

해석:

  • 모든 p-value > 0.1 → 통계적으로 0 과 구분 안 됨
  • 어떤 변수도 Extraversion 결측을 의미 있게 예측 못함
  • → MCAR 의 가능성

결론: Extraversion 결측 = MCAR (또는 MAR with weak signal).

직관 — p-value 해석의 미묘함

분석가가 자주 빠지는 함정: “p > 0.05 면 효과 없음.”

올바른 해석:

  • p > 0.05: “현재 표본으로 효과를 0 과 구분 못 함”
  • 효과가 진짜 0 일 수도, 작아서 검출 못 했을 수도

따라서:

  • 큰 표본에서 p > 0.05 → 효과가 정말 작음 (MCAR 강한 증거)
  • 작은 표본에서 p > 0.05 → 효과 모름 (조심)

AirCnC 의 N = 2000 은 충분히 큰 표본. p > 0.1 모두 → MCAR 가설 합리적.

→ 표본 크기 + p-value 의 결합 해석.

6 “Cautious Is Not Biased” 원칙

Buisson 의 핵심 통찰

“MCAR, MAR, MNAR 어느 분류인지 불확실할 때, 가장 보수적인 (worst-case) 시나리오를 가정하면 분석은 편향되지 않는다.”

의미:

  • MCAR 도구 (listwise deletion) 는 실제 MCAR 일 때만 안전
  • MAR 도구 (Multiple Imputation) 는 MCAR + MAR 둘 다 안전
  • MNAR 도구 (sensitivity analysis) 는 모든 분류 안전

→ 의심스러우면 더 보수적 도구 사용. 시간은 더 걸리지만 편향 없음.

직관 — Cautious Is Not Biased 의 비유

비유: 비를 안 맞으려는 결정.

  • 확실히 맑음 → 우산 안 챙김
  • 비 올 가능성 → 우산 챙김 (비가 안 와도 손해 없음, 비가 오면 보호)
  • 분석가 = 우산 항상 챙김 (시간 좀 더 들지만 안전)

분석에 적용:

  • MCAR 확신 → listwise drop (효율)
  • 의심 → MI (편향 없음)
  • MNAR 의심 → sensitivity analysis (편향 + 한계 명시)

이 보수성이 분석의 신뢰성 유지.

→ 분석가의 default 가 더 보수적인 도구. 효율 손실보다 신뢰성 우선.

6.1 분석가의 결정 트리

Decision Tree
1. Logistic regression of missingness on other vars
   ├── 모든 p > 0.1 (큰 표본) → MCAR 가설
   │   └── 도메인 점검 → MCAR 확신? Yes → listwise OK
   │                                  No → MI
   │
   ├── 일부 p < 0.1 → MAR 의심
   │   ├── 도메인 점검 → True_X 가 결측 일으킬 가능성?
   │   │   ├── No → MAR → MI
   │   │   └── Yes → MNAR 의심 → sensitivity analysis
   │   └── 강한 도메인 직관 (True_X 영향) → MNAR
   │
   └── 도메인 직관: 결측 변수 자체가 결측 일으킴
       └── MNAR → sensitivity analysis + 보고서 명시

이 트리가 진단 절차.

7 AirCnC 변수별 진단

7.1 State 변수

직관 — State 결측의 패턴

가설: 신규 가입자에게 State 가 옵션 (불필요)

  • Tenure (가입 기간) 가 짧을수록 State 결측 ↑

진단:

df["md_state"] = df["state"].isnull().astype(float)
mod = smf.logit("md_state ~ age + open + neuro + gender + bkg_amt", data=df).fit()

만약 age, bkg_amt 등 어느 변수가 의미 있는 효과 → MAR.

도메인 점검: “State 의 진짜 값이 결측을 일으키는가?” — 일반적으로 No (어느 주에 사는지가 응답 회피 의지에 영향 안 줌).

→ MAR 가설.

7.2 Bkg_amt 변수

직관 — Bkg_amt 결측은 MNAR 의심

가설: 예약 안 한 고객에게 Bkg_amt 가 의미 없음 (= 0 또는 NA)

  • 만약 NA 처리: Bkg_amt = 0 인 고객만 결측 → True_Bkg_amt = 0 일 때만 결측 → MNAR
  • 만약 0 처리: 결측 없음

이 사례는 “결측의 정의” 자체에 의존. NA 가 0 의 의미인가, 아니면 무관 NA 인가.

도메인 인터뷰 필수.

7.3 Neuro 변수

직관 — Neuro 50% 결측의 분석

가설: 성격 검사를 끝까지 안 한 응답자가 Neuro 결측

  • Neuro 가 가장 마지막 항목이라면 → 응답자의 인내심·신경증 등이 영향 가능
  • “Neuroticism 높은 사람이 끝까지 안 함” → True_Neuro → Missing_Neuro → MNAR!

이 가설이 옳다면:

  • 결측 안 된 응답자 = 신경증 낮은 사람만 → 표본 편향
  • Neuro 의 평균이 진짜보다 낮게 추정
  • Bkg_amt 와의 관계 추정도 편향

진단:

df["md_neuro"] = df["neuro"].isnull().astype(float)
# True_Neuro 는 결측이지만, 다른 변수로 부분 검증 가능
mod = smf.logit("md_neuro ~ age + open + extra + gender + state + bkg_amt", data=df).fit()

만약 통계적으로 0 → 다른 변수로 결측 설명 안 됨 → MCAR 또는 MNAR. 도메인 직관 + 데이터 → 어느 쪽인지 결정.

8 응용 — 비즈니스 사례

8.1 SaaS 사용 데이터

결측 분류 진단

분석 대상: 30 일 retention.

데이터:

  • 시그업: 모두 관측
  • 활동 metric: 일부 결측 (비활성 사용자)
  • 만족 설문: 50% 결측

진단:

변수 가설 진단 도구
활동 metric 비활성 = True_Active 가 낮음 → 결측 발생 logistic regression — 활동 외 변수가 결측 설명 못하면 MNAR
만족 설문 응답 회피 가능 (특정 사용자 유형) logistic regression — 사용자 유형이 결측 설명하면 MAR

8.2 헬스케어 — 추적 손실

CONSORT 진단

임상 시험 결측 분류:

  • Treatment 군에서 결측 ↑ → Treatment 가 결측 일으킴 → 부작용 가설 → MNAR 의심
  • Baseline severity 와 결측 상관 → Severity → Missing → MAR
  • 시간 따라 결측 ↑ → Time → Missing → MAR (시간 통제 시)

분석:

df["md_followup"] = df["6mo_outcome"].isnull().astype(float)
mod = smf.logit(
    "md_followup ~ treatment + age + baseline_severity + study_arm",
    data=df,
).fit()

Treatment 의 effect 가 의미 있는 양수 → 부작용 의심 → MNAR.

9 코드 예시 — Python 으로 진단 자동화

9.1 변수별 진단 함수

import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
import warnings
warnings.filterwarnings("ignore")


def diagnose_missing(df, target_var, predictors=None, alpha=0.1):
    """
    한 변수의 결측을 다른 변수에 대해 logistic regression 으로 진단.

    Returns:
        dict — 진단 결과
    """
    if not df[target_var].isnull().any():
        return {"target": target_var, "status": "no_missing"}

    df = df.copy()
    df["__missing"] = df[target_var].isnull().astype(int)

    if predictors is None:
        predictors = [c for c in df.columns if c not in [target_var, "__missing"]]

    # 범주 변수 dummy 생성
    formula = "__missing ~ " + " + ".join(predictors)

    try:
        model = smf.logit(formula, data=df).fit(disp=False)
    except Exception as e:
        return {"target": target_var, "status": "regression_failed", "error": str(e)}

    significant = []
    for var in predictors:
        # dummy 변수의 경우 변수 이름이 var[T.X] 형태
        for col in model.params.index:
            if var in col and col != "Intercept":
                p = model.pvalues[col]
                if p < alpha:
                    significant.append({"var": col, "coef": model.params[col], "p": p})

    if not significant:
        diagnosis = "MCAR_가설"
        explanation = "다른 어떤 변수도 결측을 의미 있게 설명 못 함."
    else:
        diagnosis = "MAR_가설"
        explanation = f"다음 변수가 결측에 영향: {[s['var'] for s in significant]}"

    return {
        "target": target_var,
        "missing_pct": df["__missing"].mean() * 100,
        "diagnosis": diagnosis,
        "explanation": explanation,
        "significant": significant,
    }


# AirCnC 시뮬레이션
np.random.seed(42)
n = 2000

age = np.random.normal(40, 12, n).clip(18, 80)
gender = np.random.choice(["F", "M"], n)
state = np.random.choice(["A", "B", "C"], n)
openness = np.random.normal(5, 2, n).clip(0, 10)
extraversion = np.random.normal(5, 2, n).clip(0, 10)
neuroticism = np.random.normal(5, 2, n).clip(0, 10)
bkg_amt = (5 * age + 15 * openness - 10 * neuroticism + np.random.normal(500, 100, n)).clip(0, 1000)

df = pd.DataFrame({
    "age": age, "gender": gender, "state": state,
    "open": openness, "extra": extraversion, "neuro": neuroticism,
    "bkg_amt": bkg_amt,
})

# MAR: State 결측이 age 에 의존
df.loc[(age > 60) & (np.random.uniform(0, 1, n) < 0.7), "state"] = np.nan
# MAR: Extra 결측이 무작위 + 약간 age 의존
df.loc[np.random.uniform(0, 1, n) < 0.4, "extra"] = np.nan
# MNAR-ish: Neuro 결측이 Neuro 자체에 의존
mnar_prob = 1 / (1 + np.exp(-(0.5 * (neuroticism - 5))))
df.loc[np.random.uniform(0, 1, n) < mnar_prob, "neuro"] = np.nan

# 진단
print("=== 변수별 결측 진단 ===")
for var in ["state", "extra", "neuro"]:
    if var == "state":
        # 범주 변수는 따로
        result = diagnose_missing(
            df, var,
            predictors=["age", "open", "neuro", "gender", "bkg_amt"],
        )
    elif var == "extra":
        result = diagnose_missing(
            df, var,
            predictors=["age", "open", "neuro", "gender", "state", "bkg_amt"],
        )
    elif var == "neuro":
        # Neuro 자체로는 회귀 못함 — 다른 변수로
        result = diagnose_missing(
            df, var,
            predictors=["age", "open", "extra", "gender", "state", "bkg_amt"],
        )

    print(f"\n{var.upper()}:")
    print(f"  결측 비율: {result.get('missing_pct', 0):.1f}%")
    print(f"  진단: {result.get('diagnosis', '-')}")
    print(f"  설명: {result.get('explanation', '-')}")
    if result.get("significant"):
        for s in result["significant"]:
            print(f"    - {s['var']}: coef = {s['coef']:.3f}, p = {s['p']:.4f}")
직관 — 자동 진단의 강점과 한계

이 도구가 분석가에게 주는 것:

  • 변수마다 일관된 진단 절차
  • 통계적으로 의미 있는 변수 자동 추출
  • 진단 보고서 자동 생성

한계:

  • True_X → Missing_X (MNAR) 는 진단 불가 (True_X 가 결측이므로)
  • 도메인 직관 통합 필수
  • 통계 결과를 맹목적으로 신뢰하면 안 됨

→ 자동 진단 + 도메인 인터뷰의 결합. 양쪽 다 필요.

9.2 True_X 의 영향 우회 진단

def detect_mnar_signal(df, target_var, related_var):
    """
    MNAR 의심 신호 점검 — 결측된 사람들의 다른 관측 변수 분포 비교.

    Approach: 만약 결측이 True_X 에 의존하고, X 가 다른 변수 Y 와 상관되어 있다면,
    결측된 사람들의 Y 분포가 결측 안 된 사람과 다를 가능성.
    """
    missing_group = df[df[target_var].isnull()]
    observed_group = df[df[target_var].notna()]

    if related_var not in df.columns:
        return None

    # 평균 비교
    miss_mean = missing_group[related_var].mean()
    obs_mean = observed_group[related_var].mean()

    # 분산 비교
    miss_std = missing_group[related_var].std()
    obs_std = observed_group[related_var].std()

    return {
        "target": target_var,
        "related": related_var,
        "miss_mean": miss_mean,
        "obs_mean": obs_mean,
        "diff_mean": miss_mean - obs_mean,
        "miss_std": miss_std,
        "obs_std": obs_std,
    }


# Neuro 결측에서 다른 변수 분포 비교
print("\n=== Neuro 결측 시 다른 변수 분포 ===")
for var in ["age", "open", "extra", "bkg_amt"]:
    result = detect_mnar_signal(df, "neuro", var)
    if result:
        print(f"  {var}: 결측 그룹 평균 {result['miss_mean']:.2f} vs 관측 그룹 평균 {result['obs_mean']:.2f}")
        print(f"     차이: {result['diff_mean']:.2f}")
직관 — MNAR 신호의 우회 검출

True_Neuro 가 직접 보이지 않지만:

  • True_Neuro 와 다른 변수 (예: bkg_amt) 가 인과적으로 연결
  • True_Neuro → Missing_Neuro 가 있으면, 결측 그룹의 bkg_amt 평균 ≠ 관측 그룹의 bkg_amt 평균

만약 Bkg_amt 평균이 두 그룹에서 다르다면:

  • True_Neuro 가 두 그룹에서 다름 (가설)
  • True_Neuro 가 Missing_Neuro 일으킴 (MNAR 신호)

이 우회 진단이 MNAR 의 간접 증거.

→ 도메인 인터뷰 + 우회 진단으로 MNAR 의심 가능.

9.3 진단 종합 보고서

def diagnostic_report(df, missing_vars, all_predictors):
    """모든 결측 변수에 대한 진단 종합."""
    print("=" * 60)
    print("결측 데이터 진단 종합 보고서")
    print("=" * 60)

    for var in missing_vars:
        if var not in df.columns or not df[var].isnull().any():
            continue
        predictors = [v for v in all_predictors if v != var]
        result = diagnose_missing(df, var, predictors=predictors)

        print(f"\n[{var}]")
        print(f"  결측 비율: {result.get('missing_pct', 0):.1f}%")
        print(f"  통계 진단: {result.get('diagnosis', '-')}")
        if result.get("significant"):
            print(f"  영향 변수:")
            for s in result["significant"]:
                print(f"    - {s['var']}: coef = {s['coef']:.3f}, p = {s['p']:.4f}")
        print(f"  → 다음 단계: 도메인 인터뷰로 True_X → Missing_X 가능성 점검")


diagnostic_report(
    df,
    missing_vars=["state", "extra", "neuro"],
    all_predictors=["age", "open", "extra", "neuro", "gender", "state", "bkg_amt"],
)
직관 — 보고서의 출력

분석가가 이 보고서를 받으면:

  1. 변수마다 결측 비율 + 통계 진단
  2. 통계로 잡히는 영향 변수 목록
  3. 다음 단계 안내 (도메인 인터뷰)

이 보고서가 결측 처리 작업의 첫 산출물. 다음 단계 (Multiple Imputation 적용) 의 입력.

→ 자동 진단 → 도메인 검증 → 처리. 3 단계 워크플로.

10 진단 단계의 결정

진단 후 다음 단계

진단 결과에 따라:

진단 다음 단계
MCAR (강한 신뢰) Listwise deletion 또는 MI (선택)
MCAR (낮은 신뢰) MI (보수적)
MAR MI (영향 변수 포함)
MNAR (의심) Sensitivity analysis + 보고서 명시
MNAR (강한 신뢰) 외부 데이터·실험 또는 한계 인정

대부분의 비즈니스 분석에서 default 는 MI (보수적, 편향 작음).

→ 진단 단계의 산출물 = 다음 단계 도구 선택.

11 관련 주제

11.1 Ch.6 의 형제 글

11.2 이전 챕터

11.4 카테고리 진입점

Subscribe

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