Deconfounding 개관 — 두 가지 처방의 균형 (Buisson Ch.5 overview)

Disjunctive Cause Criterion vs Backdoor Criterion — 단순함과 정밀함의 trade-off

Buisson (2021) Ch.5 의 전체 흐름을 압축한 overview. CD 가 주어졌을 때 어떤 변수를 회귀에 포함해 confounding 을 제거할지 결정하는 두 가지 규칙 (DCC, BC) 의 정의· trade-off·적용 사례를 정리한다. C-Mart 의 아이스크림·생수 매출 사례로 두 규칙을 비교 시연.

Experimentation
Causal Inference
저자

Kwangmin Kim

공개

2026년 05월 08일

1 정의

정의: Deconfounding

CD 가 주어졌을 때, 회귀에 어떤 변수를 통제 (control) 변수로 포함해 confounding bias 를 제거할지 결정하는 절차 (Buisson, 2021, Ch.5).

두 가지 규칙:

  1. Disjunctive Cause Criterion (DCC) — 단순하고 안전한 규칙
  2. Backdoor Criterion (BC) — 정밀하고 최소한의 변수만 통제

두 규칙은 CD 만 있으면 데이터 없이 적용 가능. CD 가 정확하다는 가정 하.

직관 — 데이터 없이 결정한다는 의미

지금까지 (E-BUI4) 우리는 데이터로 CD 를 검증했다. 그러나 CD 가 일단 정해지면, 어떤 변수를 통제할지 의 결정은 데이터를 더 보지 않아도 된다.

데이터 검증 → CD 확정 → 데이터 없이 통제 변수 결정 → 회귀

이 분리가 분석가의 작업을 명확히 함:

  • 단계 A: 데이터 + 도메인 직관으로 CD 짓기
  • 단계 B: CD 만 보고 통제 변수 결정 (DCC 또는 BC)
  • 단계 C: 데이터로 회귀 적합

각 단계가 다른 사고를 요구. CD 가 두 단계의 다리.

2 개념 및 원리 — 두 규칙의 trade-off

2.1 DCC vs BC 비교

Trade-off 매트릭스
항목 DCC BC
이해 난이도 쉬움 어려움 (path 개념 필요)
CD 정확성 요구 부분 OK 완전 정확 필요
통제 변수 수 많음 (중복 포함) 최소
데이터 필요량 많음 (모든 cause 변수 필요) 적음
편향 제거 보장 충분 (sufficient) — over-control 안전 필요충분 (necessary and sufficient)
언제 사용 CD 불확실, 안전 우선 CD 정확, 효율 우선
직관 — 두 규칙의 비유
  • DCC = 과보호 부모: “혹시 모르니 모든 cause 변수를 다 통제하자.” 안전하지만 비효율.
  • BC = 정밀 외과의: “정확히 confounder 만 통제. 나머지는 손대지 않는다.” 효율적이지만 CD 가 틀리면 위험.

비즈니스 분석에서 일반적 권장:

  • CD 가 잘 검증되었으면 → BC (효율, 변수 절약)
  • CD 가 불확실하면 → DCC (안전, 잔여 confounding 보험)

두 규칙을 동시에 사용해도 됨. DCC 가 BC 의 변수를 포함하므로 둘 다 적용 시 결과 동일 (또는 DCC 가 더 많이 통제).

2.2 DCC — Disjunctive Cause Criterion

DCC 의 정의

“Treatment (cause of interest) 또는 Outcome (effect of interest) 의 직접 원인 (direct cause) 인 모든 변수를 회귀에 포함하라. 단, treatment 와 outcome 사이의 mediator 는 제외.”

수식 표기:

\[ \text{Controls}_{\text{DCC}} = \{ V \in \text{CD} : V \to T \text{ or } V \to Y \} \setminus \{ \text{mediators} \} \]

여기서 \(T\) = Treatment, \(Y\) = Outcome.

직관 — DCC 가 왜 충분한가

핵심 통찰:

  • Confounder 의 정의 = \(T\)\(Y\) 의 공통 원인 = \(T\) 의 원인 AND \(Y\) 의 원인
  • DCC = “T 의 원인 OR Y 의 원인” 모두 포함
  • 따라서 DCC 는 모든 confounder 를 자동 포함 (T 의 원인 ∩ Y 의 원인 ⊂ T 의 원인 ∪ Y 의 원인)

대신 비효율:

  • \(T\) 만의 원인 (Y 와 무관) — confounder 아니지만 통제됨 (불필요)
  • \(Y\) 만의 원인 (T 와 무관) — confounder 아니지만 통제됨 (불필요)

→ “혹시 모르니까” 의 안전 도구.

DCC 의 한계

DCC 가 모든 cause 변수를 통제하라고 요구하지만:

  • 데이터 부족: 일부 cause 가 미관측이면 DCC 적용 불가
  • Mediator 제외 결정: 어떤 변수가 mediator 인지 식별 필요 (CD 의 부분 정확성 요구)
  • Effect Modifier 처리: DCC 는 effect modifier 를 통제 변수로 처리 (over-adjustment 위험 있을 수도)

이런 한계를 BC 가 보완.

2.3 BC — Backdoor Criterion

BC 의 정의

\(T\)\(Y\) 사이의 모든 noncausal path (backdoor path) 를 차단할 수 있는 최소한의 변수 집합을 회귀에 포함하라.”

용어 정의:

  • Path: \(T\)\(Y\) 사이의 화살표 연결 (방향 무관, 변수 중복 없음). E-BUI3-3 참조
  • Causal Path: 모든 화살표가 같은 방향 (chain) — \(T\) 의 진짜 인과 효과 전달
  • Noncausal Path (Backdoor Path): 그 외의 모든 path — confounder 또는 collider 가 끼어 있음
  • Blocked Path: 통제로 차단된 path
  • Unblocked Path: 차단 안 된 path → confounding 발생
Path 의 차단 규칙

Path 가 차단되는 조건:

  1. Path 위의 변수 (collider 가 아닌) 가 회귀에 포함됨, OR
  2. Path 위에 collider 가 있고, 그 collider 가 회귀에 포함되지 않음

Path 가 차단되지 않은 (unblocked) 조건:

  1. Path 위에 통제된 변수가 없음, AND
  2. Path 위의 모든 collider 의 자식이 통제됨 (또는 collider 자체가 통제됨)

→ Collider 의 통제는 path 를 개방, 다른 변수의 통제는 path 를 차단.

직관 — Blocked vs Unblocked

“차단” 의 의미:

  • Path 위에 적절한 변수가 있으면 statistical 정보가 통과 못함
  • 통과 못하면 confounding bias 발생 안 함

비유:

  • Path = 강
  • 통제 변수 = 댐
  • 정보 = 물의 흐름

댐을 적절히 세우면 물이 안 흐름. 그러나 collider (지하수 통로) 를 잘못 막으면 새 길이 열림.

→ 어떤 변수를 통제할지 결정은 댐을 어디에 세울지 결정.

3 C-Mart 사례 — 아이스크림·생수 매출

3.1 비즈니스 문제

분석 질문

C-Mart 마케팅 부서가 “Healthy Customer” 보고서를 발표 후, 패스트푸드·아이스크림 코너에 “생수 어떠세요?” 캠페인 시작.

분석 목표: 아이스크림 매출이 생수 매출에 미치는 인과 효과를 편향 없이 추정.

CD:

                                   AverageCustomerAge
                                          ↓
    NumberOfCustomers ──→ IceCreamSales ──→ BottledWaterSales
            ↓                                       ↑
       BurgerSales                          CustomerHealthMindset
            ↓                                       ↑
      FrenchFrySales ────────────────────────────────┘
                              ↓
                          (BottledWaterSales)
                              ↑
                         (자체 화살표)
            ↓
        SodaSales
            ↑
       (AverageAge, HealthMindset 둘 다 영향)

(단순화하면) 두 개념 블록:

  1. Block 1 (좌상단): NumberOfCustomers, BurgerSales, FrenchFrySales
  2. Block 2 (우하단): AverageCustomerAge, HealthMindset, SodaSales

3.2 DCC 적용

Block 1 — DCC

IceCreamSales 의 원인:

  • NumberOfCustomers (직접 원인)

BottledWaterSales 의 원인:

  • FrenchFrySales (직접 원인)

DCC 가 요구:

  • NumberOfCustomers 통제 (IceCreamSales 의 원인)
  • FrenchFrySales 통제 (BottledWaterSales 의 원인)

mediator 없음 → 둘 다 포함.

Block 2 — DCC

IceCreamSales 의 원인:

  • AverageCustomerAge

BottledWaterSales 의 원인:

  • CustomerHealthMindset

DCC 가 요구:

  • AverageCustomerAge 통제
  • CustomerHealthMindset 통제

문제: 두 변수 모두 미관측 (설문 데이터로만 평균 알 수 있음, 개별 sale 단위 측정 불가)

→ DCC 는 Block 2 를 처리 못함. 잔여 confounding 가능성 명시.

3.3 BC 적용

Block 1 — BC

T = IceCreamSales, Y = BottledWaterSales.

T 로 들어오는 화살표 (T 의 원인):

  • NumberOfCustomers (Block 1)
  • AverageCustomerAge (Block 2)

각 원인을 따라가는 path 점검 (Block 1 만):

Path: T ← NumberOfCustomers → BurgerSales → FrenchFrySales → Y

  • noncausal? Yes (NumberOfCustomers 에서 fork)
  • blocked by default? No (collider 없음, 통제 안 함)
  • → confounding path → 차단 필요

차단 방법: path 위 비-collider 중 하나 통제.

선택지: NumberOfCustomers, BurgerSales, FrenchFrySales 중 하나.

Buisson 의 권장: 가장 가까운 cause (T 의 직접 원인) 를 선택 → NumberOfCustomers.

Block 2 — BC

Path: T ← AverageCustomerAge → SodaSales ← CustomerHealthMindset → Y

  • noncausal? Yes (AverageAge 에서 fork, SodaSales 에서 collider, HealthMindset 에서 fork)
  • blocked by default? Yes (SodaSales 가 collider, 통제 안 됨 → 자동 차단)

차단 불필요. SodaSales 의 자연스러운 collider 가 path 를 닫음.

→ AverageAge, HealthMindset 데이터가 없어도 OK (BC 는 차단할 필요 없음).

3.4 DCC vs BC — 같은 사례, 다른 결정

결과 비교
변수 DCC BC
NumberOfCustomers 포함 포함
FrenchFrySales 포함 제외 (NumberOfCustomers 통제로 path 자동 차단)
AverageCustomerAge 포함 (그러나 미관측) 제외
HealthMindset 포함 (그러나 미관측) 제외
SodaSales (DCC 는 cause 가 아니므로 무시) 제외 (절대 통제 금지 — collider)

DCC: 4 변수 (2 미관측, 분석 어려움) — 잔여 confounding 가능 BC: 1 변수 (NumberOfCustomers) — 분석 가능, 깨끗

직관 — BC 의 우월성 (이 사례에서)

BC 가 보여주는 것:

  1. 불필요한 변수 제거: FrenchFrySales 가 redundant — NumberOfCustomers 통제로 같은 path 가 자동 차단
  2. 미관측 변수 무시 가능: SodaSales 의 collider 효과 덕분에 Block 2 의 미관측 변수가 confounder 아님
  3. 명확한 가이드: SodaSales 를 통제하면 collider bias 가 발생한다는 경고

DCC 라면: “Block 2 의 미관측 변수로 인해 잔여 confounding 가능” — 분석가가 결과 해석 시 주의해야 함. BC 라면: “Block 2 는 자동 처리됨” — 깨끗한 결론.

→ BC 가 가능한 상황에서는 BC 가 압도적으로 유리.

4 왜 필요한가 — 변수 선택의 통계적 의미

4.1 “모든 변수를 회귀에 넣자” 의 위험

Over-control 의 결과

분석가가 “안전을 위해 모든 변수를 회귀에” 넣으면:

  1. Mediator 통제 → over-adjustment: \(T\) 의 인과 효과를 일부 차단 (E-BUI3-2)
  2. Collider 통제 → selection bias: 가짜 상관 생성 (E-BUI3-3)
  3. 다중공선성: 표준 오차 폭증, 신뢰구간 무의미

→ “모든 변수” 는 안전하지 않음. 정확한 변수 선별이 분석의 핵심.

4.2 Why DCC 가 mediator 만 제외하는가

직관

DCC 의 정의에서 “mediator 는 제외” 가 왜 중요한가:

T (treatment) ──→ M (mediator) ──→ Y (outcome)

M 을 통제하면 → T 의 효과가 M 을 거치는 경로가 차단됨 → T 의 총 효과 추정 불가.

분석 목적이 총 효과 (total effect) 면 mediator 는 절대 통제 안 함.

분석 목적이 직접 효과 (direct effect) 면 mediator 통제 가능 (mediation 분석, E-BUI12).

→ 분석 목적이 무엇인지 명확해야 mediator 처리 결정.

5 응용 — 다양한 비즈니스 사례

5.1 호텔 NRD 사례 (E-BUI4)

DCC vs BC

호텔 사례의 단순화된 CD:

Personal Char ──→ NRD ──→ Cancel
              ───↗     ↗
PrevCancel ──→
ADR ──→
Country ──→

DCC: NRD 와 Cancel 의 모든 직접 원인 → Personal Char, PrevCancel, ADR, Country 모두 포함.

BC: NRD 의 모든 backdoor path 를 차단할 최소 집합. PrevCancel, ADR, Country 가 path 위에 있으니 통제. Personal Char 가 다른 변수에 흡수되면 부분 통제.

차이: 둘 다 거의 동일 (단순한 fork 구조). 복잡한 CD 일수록 BC 의 효율성이 두드러짐.

5.2 SaaS Onboarding 사례

DCC vs BC
Past Experience    Job Role         Cohort
       ↓             ↓                ↓
       └──→ Tutorial ──→ Retention
       └────────────────→
       └────────────────→
              (Past Experience 와 Cohort 도 직접 영향)

DCC: Tutorial 의 원인 (Past Exp, Job Role) + Retention 의 원인 (Past Exp, Cohort) → 모두 포함.

BC: Tutorial 의 backdoor path 차단 → Past Exp 가 Tutorial 과 Retention 둘 다 영향이면 통제. Cohort 가 Retention 만 영향 (Tutorial 과 무관) 이면 BC 는 통제 불필요. DCC 는 Cohort 도 통제 (cause of Y).

→ DCC 가 더 많이 통제 (Cohort 까지). 효율성 손실, 정밀성 ↑.

6 코드 예시 — Python 으로 DCC, BC 결정

6.1 DCC 자동 결정

import networkx as nx

def dcc_controls(G, T, Y):
    """DCC 에 따른 통제 변수 자동 결정."""
    # T 의 직접 원인
    causes_T = set(G.predecessors(T))
    # Y 의 직접 원인
    causes_Y = set(G.predecessors(Y))
    # Mediator: T 의 자식이면서 Y 의 부모
    descendants_T = nx.descendants(G, T)
    mediators = descendants_T & set(G.predecessors(Y))

    # DCC = (causes_T ∪ causes_Y) \ mediators
    return (causes_T | causes_Y) - mediators - {T, Y}


# C-Mart 아이스크림·생수 사례
G = nx.DiGraph()
G.add_edges_from([
    ("NumberOfCustomers", "IceCreamSales"),
    ("NumberOfCustomers", "BurgerSales"),
    ("BurgerSales", "FrenchFrySales"),
    ("FrenchFrySales", "BottledWaterSales"),
    ("AverageCustomerAge", "IceCreamSales"),
    ("AverageCustomerAge", "SodaSales"),
    ("CustomerHealthMindset", "BottledWaterSales"),
    ("CustomerHealthMindset", "FrenchFrySales"),
    ("CustomerHealthMindset", "SodaSales"),
    ("NumberOfCustomers", "SodaSales"),
    ("IceCreamSales", "BottledWaterSales"),
])

print("=== DCC 결정 ===")
controls = dcc_controls(G, "IceCreamSales", "BottledWaterSales")
print(f"통제 변수: {sorted(controls)}")
직관 — 자동화의 가치

DCC 결정이 알고리즘적이라는 사실 — 분석가가 직접 변수 목록을 따로 만들 필요 없음.

networkx 만 있으면 자동.

그러나 mediator 식별 이 핵심. CD 가 정확해야 mediator 가 제대로 잡힘.

6.2 BC 자동 결정 (단순화 버전)

def bc_simple_controls(G, T, Y):
    """
    BC 의 단순화된 결정 — T 의 모든 ancestor 가 path 위에 있으면 통제.
    실제 BC 는 path 분석 + collider 처리로 더 복잡.
    """
    # T 의 ancestor 중 Y 와 connection 이 있는 변수
    ancestors_T = nx.ancestors(G, T)
    candidate_controls = set()
    for a in ancestors_T:
        try:
            # a 에서 Y 로 가는 path 가 있는가
            if nx.has_path(G, a, Y):
                candidate_controls.add(a)
        except nx.NodeNotFound:
            pass
    return candidate_controls - {T, Y}


print("\n=== BC 단순화 결정 ===")
bc_controls = bc_simple_controls(G, "IceCreamSales", "BottledWaterSales")
print(f"후보 통제 변수: {sorted(bc_controls)}")

# 그 중 직접 원인부터
direct_causes_T = set(G.predecessors("IceCreamSales"))
print(f"\nT 의 직접 원인 우선: {direct_causes_T & bc_controls}")
직관 — 진짜 BC 의 복잡성

위 코드는 단순화 — 진짜 BC 는:

  1. T 와 Y 사이 모든 path 열거
  2. 각 path 가 causal 인지 noncausal 인지 분류
  3. Noncausal path 가 자연스럽게 차단되는지 점검 (collider 존재)
  4. 차단 안 된 noncausal path 를 통제로 차단

이 알고리즘은 dowhy 같은 인과 추론 라이브러리에 구현됨.

from dowhy import CausalModel
# ... CausalModel 생성 후 ...
identified = model.identify_effect()
# identified.get_backdoor_variables()  ← BC 의 결과

→ 비즈니스 분석가는 직접 구현하지 말고 라이브러리 사용. 이론은 이해, 적용은 자동화.

6.3 시뮬레이션 — DCC vs BC 의 추정 비교

import numpy as np
import pandas as pd
import statsmodels.api as sm

np.random.seed(42)
n = 5000

# 데이터 생성
n_customers = np.random.normal(100, 30, n)
age = np.random.normal(35, 10, n)
mindset = np.random.normal(0, 1, n)

# Confounder 효과
ice_cream = 0.3 * n_customers + 0.2 * (45 - age) + np.random.normal(0, 5, n)
burger = 0.5 * n_customers + np.random.normal(0, 5, n)
fries = 0.6 * burger + 0.3 * mindset + np.random.normal(0, 5, n)
soda = 0.2 * n_customers + 0.3 * (45 - age) + 0.2 * mindset + np.random.normal(0, 5, n)
water = (
    0.0 * ice_cream  # 진짜 인과 효과 = 0
    + 0.4 * fries
    + 0.5 * mindset
    + np.random.normal(0, 5, n)
)

df = pd.DataFrame({
    "ice_cream": ice_cream, "water": water,
    "n_customers": n_customers, "burger": burger,
    "fries": fries, "soda": soda,
    # age, mindset 는 미관측 가정
})

# 회귀 1: 단순 — 통제 안 함
m1 = sm.OLS(df["water"], sm.add_constant(df["ice_cream"])).fit()
print(f"=== 단순: water ~ ice_cream ===")
print(f"  beta = {m1.params['ice_cream']:.3f} (진짜 0)")

# 회귀 2: DCC — n_customers + fries
m2 = sm.OLS(df["water"], sm.add_constant(df[["ice_cream", "n_customers", "fries"]])).fit()
print(f"\n=== DCC: water ~ ice_cream + n_customers + fries ===")
print(f"  beta = {m2.params['ice_cream']:.3f}")

# 회귀 3: BC — n_customers 만 (fries 는 redundant)
m3 = sm.OLS(df["water"], sm.add_constant(df[["ice_cream", "n_customers"]])).fit()
print(f"\n=== BC: water ~ ice_cream + n_customers ===")
print(f"  beta = {m3.params['ice_cream']:.3f}")

# 회귀 4: 잘못된 통제 — soda 추가 (collider)
m4 = sm.OLS(df["water"], sm.add_constant(df[["ice_cream", "n_customers", "soda"]])).fit()
print(f"\n=== 잘못: water ~ ice_cream + n_customers + soda ===")
print(f"  beta = {m4.params['ice_cream']:.3f} ← collider 통제로 편향")
직관 — 시뮬레이션 결과

예상:

  • 단순: \(\beta \approx 0.5\) (가짜 양 — confounding)
  • DCC: \(\beta \approx 0.0\) (정확)
  • BC: \(\beta \approx 0.0\) (정확, 더 적은 변수)
  • Soda 추가: \(\beta \approx -0.1\) (잘못된 음 — collider bias)

DCC 와 BC 가 같은 정확도. BC 가 더 적은 변수로 도달. Soda 통제는 collider bias 를 만들어 추정을 망침.

→ 이론을 모르고 변수를 추가하면 위험. CD + DCC/BC 가 안전한 가이드.

7 관련 주제

7.1 Ch.5 의 형제 글

7.2 이전 챕터

7.3 후속 챕터

7.5 카테고리 진입점

Subscribe

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