인과효과를 추정하는 방법론 중 하나인 이중차분법에 대한 글을 써보려 한다.
- 이 글에서는 이중차분법의 기본 개념에 대해 다룬다. (이중차분법이란 무엇인지, 이를 사용하기 위해서는 어떤 가정을 만족해야 하는지)
- 반면, 고급 DID 방법론(예. staggered DID, staggered DID에서 파생되는 Bad comparision 문제, local DID 등)에 대해서는 다루지 않는다.
이중차분법에 대해 가볍게 알아보고 싶은 분들께 이 글이 도움이 될 수 있길 바라본다.
들어가며
이중차분법에 대한 개념을 본격적으로 설명하기에 앞서 게임 데이터 분석가가 된 상황을 가정해보자.
우리 게임에서 “마법사”는 매우 매력적인 직업이지만 사용하기에 너무 어려워 해당 직업을 선택하는 유저가 적은 상황이다. 이에 대대적으로 마법사 직업에 대한 버프 업데이트를 진행했고, 이후 유저의 선택 비율이 늘었는지 확인해 보려고 한다.
만일 업데이트 효과가 적다면 추가 업데이트를 진행하고 과도하게 버프가 되었다면 리스크를 감수하고 다시 너프를 할 것이다.
(약간의 TMI를 첨부하자면, 나는 마법사 직업을 편애하기 때문에 마법사의 버프는 늘 옳다고 생각한다. 하지만 착한 분석가(?) 친구들은 분석에 이런 편애를 넣으면 안 됩니다.)
우리에게는 다행히
- 버프를 진행하기 이전 데이터와 버프를 진행한 이후 시점의 데이터가 있고, (처치 전후 시점의 데이터가 있음)
- 버프를 받은 마법사 직업 이외에 다른 직업의 데이터도 갖고 있다. (처치 집단과 통제 집단의 데이터가 모두 있음)
즉 집단 간 비교와 시점 간 비교가 모두 가능한 상황으로, 이렇게 집단(unit) 및 시점(time)에 대한 정보가 있는 데이터를 패널 데이터라고 부른다. 그리고 패널 데이터를 갖고 있을 때 인과 효과를 추정할 수 있는 방법론 중 하나가 이중차분법(difference-in-difference, DID)이다.
이중차분법의 기본 개념과 가정
이중차분법의 개념
t=51 시점에 마법사 직업에 버프가 들어갔다고 가정해보자. 이후 마법사 직업을 선택한 비율이 증가한 것처럼 보이기는 한다. t=0 시점과 t=100 시점의 선택 비율을 비교해보면 대략 10%에서 15%로 늘었고, 버프 이후 선택 비율은 꾸준히 증가하는 추세로 보인다. 여기까지만 봐도 얼추 버프 업데이트가 마법사를 더 선택하도록 만들었다고 보이지만... 버프 업데이트가 선택 비율에 미치는 효과를 좀 더 엄밀하게 분석하기 위해 논의를 더 진행해본다.
버프가 미치는 (인과)효과를 제대로 구하기 위해서는 버프가 들어가지 않았을 때의 마법사 선택 비율에 대한 정보가 필요하다. 즉, 제대로 된 인과효과는 버프 효과가 들어가지 않았더라면 유지되었을 마법사 선택 비율과 버프 업데이트를 받고 변화한 마법사 선택 비율의 차이를 통해서 구할 수 있다.
버프를 받은 후의 선택 비율(1_real)과 버프를 받지 않았더라면 유지되었을 선택 비율(2_counterfactual)의 차이가 버프 업데이트 효과라 할 수 있다. (그래프에서 빨간 선으로 표시한 부분)
그렇지만 현실에서는 한 번 버프를 진행하면 "버프를 진행하지 않았을 때의 데이터"는 관측할 수 없다. 따라서 위와 같은 방식으로는 버프에 대한 인과효과를 추정할 수 없다. 이렇게 버프를 받지 않았더라면..에 해당하는 데이터를 반사실(counterfactual, 실제 데이터에 반하는 데이터)이라 부르고, 우리는 다른 데이터를 갖고 실제 관측할 수 없는 반사실 데이터를 추정해야 한다.
이 때 가져다 쓸 수 있는 다른 데이터의 후보군은 아마도 다른 직업의 선택 비율이 될 것이다.
여러 직업군 중 주술사 직업을 선택하여 이를 마법사 직업과 비교해보았다. 그런데 이미 버프 업데이트 이전부터 마법사 직업과 선택 비율의 추세가 다르게 움직인다. 이걸로 처치를 받지 않은 평행 우주의 마법사 데이터를 만들 수 있을까?
아마 어려울 것이다. 이미 마법사 직업과 완전히 따로 움직이고 있는 데이터라, 주술사 직업의 패턴을 갖고 마법사 직업을 추정할 수 없다.
우리에게 필요한 건 마법사 직업을 흉내낼 수 있는 데이터다. 자세히 말하면 버프 업데이트 이전에 마법사 직업과 비슷한 추세로 선택되고 있어 별다른 일이 없었다면 버프를 하지 않았을 때에도 마법사 선택 비율이 해당 직업과 비슷하게 흘러갈 것이라는 확신을 줄 수 있는 데이터가 필요하다. 즉, 버프가 없으면 비슷했을 거라는 가정이 필요한데, 이를 평행추세 가정이라고 부른다.
평행추세 가정을 좀 더 엄밀히 설명하면, 비교하려는 두 집단 간에 차이는 있을 수 있지만 추세는 일정해서 그 차이가 시간이 지나도 일정하게 유지된다는 가정을 의미한다.
다른 직업인 전사와 마법사 직업을 비교해보자. 버프 업데이트가 들어가기 전에는 전사는 선택 비율이 20% 내외이고, 마법사는 10% 내외이다. 처치를 받고 난 이후에 마법사 선택 비율은 서서히 증가하는 패턴으로 변화했다. 여전히 전사는 20% 내외로 선택되고 있다.
버프가 들어가기 이전에는 마법사와 전사가 비슷하게 선택되었기 때문에, 버프 업데이트가 없었다면 마법사 직업은 전사랑 비슷한 패턴으로 흘러갈 가능성이 있다. 그래서 전사 직업의 선택 비율의 추세를 그대로 가져다 마법사 선택 비율의 반사실 데이터를 만들 수 있다.
위 문장을 수식으로 표현하려 한다. 이를 위해 몇 가지 정의를 하자면 아래와 같다.
- suffix가 post인 경우는 처치 이후, pre인 경우는 처치 이전, counterfactual은 반사실을 의미한다.
- 인과효과는 처치 이후 선택 비율의 평균과 처치 이전 선택 비율의 평균으로 구한다. 즉, \(마법사_{post}\)는 처치 이후 마법사가 선택된 비율의 평균을 의미한다.
\(Causal Effect = 마법사_{post} - 마법사_{counterfactual}\)
= \(마법사_{post} - (마법사_{pre} + 마법사_{counterfactual} - 마법사_{pre})\) (양변에 같은 값을 더하고 빼준다)
= \( 마법사_{post} - (마법사_{pre} + 전사_{post} - 전사_{pre})\) (반사실의 추세는 통제집단의 추세로 대체 가능하다)
= \( (마법사_{post} - 마법사_{pre}) - (전사_{post} - 전사_{pre})\) (식을 조금 정리)
인과효과를 이렇게 수식으로 정리할 수 있다. 보면 처치집단(=마법사)의 처치 전후 차이, 통제집단(=전사)의 처치 전후 차이를 각각 구하고, 각각의 결과를 갖고 한 번 더 차이를 구하는 것을 볼 수 있는데 이렇게 두 번의 차분을 진행하기 때문에 이를 이중차분법이라 표현한다.
그리고 이렇게 차분을 진행해도 괜찮은 건 반사실의 추세는 통제 집단의 추세와 비슷하게 흘러갈 것이라는 평행추세를 가정했기 때문이다.
평행추세 가정
평행추세 가정에 대해 조금 더 이야기 해보려 한다. 평행추세 가정은 크게 2개로 분류할 수 있다.
- 처치 이전 시점의 평행추세(Pre-parallel) 가정
- 처치 이후 시점의 평행추세(Post-parallel) 가정
그리고 이 두 시점의 평행추세를 모두 만족할 때 진정으로 평행추세를 만족했다고 볼 수 있다. 그래서 처치 이전 시점에 평행추세 가정을 만족했다고 해도, 온전히 평행추세 가정을 만족했다고 볼 수 없다. 반대로 평행추세 가정을 만족한다면 처치 이전 시점에도 평행추세 가정을 만족한다고 볼 수 있다.
만일 버프 업데이트가 들어가는 시점에 공교롭게 인플루언서가 "전사 직업으로 켠김에 왕까지" 영상을 올리면서 버프를 받지 않은 다른 직업인 전사 직업이 홍보가 되었고, 이로 인해 전사 직업의 선택 비율이 늘어났다고 가정해보자. 우리는 버프 업데이트 이전에 전사와 마법사 직업의 선택 비율이 비슷했기 때문에 별다른 처치가 없다면 전사 직업과 마법사 직업의 추세가 계속 비슷할 것이라 가정했지만, 그 가정이 깨져버렸다. 평행추세 가정이 더 이상 성립하지 않게 된다.
다만, 처치 이후에는 처치가 들어가지 않은 상태를 더 이상 관측할 수 없기 때문에 평행추세 가정을 검증할 수 없다. 그래서 일반적으로는 처치 이전 시점의 평행추세 가정이 성립하는지를 검증한다. 처치 이전 시점에는 "버프"라는 처치가 들어가지 않았기 때문에 평행추세 가정이 성립한다면, 마법사 선택 비율과 전사 선택 비율의 차이는 시간이 지나도 변화가 없어야 한다.
이중차분법 모형
Canonical DID
위에서 사용한 데이터를 분석의 편의를 위해 다음과 같이 요약했다.
처치 전후 구분 / 직업 | 마법사 🧙♂️ | 전사 ⚔ |
처치 전 (pre) | 0.10 | 0.20 |
처치 후 (post) | 0.13 | 0.20 |
그러면 다음과 같은 사실을 알 수 있다.
- 전사 직업의 선택 비율이 0.2로 처치 이전 시점만 놓고 보면 마법사보다 0.1이 높게 나타난다.
- 전사 직업의 선택 비율이 그대로 유지되었기 때문에 시간에 따른 직업 선택 비율 변화는 없는 것으로 보인다.
- 그럼에도 불구하고 마법사 직업은 선택 비율이 0.03만큼 증가하였고, 이는 버프(처치) 효과로 보인다.
처치 이전의 전사 직업 선택 비율을 기본값(\(전사_{pre}\))로 놓으면 위와 같은 문장을 아래 수식으로 바꿀 수 있다.
\(y = 전사_{pre} + (-0.1)*Treat_i + 0* Post_t + \beta_3 Treat_i * Post_i\) (이 때 \(\beta_3\) 는 인과효과)
처치 이전 시점, 전사 직업 → \(Post_t\) (처치 이후 시점) = 0, \(Treat_i\) (버프 대상 여부) = 0을 대입하면 0.2
처치 이후 시점, 전사 직업 → \(Post_t\) (처치 이후 시점) = 1, \(Treat_i\) (버프 대상 여부) = 0을 대입하면 0.2
처치 이전 시점, 마법사 직업 → \(Post_t\) (처치 이후 시점) = 0, \(Treat_i\) (버프 대상 여부) = 1을 대입하면 0.2 - 0.1 = 0.1
처치 이전 시점, 마법사 직업 → \(Post_t\) (처치 이후 시점) = 1, \(Treat_i\) (버프 대상 여부) = 1을 대입하면 0.2 - 0.1 + 0 + \(\beta_3\) = 0.13 이 나와야 하므로 인과효과가 0.03임을 쉽게 구할 수 있다.
\(y = \beta_0 + \beta_1 * Treat_i + \beta_2 * Post_t + \beta_3 Treat_i * Post_i\) 와 같은 모양으로 수식을 구성하는 것을 Canonical DID 모형이라고 부른다.
Canonical DID with TWFE
위에서는 시계열 데이터를 처치 전, 처치 후로 요약해서 정리했다면 이번엔 원본 데이터 그대로를 사용하는 모델링에 대해 알아보자.
원본 데이터는 직업(job), 시점(time), 처치 전후 구분(is_post), 직업선택 비율(pick_rate)이 나와 있다.
즉, 원본 데이터에서는 시간에 따른 추이를 포착할 수 있고, 직업별 특성을 파악할 수 있다.
버프 업데이트 효과(인과효과)는 시간에 따른 변화와 직업 자체 특성과는 별개로 측정되어야 한다. 이를 위해 시간을 더미변수(dummy variable)로 둬서 각 시간에 따른 효과를 더미변수에 흡수시키고, 직업별 특성을 더미변수에 흡수시킨다. 이렇게 데이터를 표현하는 모델을 Two Way Fixed Effect (TWFE)라고 하는데, 보다 자세한 설명은 해당 영상을 참고하면 좋을 것 같다.
인과효과는 마법사 직업이 처치 이후에 얼마나 더 많이 선택되었는지의 증가분을 포착하는 것이고, 이는 버프 대상여부*처치 이후 여부의 상호작용 항으로 표현할 수 있다.
그래서 위 표는
선택비율 = 마법사 직업 여부 + \(time_1 + time_2 + ... + time_{99}\) + 버프 대상여부 * 처치 이후 여부
의 수식으로 바꿔쓸 수 있다. (전사 직업 여부, time_100은 perfect collinearity 문제를 피하기 위해 제외했다.)
위 수식을 일반화해서 쓰면
\(y = \delta_i + \theta_t + \beta_3 * Treat_i * Post_t \) 로 나타낼 수 있고, \(Treat_i\)는 unit fixed effect (유닛 고정 효과), \(Post_t\)는 time fixed effect (시간 고정 효과) 를 의미한다.
분석
분석은 위 수식을 함수로 표현하면 간단히 해결할 수 있다. 먼저 처치 이전의 평행추세 가정이 성립한다고 볼 수 있는지를 검증해보자.
만일 평행추세를 만족한다면, 처치 이전에는 시간과 직업을 고정시켰을 때 마법사와 전사 간의 선택 비율 차이는 없어야 한다. 시간에 따른 추세는 시간고정효과(time fixed effect)에 흡수되고, 직업별 차이는 유닛고정효과(unit fixed effect)에 흡수되기 때문에 처치 이전에 차이가 발생하지 않는다. 단, 버프에 따른 직업 선택 효과가 실제로 있다면 처치 이후부터는 시간과 직업을 고정시켰을 때도 직업 선택 비율(pick_rate)에 차이가 발생한다.
library(tidyverse)
# 변수 추가 (처치 여부를 판별하는 컬럼 추가)
summary_data <- summary_data %>% mutate(treated = ifelse(job == 'mage', 1, 0))
# 범주형 변수의 레벨 조정 (어디를 기준으로 둘 것인지)
summary_data$job <- relevel(as.factor(summary_data$job), ref = 'warrior') # 통제집단을 기준값으로
summary_data$time_f <- relevel(as.factor(summary_data$time), ref = 50) # 처치 전 -1 시점을 기준값으로
es_model <- lm(pick_rate ~ time_f + job, data = summary_data)
summary(es_model)
아까 보여준 데이터에는 처치 여부를 판별하는 필드가 따로 없다. 실험군, 대조군에 각각 직업이 한 개 뿐이라 이걸 구분하는 게 크게 의미가 없긴 한데 그래도 처치 여부를 판별하는 필드를 추가해줬고, 베이스라인이 되는 직업군(전사)과 시간대(처치 전 -1 시점인 time = 50)가 기준값이 될 수 있도록 조정을 해 주었다.
이 상태에서 시간과 직업을 각각 고정시킨 상태로 선형회귀분석을 돌려본다.
분석 결과를 살펴보면 처치 전 시점인 time_f1 ~ time_f49의 변수는 모두 p-value가 0.05보다 커서 통계적으로 유의하지 않다고 나온다. 즉, 처치 전 평행추세 가정이 성립한다고 볼 수 있다. 반면, 처치 이후 시점인 time_f51부터는 조금씩 p-value가 0.05보다 큰 변수들이 눈에 보이기 시작한다. 즉, 모종의 인과효과가 있다고 볼 수 있는 것이다.
이제 인과효과를 제대로 포착하기 위해, 처치 대상 여부, 처치 이후 시점 여부의 상호작용(is_treated * is_post)을 고려하여 분석해보자.
lm_model <- lm(data = summary_data, pick_rate ~ time_f + job + treated*is_post)
summary(lm_model)
시간 더미변수에 약간씩 유의한 항이 나타나는 게 걸리기는 하지만, 앞서의 결과보다 더미변수의 유의성이 많이 사라졌다. 그리고 이는 인과효과를 나타내는 상호작용 항(treated:is_post)에 대부분 흡수되었다.
즉, 이중차분법을 적용해서 분석한 결과, 마법사 버프로 마법사를 선택하는 비율이 약 2.9%p 늘어났다는 결론을 얻을 수 있다.
파이썬으로 구현해도 어려움이 크게 없다.
import pandas as pd
import statsmodels.formula.api as smf
import numpy as np
# R과 동일한 데이터 로드
data = pd.read_csv("did_sim_data_final.csv")
# 처치 여부에 해당하는 필드를 동일하게 만들어줌
data['is_treated'] = np.where(data['job'] == 'mage', 1, 0)
# 이중차분법 분석
m = smf.ols('pick_rate ~ is_treated:is_post + C(job) + C(time)',
data=data).fit()
m.params["is_treated:is_post"]
위 코드의 결과도 약 0.0288로 R로 분석한 결과와 유사한 결과가 나온다.
이중차분법 분석 시 주의사항
기껏 분석을 다 했는데 이제 와서 뭘 주의하라는 거야! 라는 생각이 들 수도 있을 것 같다. 주의사항이라는 건 모름지기 흥이 깨지는 이야기니까 말이다. 그렇지만 주의사항부터 나오면 지레 포기해버리고 싶은 생각이 들 수도 있지 않을까 하는 마음과, 어차피 여기서 말하는 주의사항들은 실험설계 자체의 문제인 경우가 많아 이런 건 주의해야겠구나하고 가볍게 봤으면 하는 마음이었기 때문에 뒤에 배치해 두었다. (부록 느낌으로 봐주면 좋을 것 같다.)
처치 집단이 미리 처치 여부를 예상하는 경우
이를 No Anticipation 가정이라 하는데, 처치가 되기 전에 이미 처치가 될 것을 기대하고 행동하면 안 된다는 가정이다. 마법사 버프 업데이트 일주일 전부터 버프 정보가 유출되어 업데이트 전부터 유저가 마법사 직업을 선택하기 시작했다면, 이는 No Anticipation 가정에 위배되는 것이다.
공변량이 편향을 야기하는 경우
공변량은 처치 변수(직업 버프) 이외에 결과 변수(직업 선택 비율)에 영향을 미치는 요소를 통제하기 위해 곁다리로 놓는 보조 변수(일종의 깍두기)를 의미한다. 일반적으로는 선택 편향을 줄이기 위해 공변량을 넣는다. 예를 들어 교육이 소득에 미치는 영향을 본다고 해보자. 교육 수준과 소득은 부모님의 소득에 동시에 영향을 받을 수 있다. 이때 부모님의 소득을 고려하지 않으면 부모님의 소득에 의한 차이를 인과 효과로 잘못 판단하는 "선택 편향" 문제가 생길 수 있다. 그래서 부모님의 소득이라는 공변량을 넣어 교육이 소득에 비치는 인과효과를 올바르게 포착할 수 있고, 이렇게 선택 편향 문제를 해결하기 위해 일반적으로는 공변량을 고려한다.
그러나 이때, 오히려 처치 변수에 영향을 받는 공변량을 추가하거나, 더 나아가 처치변수와 결과변수에 동시에 영향을 받는 공변량을 추가하면 오히려 심각한 편향을 야기할 수 있다.
예를 들어 버프 효과가 직업 선택 비율에 미치는 인과 효과를 파악하기 위해 "주간 공략글 개수"가 비슷한 직업끼리 비교한다고 가정해 보자.
이를 그래프로 표현하면
- 버프 업데이트 -----> 직업 선택 비율
- 버프 업데이트 -----> 공략글 개수 (버프를 받으면서 해당 직업이 관심을 받아 공략글 개수도 같이 늘었다)
- 직업 선택 비율 -----> 공략글 개수 (일반적으로 유저가 많이 선택하는 직업의 공략글이 많다)
로 표현할 수 있다.
이때 주간 공략글 개수가 많은 직업끼리 비교한다고 하면 버프 업데이트를 받은 직업이거나, 선택 비율이 기본적으로 높은 직업끼리 비교하게 될 것이다. 이 경우 버프를 받지 않고 기본적으로 선택 비율이 높았던 직업과 버프를 받아 이제야 겨우 선택 비율이 늘어난 직업을 비교하게 되므로 오히려 버프가 직업 선택 비율을 낮춘다는 잘못된 결론에 도달하게 될 수 있다.
그래서 늘 인과효과를 추정할 때는 아무 변수나 막 집어넣으면 안 된다.. (그 점이 인과분석의 어려운 부분 중 하나이다.)
만일 공변량을 고려해야 한다면 처치 이후에 영향을 받지 않는, 즉 처치 이전에 결정되는 변수를 고려해야 한다.
마치며
여기까지 이중차분법이 무엇인지 개념에 대해 알아보았다. 이래저래 글은 길었지만 요약하면 다음과 같다.
- 인과효과를 분석하기 위해서는 처치를 받지 않았더라면..에 해당하는 반사실 데이터가 필요하다.
- 반사실을 어떻게 잘 흉내 낼 것인가를 고민해야 하는데, 만일 처치 이전-이후, 처치집단-통제집단에 대한 데이터가 모두 있다면 평행추세 가정을 만족한다는 가정 하에서 통제 집단의 데이터로 반사실 데이터를 잘 흉내낼 수 있다.
- 이때 1) 처치집단의 처치 이후-이전에 대한 차이, 2) 통제집단의 처치 이후-이전에 대한 차이 두 값의 차이를 통해 인과효과를 추정한다. 차이를 두 번 구하기 때문에 이를 차이의 차이, 즉 이중차분법이라 부른다.
또한, 마법사 버프 이후 선택비율이 약 2.8%p ~ 3%p가 증가했다는 결론을 얻을 수 있었다. 가상의 데이터이기 때문에 깔끔하게 결과가 나왔지만, 만약 현실의 데이터가 있었다면 이렇게 깔끔한 결론이 나오기는 어려울 지도 모른다. 결국 이중차분법을 실무에 써먹기 위해서는 평행추세 가정을 만족시키는 깔끔한 상황이 나올 수 있는가가 주요 관건일 것이다.
그 이외에 놓치기 쉽지만, 정말 중요한 것이 있다면 "버프 업데이트로 마법사 선택이 2~3%p 늘었다."에 이어 분석을 받아보는 사람들에게 뭔가 더 할 이야기가 있어야 한다는 것이다.
- 2~3%p 증가분은 버프 업데이트에 대한 목표를 달성한 수치가 맞는가?
- 버프 업데이트가 성공적이었다면 어떤 요소가 유저로 하여금 매력을 느끼게 만들었을까?
- 다음에 버프 업데이트를 한다면 우리는 어떤 부분을 벤치마크할 수 있을 것인가?
와 같은, 현업에서 필요로 하는 질문에 대한 답을 할 수 없다면 이런 분석 결과는 허공에 울려 퍼지는 메아리처럼 사라질지도 모른다.
부록 (데이터 생성에 사용한 R 코드, feat.ChatGPT)
가상의 데이터 생성 코드
n_samples <- 500
time_periods <- 50 # 처치 전후 각각 30개
total_periods <- time_periods * 2 # 총 기간
buff_start <- time_periods # 중간에 버프 시작
base_mage <- 0.1 # 처치 전 mage 선택 비율
base_warrior <- 0.2 # 처치 전 warrior 선택 비율
buff_effect <- 0.05 # 버프 효과
# 데이터프레임 초기화
data <- data.frame()
for (time in 1:total_periods) {
for (job in c("warrior", "mage")) {
treated <- ifelse(time > buff_start & job == "mage", 1, 0)
base_rate <- ifelse(job == "mage", base_mage, base_warrior)
# time_trend <- ifelse(time <= buff_start, 0, 0.01 * (time - buff_start))
treatment_effect <- ifelse(treated == 1, buff_effect * (time - buff_start) / time_periods, 0)
selection_rate <- base_rate + treatment_effect
selection_rate <- max(0, min(selection_rate, 1)) # 비율 제한
# 각 시간마다 n_samples 샘플 생성
chosen <- rbinom(n_samples, 1, selection_rate)
temp_data <- data.frame(
job,
time = time,
treated,
is_post = ifelse(time <= buff_start, 0, 1),
chosen
)
data <- bind_rows(data, temp_data)
}
}
# 요약 데이터 생성
summary_data <- data %>%
group_by(job, time, is_post) %>%
summarise(pick_rate = mean(chosen), .groups = "drop")
반사실 데이터 생성
n_samples <- 500
time_periods <- 50 # 처치 전후 각각 30개
total_periods <- time_periods * 2 # 총 기간
buff_start <- time_periods # 중간에 버프 시작
base_mage <- 0.1 # 처치 전 mage 선택 비율
base_warrior <- 0.2 # 처치 전 warrior 선택 비율
buff_effect <- 0.05 # 버프 효과
# 데이터프레임 초기화
data_mage_counterfactual <- data.frame()
for (time in 1:total_periods) {
treated <- 0
base_rate <- base_mage
selection_rate <- base_rate
selection_rate <- max(0, min(selection_rate, 1)) # 비율 제한
# 각 시간마다 n_samples 샘플 생성
chosen <- rbinom(n_samples, 1, selection_rate)
temp_data <- data.frame(
job = "mage",
time = time,
treated,
is_post = ifelse(time <= buff_start, 0, 1),
chosen
)
data_mage_counterfactual <- bind_rows(data_mage_counterfactual, temp_data)
}
# 요약 데이터 생성
summary_data_counter <- data_mage_counterfactual %>%
group_by(job, time, is_post) %>%
summarise(pick_rate = mean(chosen), .groups = "drop")
summary_data_counter <- summary_data_counter %>% transform(
pick_rate = ifelse(
time < 51, summary_data %>% filter(job=='mage' & time < 51) %>%
dplyr::select(pick_rate) %>% as_vector(), pick_rate
)
)
data_mage <- rbind(summary_data_counter %>% mutate(category = "2_counterfactual"),
summary_data %>% dplyr::select(-c(treated)) %>% filter(job == 'mage')%>% mutate(category = "1_real"))
평행추세 가정을 만족하지 않는 데이터 생성
data_shaman <- data.frame()
base_shaman <- 0.15
for (time in 1:total_periods) {
treated <- 0
base_rate <- base_shaman
time_trend <- ifelse(time <= buff_start, 0.001, 0.0005)
selection_rate <- ifelse(time ==1, base_rate, selection_rate) + time_trend
selection_rate <- max(0, min(selection_rate, 1)) # 비율 제한
# 각 시간마다 n_samples 샘플 생성
chosen <- rbinom(n_samples, 1, selection_rate)
temp_data <- data.frame(
job = "shaman",
time = time,
treated,
is_post = ifelse(time <= buff_start, 0, 1),
chosen
)
data_shaman <- bind_rows(data_shaman, temp_data)
}
summary_data_shaman <- data_shaman %>%
group_by(job, time, is_post) %>%
summarise(pick_rate = mean(chosen), .groups = "drop")
data_job <- rbind(summary_data_shaman,
summary_data %>% dplyr::select(-c(treated)) %>% filter(job == 'mage'))
'Statistics' 카테고리의 다른 글
인과추론 학습기 - 개입과 뒷문 기준 (2) | 2024.11.01 |
---|---|
인과추론 학습기 - SCM과 인과 그래프 (3) | 2024.10.19 |
인과추론을 위한 회귀분석 개념 정리 - 편회귀계수, FWL 정리 (2) | 2024.04.21 |
매칭(Matching)을 통한 인과추론 : 개념부터 실습까지 (feat. ChatGPT) (2) | 2024.03.25 |
꼬리에 꼬리를 무는 시계열 개념 정리, 정상성부터 공적분까지 (4) | 2024.01.14 |