case-kの備忘録

日々の備忘録です。データ分析とか基盤系に興味あります。

書籍メモ:効果検証入門 2章 介入効果を測るための回帰分析 

効果検証の2章では回帰モデルを使い、介入変数の説明力から因果関係を解釈します。

www.case-k.jp

概要

回帰モデルをつくり、介入変数の説明力から因果関係の有無を確かめます。

実践

回帰モデルをつくり、介入変数の説明力を確認してみます。

データ項目

RCTを試してみます。男性向けに配信されたメールの効果を検証してみます。

# install package
library("tidyverse")
# read data
email_data <- read_csv("http://www.minethatdata.com/Kevin_Hillstrom_MineThatData_E-MailAnalytics_DataMiningChallenge_2008.03.20.csv")

データ項目を確認してみます。

  • recency:最後の購入からの経過月数
  • history_segment:昨年の購入額の階層
  • history:昨年の購入額
  • mens:昨年に男物の商品を購入しているか
  • womens:昨年に女物の商品を購入しているか
  • zip_code :地区分類コード
  • newbie:過去12ヶ月に新しくユーザになったか
  • channel:昨年に置いてどのチャンネルから購入したか
  • segment:どのメールが配信されたか
  • visit:メールが配信されてから2週間いないにサイトに訪問したか
  • conversion:メールが配信されてから2週間いないに購入したか
  • spend:購入した際の購入額
head(email_data,5)
# A tibble: 5 x 12
  recency history_segment history  mens womens zip_code  newbie channel segment       visit conversion spend
    <dbl> <chr>             <dbl> <dbl>  <dbl> <chr>      <dbl> <chr>   <chr>         <dbl>      <dbl> <dbl>
1      10 2) $100 - $200    142.      1      0 Surburban      0 Phone   Womens E-Mail     0          0     0
2       6 3) $200 - $350    329.      1      1 Rural          1 Web     No E-Mail         0          0     0
3       7 2) $100 - $200    181.      0      1 Surburban      1 Web     Womens E-Mail     0          0     0
4       9 5) $500 - $750    676.      1      0 Rural          1 Web     Mens E-Mail       0          0     0
5       2 1) $0 - $100       45.3     1      0 Urban          0 Web     Womens E-Mail     0          0     0

RCTでデータを絞り込む。

介入は男性向けメールと女性向けメールがあるので、まず女性向けメールのレコードを取り除きます。
男性向けのメールが配信されたサンプルと配信されなかったサンプルに絞り込みます。(後から色々と検証できるのでログは残しておいた方が良さそうです)

# remove delivery message for women and add columns of delivery message or not
male_df <- email_data %>%
  filter(segment != "Womens E-Mail") %>%
  mutate(treatment= if_else(segment == "Mens E-Mail", 1, 0))

女性に対して配信されたメールが削除されて、施策の介入有無を意味するカラムが追加されていることを確認できます。

head(male_df, n=5)
# A tibble: 5 x 13
  recency history_segment history  mens womens zip_code newbie channel      segment     visit conversion spend treatment
    <dbl> <chr>             <dbl> <dbl>  <dbl> <chr>     <dbl> <chr>        <chr>       <dbl>      <dbl> <dbl>     <dbl>
1       6 3) $200 - $350     329.     1      1 Rural         1 Web          No E-Mail       0          0     0         0
2       9 5) $500 - $750     676.     1      0 Rural         1 Web          Mens E-Mail     0          0     0         1
3       9 5) $500 - $750     675.     1      1 Rural         1 Phone        Mens E-Mail     0          0     0         1
4       2 2) $100 - $200     102.     0      1 Urban         0 Web          Mens E-Mail     1          0     0         1
5       4 3) $200 - $350     241.     0      1 Rural         1 Multichannel No E-Mail       0          0     0         0

バイアスのあるデータを作る

意図的にバイアスのあるデータを用意します。

set.seed(1)
obs_rate_c <- 0.5
obs_rate_t <- 0.5
bias_data <- male_df %>%
  mutate(obs_rate_c=if_else(
    (history > 300) | (recency < 6) | (channel == "Multichannel"),
    obs_rate_c,1),
    obs_rate_t=if_else(
      (history > 300) | (recency < 6) | (channel == "Multichannel"),
      1, obs_rate_t),
    random_number=runif(n=NROW(male_df))) %>%
  filter((treatment==0 & random_number < obs_rate_c)|
           (treatment==1 & random_number < obs_rate_t))

作られたデータは次のようになっています。

bias_data 
# A tibble: 31,863 x 16
   recency history_segment history  mens womens zip_code newbie channel segment visit conversion spend treatment obs_rate_c obs_rate_t
     <dbl> <chr>             <dbl> <dbl>  <dbl> <chr>     <dbl> <chr>   <chr>   <dbl>      <dbl> <dbl>     <dbl>      <dbl>      <dbl>
 1       6 3) $200 - $350    329.      1      1 Rural         1 Web     No E-M…     0          0     0         0        0.5        1  
 2       9 5) $500 - $750    676.      1      0 Rural         1 Web     Mens E…     0          0     0         1        0.5        1  
 3       9 5) $500 - $750    675.      1      1 Rural         1 Phone   Mens E…     0          0     0         1        0.5        1  
 4       2 2) $100 - $200    102.      0      1 Urban         0 Web     Mens E…     1          0     0         1        0.5        1  
 5       4 3) $200 - $350    241.      0      1 Rural         1 Multic… No E-M…     0          0     0         0        0.5        1  
 6       5 1) $0 - $100       30.0     1      0 Surburb…      0 Phone   Mens E…     0          0     0         1        0.5        1  
 7       5 6) $750 - $1,0828.      1      0 Surburb…      1 Multic… Mens E…     0          0     0         1        0.5        1  
 8       9 1) $0 - $100       30.0     0      1 Surburb…      1 Phone   No E-M…     0          0     0         0        1          0.5
 9      11 2) $100 - $200    182.      1      0 Surburb…      0 Phone   Mens E…     0          0     0         1        1          0.5
10       2 2) $100 - $200    118.      1      0 Surburb…      0 Web     Mens E…     1          0     0         1        0.5        1  

回帰分析

バイアスのあるデータで回帰分析を行ます。

biased_reg <- lm(data=bias_data,
                 formula=spend ~treatment+history)

分析結果のレポート

説明変数の説明力を確認したいので、「Coefficients」に着目し分析結果の解釈をします。今回知りたいのは介入効果なので「treatment」に着目します。treatmentの推定結果は 0.9026109であり、その検定におけるP値の値も2.25e-07と非常に小さい値です。よってこの介入によって売上平均が0.9ほど増加すると解釈することができます。

summary(biased_reg)

Call:
lm(formula = spend ~ treatment + history, data = bias_data)

Residuals:
   Min     1Q Median     3Q    Max 
 -4.74  -1.46  -1.26  -0.48 497.74 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 0.3241996  0.1444390   2.245  0.02480 *  
treatment   0.9026109  0.1743057   5.178 2.25e-07 ***
history     0.0010927  0.0003366   3.246  0.00117 ** 
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 15.36 on 31860 degrees of freedom
Multiple R-squared:  0.001339,	Adjusted R-squared:  0.001276 
F-statistic: 21.35 on 2 and 31860 DF,  p-value: 5.406e-10

今回のように「Coefficients」にのみ着目したい場合は以下のようにするとわかりやすくなります。

library("broom")
biased_reg_coef <- tidy(biased_reg)
biased_reg_coef
# A tibble: 3 x 5
  term        estimate std.error statistic     p.value
  <chr>          <dbl>     <dbl>     <dbl>       <dbl>
1 (Intercept)  0.324    0.144         2.24 0.0248     
2 treatment    0.903    0.174         5.18 0.000000225
3 history      0.00109  0.000337      3.25 0.00117  

回帰分析におけるセレクションバイアスと解決策

セレクションバイアスの問題は解決されてるのか?

回帰分析において、セレクションバイアスが起きているか確認してみます。RCTとバイアスのあるデータで介入変数の説明力を確認します。

rct_reg <- lm(data=male_df, formula=spend~treatment)
rct_reg_coef <- summary(rct_reg) %>% tidy()
nonrct_reg <- lm(data=bias_data, formula=spend~treatment)
nonrct_reg_coef <- summary(nonrct_reg) %>% tidy()

RCTの説明力を確認。

rct_reg_coef
# A tibble: 2 x 5
  term        estimate std.error statistic  p.value
  <chr>          <dbl>     <dbl>     <dbl>    <dbl>
1 (Intercept)    0.653     0.103      6.36 2.09e-10
2 treatment      0.770     0.145      5.30 1.16e- 7

バイアスのあるデータの説明力を確認。RCTと比較すると介入効果を過大評価しており、セレクションバイアスの問題は解決されていないことが確認できます。

 nonrct_reg_coef
# A tibble: 2 x 5
  term        estimate std.error statistic      p.value
  <chr>          <dbl>     <dbl>     <dbl>        <dbl>
1 (Intercept)    0.548     0.127      4.32 0.0000156   
2 treatment      0.979     0.173      5.67 0.0000000143

回帰分析におけるバイアスを取り除く方法

セレクションバイアスは人為的に引き起こされます。今回のセレクションバイアスは次の3つによって引き起こされているため、これらを共変量として回帰モデルに追加します。

バイアスのあるデータで重回帰分析を行います。

nonrct_mreg <- lm(data=bias_data,
                  formula=spend ~ treatment+recency+channel+history)
nonrct_mreg_coef <- tidy(nonrct_mreg)

重回帰分析の結果を確認すると介入(treatment )の説明力は0.847とRCTデータの結果に近づいたことが確認できます。

nonrct_mreg_coef
# A tibble: 6 x 5
  term         estimate std.error statistic    p.value
  <chr>           <dbl>     <dbl>     <dbl>      <dbl>
1 (Intercept)   0.502    0.379      1.32    0.185     
2 treatment     0.847    0.178      4.74    0.00000211
3 recency      -0.0403   0.0259    -1.55    0.121     
4 channelPhone -0.00178  0.304     -0.00585 0.995     
5 channelWeb    0.226    0.303      0.745   0.456     
6 history       0.00103  0.000375   2.74    0.00608  

追加した共変量のように本来必要だがモデルから抜け落ちている変数を脱落変数と呼びます。このように脱落変数が抜け落ちたバイアスを「脱落変数バイアス(OVB)」と呼びます。
これは直感的には脱落変数が目的変数Yに対して与える影響が、脱落変数と介入変数との相関を通してZの効果として現れています。実際に回帰モデルで脱落変数バイアスのあるモデルとないモデルで推定し、OVBの値を求めると、OVBの差がtreatment の説明力の差になっていることを確認できます。この脱落変数のように目的変数と介入変数両方に相関のある変数のことを交路因子と呼びます。

CIA (Conditional Independence Assumption)

効果検証のための回帰分析における共変量の選択は理想的にはモデルに含まれていない変数によるOVBが全て0になるような状態を目指す。これはモデルに含めた共変量で条件付けしたときに、介入変数が目的変数と独立している状態になり、CIAと呼ばれ(conditional independence assumption) RCTのように介入Zがランダムに割り振られた状態を作り出します。

CIAを満たす上での問題点
  • バイアスの評価ができない問題

OVBはモデル間でのバイアスの変化を示す指標。残りのバイアスを評価できるものではない。なので分析者はセレクションバイアスがどのような理由で発生しどのような変数を加えることでコントロール可能であり、どのような変数を組み合わせることでCIAが満たされるのか仮定する必要があります。

  • 必要な共変量がデータにはないと言う問題

つまり完全に影響を取り除くことは難しい場合がある。
詳細は記載されてませんが、対処法として次のような手法が考えられます。

  • 操作変数法
  • 固定効果モデル
  • 差分の差分法(4章で説明。必要な共変量がデータとして入手できない場合の対応)

Sensitivity Analysis

手持ちのデータに含まれない変数がセレクションバイアスを起こしているか評価するための手法。これは重要だと分析者が認識している共変量以外の共変量をモデルから抜くことで、効果の推定値が大きく変動しないか確認する分析手法です。

Post treatment bias

介入によって影響を受けた変数によって起きるバイアスをPost treatment biasといいます。このように介入より後のタイミングで値が決まるような変数はモデルから除外する必要があり、セレクションバイアスが減る可能性があるからといって、OVBの値が0でない値を全てモデルに入れて良いわけではありません。

予測精度と推定効果の関係

本書籍ではモデルの予測精度は考慮していません。それは「モデルのデータに対する説明能力や、道のサンプルに対する予測能力を高めることが効果検証において有用である保証にはならないため」です。例えばRCTを行ったデータセットに置いて介入変数のみを入れて回帰した場合、ほとんどの場合、バイアスが非常に小さい値を得ることができます。しかし、モデルの目的変数の予測能力は非常に低くなります。脱落変数に着目した場合、介入変数と目的変数に相関する変数を加えれば、より正確な介入効果を得ることができますが、単にYの予測を改善するような変数を追加しても、その変数が介入効果と相関がない場合には効果の推定は改善しません。このような理由からYに対する予測能力が一定以上なければ効果検証としてのモデルに価値がないとは言えません。

多重共線性

回帰モデルに含まれている変数のうち2つ以上が強い相関をもつ状況のことです。予測問題に置いて問題視される多重共線性ですが、効果検証に置いても標準誤差が変化することで検定の結果が歪んだものになってしまいます。介入変数における多重共線性が確認できる場合対応が必要ですが、それ以外の変数に置いてはもし起きていても介入効果を検証する上では考慮する必要はありません。

所感

モデルの説明変数を活用しすれば全量を変数に入れOVBを可能なかぎり0に近づけることで、施策のユーザ属性を考慮せずにABテストの評価自動化システムをつくり出すことができそうに思う。評価の敷居値はOVBでやれば良さそう。ただし、リークのように入れてはいけない項目があることを認識して設計する必要がありそうです。