Tae Hyun Kim (Lowell)

Propensity Score Matching (PSM)

3분 읽기 #causal-inference#matching#psm

정의

Propensity Score가 유사한 처치군과 대조군 개인을 매칭

j(i)=argminj:Wj=0e(Xi)e(Xj)j(i) = \arg\min_{j: W_j=0} |e(X_i) - e(X_j)|

여기서 e(X)=P(W=1X)e(X) = P(W=1 \mid X)Propensity Score.


직관적 이해

왜 PS로 매칭?

Rosenbaum & Rubin (1983):

W ⁣ ⁣ ⁣Xe(X)W \perp\!\!\!\perp X \mid e(X)
  • PS가 같으면 처치 확률이 같음
  • → 고차원 XX 대신 스칼라 e(X)e(X)로 매칭 가능
  • 차원 축소: Curse of dimensionality 완화

예시

환자나이성별병력PS
A (처치)45M고혈압0.72
B (대조)52F당뇨0.70

XX는 다르지만 e(X)e(X)가 유사 → 매칭 가능.


절차

1단계: PS 추정

e^(Xi)=P(Wi=1Xi)\hat{e}(X_i) = P(W_i = 1 \mid X_i)

방법:

  • Logistic regression (일반적)
  • Random forest
  • Gradient boosting
  • Neural network

2단계: 매칭

각 처치군 개인에 대해 PS가 가장 유사한 대조군 찾기:

For each treated unit i:
    Find control j* with |e(Xi) - e(Xj)| minimized
    Match (i, j*)

3단계: 효과 추정

ATT^=1n1i:Wi=1(YiYj(i))\hat{\text{ATT}} = \frac{1}{n_1} \sum_{i: W_i=1} (Y_i - Y_{j(i)})

매칭 옵션

1. Caliper

거리 제한:

e(Xi)e(Xj)<c|e(X_i) - e(X_j)| < c

일반적으로 c=0.2×SD(e(X))c = 0.2 \times \text{SD}(e(X)).

2. 1:k Matching

처치 1명당 대조 k명 매칭:

  • k=1: 표준
  • k>1: 분산 감소, 편향 증가 가능

3. With/Without Replacement

옵션장점단점
With좋은 매칭, 편향↓분산↑, 일부 대조군 과다 사용
Without분산↓, 공정매칭 품질↓

장단점

장점

장점설명
차원 축소고차원 X → 스칼라 PS
직관적”유사 확률 비교”
투명성매칭 쌍 검토 가능
유연성다양한 매칭 옵션

단점

단점설명
PS 모델 의존PS 오특정 시 편향
정보 손실매칭 안 된 샘플 제외
분산IPW보다 효율 낮을 수 있음
매칭 품질나쁜 매칭 가능

품질 평가

균형 확인

매칭 후 공변량 분포 비교:

SMDk=Xˉk,TXˉk,C(sk,T2+sk,C2)/2\text{SMD}_k = \frac{\bar{X}_{k,T} - \bar{X}_{k,C}}{\sqrt{(s^2_{k,T} + s^2_{k,C})/2}}

기준: SMD<0.1|\text{SMD}| < 0.1

시각화

import matplotlib.pyplot as plt

# 매칭 전후 PS 분포
plt.subplot(1, 2, 1)
plt.hist(ps[W==1], alpha=0.5, label='Treated')
plt.hist(ps[W==0], alpha=0.5, label='Control')
plt.title('Before Matching')

plt.subplot(1, 2, 2)
plt.hist(ps_matched[W_matched==1], alpha=0.5, label='Treated')
plt.hist(ps_matched[W_matched==0], alpha=0.5, label='Control')
plt.title('After Matching')

구현

R (MatchIt)

library(MatchIt)

# PSM with 1:1 nearest neighbor
m.out <- matchit(treat ~ x1 + x2 + x3,
                 data = df,
                 method = "nearest",
                 distance = "logit",
                 caliper = 0.2)

# 균형 확인
summary(m.out)

# 매칭 데이터
matched_data <- match.data(m.out)

# ATT 추정
lm(y ~ treat, data = matched_data, weights = weights)

Python

from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import NearestNeighbors

# PS 추정
lr = LogisticRegression()
lr.fit(X, W)
ps = lr.predict_proba(X)[:, 1]

# 매칭
treated_idx = np.where(W == 1)[0]
control_idx = np.where(W == 0)[0]

nn = NearestNeighbors(n_neighbors=1)
nn.fit(ps[control_idx].reshape(-1, 1))
distances, matches = nn.kneighbors(ps[treated_idx].reshape(-1, 1))

# ATT 추정
att = np.mean(Y[treated_idx] - Y[control_idx[matches.flatten()]])

관련 개념

  • Matching Methods Overview - 매칭 방법 통합
  • Propensity Score - 핵심 도구
  • Nearest Neighbor Matching - 일반 NNM
  • ATT - PSM의 주요 추정 대상
  • Selection Bias - 해결 대상

참고 논문

  • Rosenbaum, P. R., & Rubin, D. B. (1983). The central role of the propensity score
  • yaoSurveyCausalInference2021 - Section 3.3
  • Caliendo, M., & Kopeinig, S. (2008). Some practical guidance for PSM

연결 그래프