DoWhy 를 사용하여 고객에 대한 구독 또는 리워드 프로그램의 효과를 추정하는 방법을 알아보겠습니다.
고객이 웹사이트에 가입하면 추가 혜택을 받는 멤버십 리워드 프로그램이 있다고 가정해봅시다. 해당 프로그램이 효과적인지 어떻게 알 수 있을까요? 여기서 우리는 인과적인 질문을 할 수 있습니다.
멤버십 리워드 프로그램 제공이 총 매출에 미치는 영향은 무엇인가?
그리고 이와 동등한 반사실적(counterfactual) 질문은 다음과 같습니다.
만약 고객들이 멤버십 리워드 프로그램에 가입하지 않았다면, 그들은 웹사이트에 얼마나 더 적은 돈을 썼을 것인가?
다시 말하면, 우리는 처치집단에 대한 평균 처치 효과(the Average Treatment Effect On the Treated, ATT)를 구하고자 합니다.
I. Formulating the causal model
리워드 프로그램이 2019년 1월에 도입되었다고 가정해봅시다. 결과 변수는 연말 총 지출액입니다. 우리는 모든 유저의 모든 월별 거래 내역과 리워드 프로그램 가입을 선택한 유저들의 가입 시간에 대한 데이터를 가지고 있습니다. 데이터는 다음과 같습니다.
# Creating some simulated data for our example exampleimport pandas as pdimport numpy as npnum_users =10000num_months =12signup_months = np.random.choice(np.arange(1, num_months), num_users)* np.random.randint(0,2, size=num_users)df = pd.DataFrame({'user_id': np.repeat(np.arange(num_users), num_months),'signup_month': np.repeat(signup_months, num_months), # signup month == 0 means customer did not sign up'month': np.tile(np.arange(1, num_months+1), num_users), # months are from 1 to 12'spend': np.random.poisson(500, num_users*num_months) #np.random.beta(a=2, b=5, size=num_users * num_months)*1000 # centered at 500})# Assigning a treatment value based on the signup monthdf["treatment"]= (1-(df["signup_month"]==0)).astype(bool)# Simulating effect of month (monotonically increasing--customers buy the most in December)df["spend"]= df["spend"]- df["month"]*10# The treatment effect (simulating a simple treatment effect of 100)after_signup = (df["signup_month"]< df["month"]) & (df["signup_month"]!=0)df.loc[after_signup,"spend"]= df[after_signup]["spend"] +100df
user_id
signup_month
month
spend
treatment
0
0
6
1
526
True
1
0
6
2
464
True
2
0
6
3
473
True
3
0
6
4
502
True
4
0
6
5
436
True
...
...
...
...
...
...
119995
9999
7
8
533
True
119996
9999
7
9
518
True
119997
9999
7
10
485
True
119998
9999
7
11
504
True
119999
9999
7
12
459
True
120000 rows × 5 columns
The importance of time
이 문제를 모델링하는 데 있어서 시간이 중요한 역할을 합니다.
리워드 프로그램 가입은 향후 거래에 영향을 미칠 수 있지만, 이전 거래에는 영향을 미치지 않습니다. 사실 리워드 가입 이전의 거래는 리워드 가입 결정을 유발한다고 가정할 수 있습니다.
따라서 각 유저의 변수들을 다음과 같이 나눌 수 있습니다.
처치 전 활동 (처치의 원인)
처치 후 활동 (처치 적용 결과)
물론 가입과 총 지출에 영향을 미치는 많은 중요한 변수가 누락되어 있습니다(e.g., 구입한 제품 유형, 유저 계정 사용 기간, 지역 등). 관측되지 않은 교란 변수(Unobserved Confounders)를 나타내는 노드가 필요합니다.
아래는 i=3 개월에 가입한 유저에 대한 인과 그래프입니다. 모든 i에 대해서 분석은 유사할 것입니다.
import os, syssys.path.append(os.path.abspath("../../../"))import dowhy# Setting the signup month (for ease of analysis)i =3
causal_graph ="""digraph {treatment[label="Program Signup in month i"];pre_spends;post_spends;Z->treatment;U[label="Unobserved Confounders"];pre_spends -> treatment;treatment->post_spends;signup_month->post_spends; signup_month->pre_spends;signup_month->treatment;U->treatment; U->pre_spends; U->post_spends;}"""# Post-process the data based on the graph and the month of the treatment (signup)df_i_signupmonth = df[df.signup_month.isin([0,i])].groupby(["user_id", "signup_month", "treatment"]).apply(lambdax: pd.Series({'pre_spends': np.sum(np.where(x.month < i, x.spend,0))/np.sum(np.where(x.month<i, 1,0)),'post_spends': np.sum(np.where(x.month > i, x.spend,0))/np.sum(np.where(x.month>i, 1,0)) })).reset_index()print(df_i_signupmonth)model = dowhy.CausalModel(data=df_i_signupmonth, graph=causal_graph.replace("\n", " "), treatment="treatment", outcome="post_spends")model.view_model()from IPython.display import Image, displaydisplay(Image(filename="causal_model.png"))