Zarathu Co., Ltd
2025-01-18
회사: 차라투 주식회사(Zarathu Co.,Ltd)
경력
jinseob2kim@gmail.com, github.com/jinseob2kim
Baseline 맞춘다(X), RCT를 모방한다(O)
ATE(average treatment effect) vs ATT(average treatment effect on treated)
Matching은 ATT, IPTW는 ATE
2그룹 MatchIt
, 3그룹 twang
패키지
Logistic regression, Nearest neighbor, caliper 이해
3그룹 matching은 가장 작은 N수에 맞춰 2번 수행
openstat.ai 에서 2그룹 matching/IPTW 지원
분석 이슈
Matching 후 pair정보 이용해야 하는가?(ex: stratified cox)
성별에 따라 매칭/IPTW 해도 되는가?
목표는 Causal inference, RCT like design
\(ITE = Y_{1i} - Y_{0i}\), 하늘만이..
\(ATE = E[Y_1 - Y_0]\), RCT
\(ATT = E[Y_1 - Y_0 | T=1]\), \(ATC = E[Y_1 - Y_0 | T=0]\)
Propensity score란?
따라서, 치료군의 PS와 비슷한 사람을 대조군에서 뽑으면 두 그룹의 Baseline이 비슷해지겠군!
RCT: 어떤 사람이 두 군에 배정될 확률이 50:50
PS matching: PS가 0.7인 사람이 두 군에 배정될 확률? 50:50
그럼 PS matching하면 RCT만큼 인정받을 수 있다?
Inverse probability of treatment weighting
PS 0.7인 사람이 각 군에 속할 확률은?
\[0.7 \times \frac{1}{0.7} = 0.3 \times \frac{1}{0.3} = 1\]
그럼 IPTW는 ATE vs ATT?
ATE weight
\[ w_i \text{ for } ATE= \begin{cases} \frac{1}{p_i} & \text{if treated} \\ \frac{1}{1 - p_i} & \text{if control} \end{cases} \] 전체 샘플(Treated + Control) 의 2배를 랜덤하게 배정한 RCT
ATT weight
\[ w_i \text{ for } ATT = \begin{cases} 1 & \text{if treated} \\ \frac{p_i}{1 - p_i} & \text{if control} \end{cases} \] Treated + Treated 를 랜덤하게 배정
name | age | TAVI | Survival | Propensity_score | ATE | ATT |
---|---|---|---|---|---|---|
빈센조 | 35 | 1 | 1 | 0.1718567 | 5.818801 | 1.0000000 |
루카스 | 48 | 0 | 1 | 0.3163092 | 1.462649 | 0.4626495 |
제이슨 | 50 | 0 | 1 | 0.3435664 | 1.523383 | 0.5233834 |
토마스 | 53 | 1 | 0 | 0.3864109 | 2.587919 | 1.0000000 |
리오넬 | 55 | 0 | 1 | 0.4160330 | 1.712426 | 0.7124256 |
카밀라 | 68 | 1 | 1 | 0.6136449 | 1.629607 | 1.0000000 |
아칸지 | 70 | 1 | 1 | 0.6424478 | 1.556547 | 1.0000000 |
에밀리 | 75 | 0 | 1 | 0.7097903 | 3.445784 | 2.4457837 |
노이어 | 80 | 1 | 0 | 0.7690095 | 1.300374 | 1.0000000 |
호날두 | 85 | 1 | 0 | 0.8192224 | 1.220670 | 1.0000000 |
앨리스 | 40 | 0 | 1 | 0.2202580 | 1.282475 | 0.2824754 |
밥 | 45 | 0 | 1 | 0.2777194 | 1.384504 | 0.3845035 |
찰리 | 52 | 0 | 0 | 0.3718948 | 1.592090 | 0.5920901 |
다니엘 | 60 | 1 | 0 | 0.4923210 | 2.031195 | 1.0000000 |
엘리자베스 | 62 | 1 | 1 | 0.5231400 | 1.911534 | 1.0000000 |
프랭크 | 67 | 0 | 1 | 0.5989249 | 2.493298 | 1.4932985 |
그레이스 | 73 | 1 | 1 | 0.6837417 | 1.462541 | 1.0000000 |
헨리 | 77 | 0 | 1 | 0.7345263 | 3.766852 | 2.7668518 |
이사벨 | 82 | 1 | 0 | 0.7901901 | 1.265518 | 1.0000000 |
제임스 | 88 | 1 | 0 | 0.8450253 | 1.183396 | 1.0000000 |
존 | 42 | 0 | 1 | 0.2421700 | 1.319557 | 0.3195571 |
마리아 | 49 | 0 | 1 | 0.3297948 | 1.492080 | 0.4920803 |
피터 | 54 | 1 | 0 | 0.4011317 | 2.492947 | 1.0000000 |
사라 | 59 | 1 | 0 | 0.4769187 | 2.096793 | 1.0000000 |
데이비드 | 61 | 0 | 1 | 0.5077378 | 2.031438 | 1.0314379 |
제니퍼 | 46 | 0 | 1 | 0.2902582 | 1.408963 | 0.4089632 |
케빈 | 51 | 1 | 1 | 0.3576063 | 2.796371 | 1.0000000 |
레베카 | 76 | 0 | 1 | 0.7223278 | 3.601369 | 2.6013690 |
토니 | 81 | 1 | 0 | 0.7797825 | 1.282409 | 1.0000000 |
엘리 | 83 | 1 | 0 | 0.8002318 | 1.249638 | 1.0000000 |
스티브 | 37 | 1 | 1 | 0.1901277 | 5.259622 | 1.0000000 |
안나 | 47 | 0 | 1 | 0.3031256 | 1.434979 | 0.4349789 |
마이클 | 51 | 0 | 1 | 0.3576063 | 1.556678 | 0.5566777 |
제시카 | 68 | 1 | 0 | 0.6136449 | 1.629607 | 1.0000000 |
댄 | 84 | 1 | 1 | 0.8099085 | 1.234707 | 1.0000000 |
소피아 | 59 | 0 | 1 | 0.4769187 | 1.911749 | 0.9117489 |
브라이언 | 62 | 1 | 1 | 0.5231400 | 1.911534 | 1.0000000 |
나탈리 | 78 | 0 | 1 | 0.7463771 | 3.942861 | 2.9428614 |
대니얼 | 84 | 1 | 0 | 0.8099085 | 1.234707 | 1.0000000 |
엘레나 | 89 | 1 | 0 | 0.8529311 | 1.172428 | 1.0000000 |
로버트 | 39 | 0 | 1 | 0.2098490 | 1.265581 | 0.2655808 |
줄리아 | 46 | 0 | 1 | 0.2902582 | 1.408963 | 0.4089632 |
스콧 | 53 | 0 | 0 | 0.3864109 | 1.629755 | 0.6297551 |
니콜 | 77 | 1 | 0 | 0.7345263 | 1.361422 | 1.0000000 |
앤드류 | 75 | 1 | 1 | 0.7097903 | 1.408867 | 1.0000000 |
케이트 | 54 | 0 | 1 | 0.4011317 | 1.669816 | 0.6698162 |
라이언 | 59 | 1 | 1 | 0.4769187 | 2.096793 | 1.0000000 |
미셸 | 86 | 0 | 1 | 0.8281768 | 5.819935 | 4.8199354 |
조셉 | 88 | 1 | 0 | 0.8450253 | 1.183396 | 1.0000000 |
엘레나 | 83 | 1 | 0 | 0.8002318 | 1.249638 | 1.0000000 |
그룹 | Treatment | Control |
---|---|---|
ATT | 70 | 69.04 |
ATE | 62.17 | 63.1 |
Original Cohort | 70(63.72) | 56.35(63.72) |
IPTW는 ATE니까 무조건 이걸해야겠네?
Truncated weight
분석난이도 증가
GLM, Cox에 Weight를 고려 (glm
, cox
weights 옵션 또는 svyglm
, svycox
)
Weighted Kaplan-meier from svycox (survfit
weights 옵션 또는 svykm
)
log-rank test(X), survey rank test(O, survey::svyranktest
)
IPWsurvival::adjusted.LR
이용(SAS 디폴트)MatchIt
2그룹 PS/IPTW에서 가장 많이 쓰는 패키지
0 1
429 185
#1:1 NN matching w/ replacement on a logistic regression PS
m.out <- matchit(treat ~ age + educ + race + married +
nodegree + re74 + re75, data = lalonde, distance = "glm", method = "nearest", ratio = 1, caliper = NULL)
m.out
A matchit object
- method: 1:1 nearest neighbor matching without replacement
- distance: Propensity score
- estimated with logistic regression
- number of obs.: 614 (original), 370 (matched)
- target estimand: ATT
- covariates: age, educ, race, married, nodegree, re74, re75
distance
: glm(로지스틱) 이 기본, 다른 ML모델도 가능method
: nearest 충분, ratio: 1:N 매칭caliper
: 매칭이 잘 안될때(ex: Treat/Control 숫자가 비슷할 때)
Call:
matchit(formula = treat ~ age + educ + race + married + nodegree +
re74 + re75, data = lalonde, method = "nearest", distance = "glm",
caliper = NULL, ratio = 1)
Summary of Balance for All Data:
Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean
distance 0.5774 0.1822 1.7941 0.9211 0.3774
age 25.8162 28.0303 -0.3094 0.4400 0.0813
educ 10.3459 10.2354 0.0550 0.4959 0.0347
raceblack 0.8432 0.2028 1.7615 . 0.6404
racehispan 0.0595 0.1422 -0.3498 . 0.0827
racewhite 0.0973 0.6550 -1.8819 . 0.5577
married 0.1892 0.5128 -0.8263 . 0.3236
nodegree 0.7081 0.5967 0.2450 . 0.1114
re74 2095.5737 5619.2365 -0.7211 0.5181 0.2248
re75 1532.0553 2466.4844 -0.2903 0.9563 0.1342
eCDF Max
distance 0.6444
age 0.1577
educ 0.1114
raceblack 0.6404
racehispan 0.0827
racewhite 0.5577
married 0.3236
nodegree 0.1114
re74 0.4470
re75 0.2876
Summary of Balance for Matched Data:
Means Treated Means Control Std. Mean Diff. Var. Ratio eCDF Mean
distance 0.5774 0.3629 0.9739 0.7566 0.1321
age 25.8162 25.3027 0.0718 0.4568 0.0847
educ 10.3459 10.6054 -0.1290 0.5721 0.0239
raceblack 0.8432 0.4703 1.0259 . 0.3730
racehispan 0.0595 0.2162 -0.6629 . 0.1568
racewhite 0.0973 0.3135 -0.7296 . 0.2162
married 0.1892 0.2108 -0.0552 . 0.0216
nodegree 0.7081 0.6378 0.1546 . 0.0703
re74 2095.5737 2342.1076 -0.0505 1.3289 0.0469
re75 1532.0553 1614.7451 -0.0257 1.4956 0.0452
eCDF Max Std. Pair Dist.
distance 0.4216 0.9740
age 0.2541 1.3938
educ 0.0757 1.2474
raceblack 0.3730 1.0259
racehispan 0.1568 1.0743
racewhite 0.2162 0.8390
married 0.0216 0.8281
nodegree 0.0703 1.0106
re74 0.2757 0.7965
re75 0.2054 0.7381
Sample Sizes:
Control Treated
All 429 185
Matched 185 185
Unmatched 244 0
Discarded 0 0
jstable::CreateTableOneJS
jstable::CreateTableOneJS(vars = names(m.out$X), strata = "treat", data = lalonde, smd = T)$table %>% kable
level | 0 | 1 | p | test | SMD | sig | |
---|---|---|---|---|---|---|---|
n | 429 | 185 | NA | ||||
age | 28.03 ± 10.79 | 25.82 ± 7.16 | 0.003 | 0.242 | ** | ||
educ | 10.24 ± 2.86 | 10.35 ± 2.01 | 0.585 | 0.045 | |||
race (%) | black | 87 (20.3) | 156 (84.3) | <0.001 | 1.701 | ** | |
hispan | 61 (14.2) | 11 ( 5.9) | NA | ||||
white | 281 (65.5) | 18 ( 9.7) | NA | ||||
married | 0.51 ± 0.50 | 0.19 ± 0.39 | <0.001 | 0.719 | ** | ||
nodegree | 0.60 ± 0.49 | 0.71 ± 0.46 | 0.007 | 0.235 | ** | ||
re74 | 5619.24 ± 6788.75 | 2095.57 ± 4886.62 | <0.001 | 0.596 | ** | ||
re75 | 2466.48 ± 3292.00 | 1532.06 ± 3219.25 | 0.001 | 0.287 | ** |
jstable::CreateTableOneJS(vars = names(m.out$X), strata = "treat", data = mdata, smd = T)$table %>% kable
level | 0 | 1 | p | test | SMD | sig | |
---|---|---|---|---|---|---|---|
n | 185 | 185 | NA | ||||
age | 25.30 ± 10.59 | 25.82 ± 7.16 | 0.585 | 0.057 | |||
educ | 10.61 ± 2.66 | 10.35 ± 2.01 | 0.290 | 0.110 | |||
race (%) | black | 87 (47.0) | 156 (84.3) | <0.001 | 0.855 | ** | |
hispan | 40 (21.6) | 11 ( 5.9) | NA | ||||
white | 58 (31.4) | 18 ( 9.7) | NA | ||||
married | 0.21 ± 0.41 | 0.19 ± 0.39 | 0.604 | 0.054 | |||
nodegree | 0.64 ± 0.48 | 0.71 ± 0.46 | 0.151 | 0.150 | |||
re74 | 2342.11 ± 4238.98 | 2095.57 ± 4886.62 | 0.605 | 0.054 | |||
re75 | 1614.75 ± 2632.35 | 1532.06 ± 3219.25 | 0.787 | 0.028 |
m.out.caliper <- matchit(treat ~ age + educ + race + married +
nodegree + re74 + re75, data = lalonde, method = "nearest", ratio = 1, caliper = 0.1)
jstable::CreateTableOneJS(vars = names(m.out$X), strata = "treat", data = match.data(m.out.caliper), smd = T)$table %>% kable
level | 0 | 1 | p | test | SMD | sig | |
---|---|---|---|---|---|---|---|
n | 111 | 111 | NA | ||||
age | 26.14 ± 10.88 | 25.95 ± 6.58 | 0.870 | 0.022 | |||
educ | 10.49 ± 2.66 | 10.49 ± 2.04 | 1.000 | <0.001 | |||
race (%) | black | 80 (72.1) | 82 (73.9) | 0.954 | 0.041 | ||
hispan | 12 (10.8) | 11 ( 9.9) | NA | ||||
white | 19 (17.1) | 18 (16.2) | NA | ||||
married | 0.24 ± 0.43 | 0.21 ± 0.41 | 0.523 | 0.086 | |||
nodegree | 0.61 ± 0.49 | 0.65 ± 0.48 | 0.580 | 0.074 | |||
re74 | 2408.56 ± 4436.77 | 2667.11 ± 5732.76 | 0.707 | 0.050 | |||
re75 | 1800.87 ± 2917.71 | 1811.30 ± 3699.16 | 0.981 | 0.003 |
svyCreateTableOneJS
library(survey)
lalonde$wt <- ifelse(lalonde$treat == 1, 1/m.out$distance, 1/(1 - m.out$distance))
design.lalonde <- svydesign(ids=~1, strata=NULL, weights=~wt, data = lalonde)
jstable::svyCreateTableOneJS(vars = names(m.out$X), strata = "treat", data = design.lalonde, smd = T)$table %>% kable
level | 0 | 1 | p | test | SMD | sig | |
---|---|---|---|---|---|---|---|
n | 616.00 | 553.63 | NA | ||||
age | 27.10 ± 10.80 | 25.57 ± 6.53 | 0.088 | 0.172 | |||
educ | 10.29 ± 2.74 | 10.61 ± 2.05 | 0.303 | 0.132 | |||
race (%) | black | 245.1 (39.8) | 247.9 (44.8) | 0.695 | 0.112 | ||
hispan | 72.1 (11.7) | 67.4 (12.2) | NA | ||||
white | 298.8 (48.5) | 238.3 (43.0) | NA | ||||
married | 0.41 ± 0.49 | 0.31 ± 0.47 | 0.228 | 0.197 | |||
nodegree | 0.62 ± 0.48 | 0.57 ± 0.50 | 0.458 | 0.112 | |||
re74 | 4552.74 ± 6337.09 | 2932.18 ± 5709.42 | 0.040 | 0.269 | ** | ||
re75 | 2172.04 ± 3160.14 | 1658.07 ± 3072.89 | 0.136 | 0.165 |
Matching: N수 제일 작은 그룹(A) 에 맞춰 여러번 매칭
MatchIt
그대로 쓸 수 있는건 장점IPTW: twang::mnps
Matching 후 stratified cox를 해야하는가? Yes or No
Pair 마다 baseline hazard가 다르다는 가정
RCT 재현 관점: 필요없음(pair정보 없음)
매칭이 너무 잘 되어서 pair정보가 확실하다면?
Pair 들끼리 뭔가 다르다면?
OMOP-CDM ATLAS에서는 strata 사용하는게 Default.
예) 다기관 RCT에서 hospital, stratified random
+ hospital
도 가능, but 각 병원의 계수를 굳이 구할필요가?
coxme
: mixed model의 Random effect, strata
와 비슷한 의미
cluster = hospital
: 같은 hospital 간에 상관관계가 있음을 보정하므로 strata
와 비슷한 의미일 것 같지만, baseline hazard 이 다르다는 가정이 아님. cluster
옵션 전후 HR은 변하지 않음, SE만 변화.
Sex가 RCT 가능한가?
Matching: \(ATT\)
IPTW: \(ATE\) 니까 OK?
- Clone-Censor-Weight method
- Clone : eligibile한 모든 대상자를 clone -> Treatment, Control arm에 assign
- Censor : assign된 arm의 strategy에서 벗어나면, Protocol violation -> artificial censoring
- Weight : 관찰시간에 따라 artificial censoring되지 않고 남아있을 확률의 역수를 가중치로 부여
Baseline 맞춘다(X), RCT를 모방한다(O)
ATE(average treatment effect) vs ATT(average treatment effect on treated)
Matching은 ATT, IPTW는 ATE
2그룹 MatchIt
, 3그룹 twang
패키지
Logistic regression, Nearest neighbor, caliper 이해
3그룹 matching은 가장 작은 N수에 맞춰 2번 수행
openstat.ai 에서 2그룹 matching/IPTW 지원
분석 이슈
Matching 후 pair정보 이용해야 하는가?(ex: stratified cox)
성별에 따라 매칭/IPTW 해도 되는가?
openstat.ai