1 정의
CD 가 주어졌을 때, 회귀에 어떤 변수를 통제 (control) 변수로 포함해 confounding bias 를 제거할지 결정하는 절차 (Buisson, 2021, Ch.5).
두 가지 규칙:
- Disjunctive Cause Criterion (DCC) — 단순하고 안전한 규칙
- 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 비교
| 항목 | 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
“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.
핵심 통찰:
- Confounder 의 정의 = \(T\) 와 \(Y\) 의 공통 원인 = \(T\) 의 원인 AND \(Y\) 의 원인
- DCC = “T 의 원인 OR Y 의 원인” 모두 포함
- 따라서 DCC 는 모든 confounder 를 자동 포함 (T 의 원인 ∩ Y 의 원인 ⊂ T 의 원인 ∪ Y 의 원인)
대신 비효율:
- \(T\) 만의 원인 (Y 와 무관) — confounder 아니지만 통제됨 (불필요)
- \(Y\) 만의 원인 (T 와 무관) — confounder 아니지만 통제됨 (불필요)
→ “혹시 모르니까” 의 안전 도구.
DCC 가 모든 cause 변수를 통제하라고 요구하지만:
- 데이터 부족: 일부 cause 가 미관측이면 DCC 적용 불가
- Mediator 제외 결정: 어떤 변수가 mediator 인지 식별 필요 (CD 의 부분 정확성 요구)
- Effect Modifier 처리: DCC 는 effect modifier 를 통제 변수로 처리 (over-adjustment 위험 있을 수도)
이런 한계를 BC 가 보완.
2.3 BC — Backdoor Criterion
“\(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 위의 변수 (collider 가 아닌) 가 회귀에 포함됨, OR
- Path 위에 collider 가 있고, 그 collider 가 회귀에 포함되지 않음
Path 가 차단되지 않은 (unblocked) 조건:
- Path 위에 통제된 변수가 없음, AND
- Path 위의 모든 collider 의 자식이 통제됨 (또는 collider 자체가 통제됨)
→ Collider 의 통제는 path 를 개방, 다른 변수의 통제는 path 를 차단.
“차단” 의 의미:
- 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 둘 다 영향)
(단순화하면) 두 개념 블록:
- Block 1 (좌상단): NumberOfCustomers, BurgerSales, FrenchFrySales
- Block 2 (우하단): AverageCustomerAge, HealthMindset, SodaSales
3.2 DCC 적용
IceCreamSales 의 원인:
- NumberOfCustomers (직접 원인)
BottledWaterSales 의 원인:
- FrenchFrySales (직접 원인)
DCC 가 요구:
- NumberOfCustomers 통제 (IceCreamSales 의 원인)
- FrenchFrySales 통제 (BottledWaterSales 의 원인)
mediator 없음 → 둘 다 포함.
IceCreamSales 의 원인:
- AverageCustomerAge
BottledWaterSales 의 원인:
- CustomerHealthMindset
DCC 가 요구:
- AverageCustomerAge 통제
- CustomerHealthMindset 통제
문제: 두 변수 모두 미관측 (설문 데이터로만 평균 알 수 있음, 개별 sale 단위 측정 불가)
→ DCC 는 Block 2 를 처리 못함. 잔여 confounding 가능성 명시.
3.3 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.
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 가 보여주는 것:
- 불필요한 변수 제거: FrenchFrySales 가 redundant — NumberOfCustomers 통제로 같은 path 가 자동 차단
- 미관측 변수 무시 가능: SodaSales 의 collider 효과 덕분에 Block 2 의 미관측 변수가 confounder 아님
- 명확한 가이드: SodaSales 를 통제하면 collider bias 가 발생한다는 경고
DCC 라면: “Block 2 의 미관측 변수로 인해 잔여 confounding 가능” — 분석가가 결과 해석 시 주의해야 함. BC 라면: “Block 2 는 자동 처리됨” — 깨끗한 결론.
→ BC 가 가능한 상황에서는 BC 가 압도적으로 유리.
4 왜 필요한가 — 변수 선택의 통계적 의미
4.1 “모든 변수를 회귀에 넣자” 의 위험
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)
호텔 사례의 단순화된 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 사례
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 는:
- T 와 Y 사이 모든 path 열거
- 각 path 가 causal 인지 noncausal 인지 분류
- Noncausal path 가 자연스럽게 차단되는지 점검 (collider 존재)
- 차단 안 된 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 의 형제 글
- E-BUI5-1 DCC 분리적 원인 기준 — DCC 자세히
- E-BUI5-2 백도어 기준과 M-패턴 — BC 자세히 + 함정 사례
7.2 이전 챕터
- E-BUI4-3 데이터 검증과 반복 정제 — Ch.4: CD 짓기
- E-BUI3-2 체인과 포크 구조 — Mediator·Confounder 도입
- E-BUI3-3 충돌 변수와 경로 — Path·Collider 도입
7.3 후속 챕터
- E-BUI6-0 결측 데이터 처리 overview — Ch.6: 결측 데이터
- E-BUI8-0 Theory of Change 실험 설계 — Ch.8: 실험으로 confounding 제거
7.4 Hernan 정통 cross-link
- Causal_Inference/06 DAG와 인과 다이어그램 — DAG·d-separation
- Causal_Inference/07 교란 — Backdoor criterion 의 학술적 처리
7.5 카테고리 진입점
- Experimentation 학습 로드맵 — 11 Phase × 7 교재 매핑