はじめに
A/Bテスト(RCT)におけるノンコンプライアンスと操作変数法の応用
https://qiita.com/s1ok69oo/items/d9f3a3860388f3b9911d
が面白そうだったのでRで実装してみました。
数式等の詳細は元記事が丁寧に書いてあるのでご参考ください。
(私だと理解が怪しくて解説できる気もしない)
内容に誤り等ございましたらご指摘いただけますと幸いです。
問題の状況
CRMのメールマーケティングを考えます。新商品の発売を、会員へのメールで宣伝するとします。
ランダムにメールの送付対象を選定し、その効果を見るにはどうすればよいでしょうか。
考えなければならない事項
・メールを送付する対象はランダムに選定したが、そのあと開封するかは会員次第
・開封する会員はそもそも購入意欲(未観測)が高いと推定される、つまりメールの効果が過大推定される懸念
メール配信:Z
メール開封:D
購入の有無:Y
購入意欲 :U (未観測の値)
のようにそれぞれ変数を設定したとき、そのDAGは以下の通り
メールの効果は開封後を前提で考えており、未開封状態でのメール効果はないという仮定で試算を行なっています。例えば、タイトルだけ見て購入意欲が向上することはない or かなり小さいので無視できるという発想です。
Rを用いたシミュレーション
rm(list =ls())
library(dplyr)
# データの設定
set.seed(99)
size = 5000
# -----------------------------
# 各カラムを乱数で生成
# -----------------------------
# 潜在的な購入意欲(実際は観測不可)
U = runif(n = size,min = -5,max = 1)
sigmoid = function(X){1/(1+exp(-X))} # シグモイド関数を定義する
d_prob = sigmoid(U) # 0~1の範囲にスケーリング
# メール配信の有無
Z = rbinom(size, 1, 0.5)# ※sample関数でいい
# sample関数を使う場合
# Z = sample(2,size = size,replace = TRUE,prob = c(0.5,0.5))-1
# メールの開封
D_i = rbinom(size, 1, d_prob) #i番目の人の購入意欲を元に開封フラグ
D = Z * D_i #データに矛盾が出ないよう配信=0の場合は0にする
# 購入実績
y_prob = (U+5)/24 + 0.3*D #Dはダミー変数。開封した場合は0.3が足される設定
Y = rbinom(size, 1, y_prob)
# -----------------------------
# dfの生成
# -----------------------------
df <- data.frame('メール配信の有無'= Z
, 'メールの開封'= D
, '購入の有無'= Y)
df
# -----------------------------
# 試算:メール見たときの効果ってどうだったの?
# -----------------------------
# フィルター
df_z1 = df %>% filter(メール配信の有無==1)%>% select(購入の有無)
df_z0 = df %>% filter(メール配信の有無==0)%>% select(購入の有無)
df_z1_d1 = df %>% filter(メールの開封==1)%>% select(購入の有無)
df_z1_d0 = df %>% filter(メールの開封==0)%>% select(購入の有無)
# パターン1.メール配信ありの場合の購入率 - メール配信なしの場合の購入率
df_z1 %>% sum() /df_z1 %>% count() -
df_z0 %>% sum() /df_z0 %>% count()
# パターン2.メール配信同士で開封の場合の購入率 - 開封なしの場合の購入率
df_z1_d1 %>% sum() / df_z1_d1 %>% count() -
df_z1_d0 %>% sum() /df_z1_d0 %>% count()
# パターン3.開封の場合の購入率 - メール配信なしの場合の購入率
df_z1_d1 %>% sum() /df_z1_d1 %>% count() -
df_z0 %>% sum() /df_z0 %>% count()
# パターン4.(配信がある場合の購入率- 配信がない場合の購入率) / (配信されたうちの開封率)
(df_z1 %>% sum() /df_z1 %>% count() -df_z0 %>% sum() /df_z0 %>% count())/
(df_z1_d1 %>% count() /df_z1 %>% count())
出力結果の比較
・パターン1. 0.06622 過少
・パターン2. 0.38122 過大
・パターン3. 0.37317 過大
・パターン4. 0.30566 適切
操作変数法に関して
上記の推計が成立するのは、操作変数が成立する場合のみです。
操作変数とは効果量を知りたい処置変数Xにのみ影響を与え、結果Yには影響を与えない変数Zのことです。
操作変数が成立する前提条件は3つあります。
1:操作変数の外生性:Zと未観測の共変量Uの相関0
2:操作変数の関連性:Zと処置変数Xの相関あり
3:除外制約:Xを行なった時のYへの影響はZに影響されない
今回の事例を考えると、以下の通り上述の条件を満たすことが分かります。
1.本来未観測の共変量Uは多く存在し、Uに関する相関は観測不可のため検証の余地はない。
今回想定のU(購入意欲)に関してのみ考えると、ランダム選定のためZ(メール送付)とは独立。
2.配信と開封には相関がある
3.開封していない場合、Z(配信)のY(購入)への影響は0と考えられる
ザックリ結論
DAGを考えたときに今回のように操作変数が設定可能な形になっていた場合は
操作変数法の導入を検討し、パターン4の形で求めると適切に効果が求められる
終わりに
twitter: https://twitter.com/nekobo_01 にて統計やデータサイエンスに関するツイートをしてます。
よければフォローください。