case-kの備忘録

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

書籍メモ:効果検証入門 1章 セレクションバイアスとRCT

本記事は効果検証入門1章の備忘録となります。

gihyo.jp

概要

セレクションバイアスとは

比較しているグループの潜在的な傾向が異なることによって発生するバイアスのことです。例えば購買傾向の高いユーザに対してPUSH通知を行い、施策を介入したグループ(A) の平均売上は600万。施策を介入しなかったグループ(B)の平均売上は300万だったとします。そうすると単純に売上平均の差分をとるとAの方が300万高く、施策効果によって300万向上したと誤った結論を導いてしまいます。この300万は本当の介入による効果とセレクションバイアスを足し合わせたものになっています。実際には何も介入をしなかった場合に起きる売上(潜在的な購買量)が含まれているからです。このようにデータから得られた分析結果と本当の効果の乖離を「バイアス」と呼びます。そして比較しているグループの潜在的な傾向が異なることによって発生するバイアスのことを「セレクションバイアス」といいます。セレクションバイアスは意図的に介入を行うと発生します。

効果:ビジネス上重要なKPIに与えた影響
介入:何らかのアクションや施策のこと

RCTとは

バイアスをなくす方法としてRCT(無作為化比較試験)と言う手法があります。これは介入をランダムに割り当てることでバイアスを取り除く手法です。

RCTの状態をつくりだすにはコストがかかる

RCTは効果を検証するために介入がランダムに割り当てられる必要があります。なのでよくキャンペーン施策であるような、ユーザを絞り込んだ配信ではRCTを実現することはできません。なので実務に置いては分析者がセレクションバイアスを理解した上で分析を設計する必要があります。

実践編

実際にRを使いRCTでの評価とセレクションバイアスのあるデータの評価をしてみます。評価として使うt検定もセレクションバイアスがあまりに大きい場合、本来有意ではないにもかかわらず有意となってしまうことを確認します。

データ項目

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

介入グループごとに集計し、conversionとspendの平均を出します。

summary_by_segment <- male_df %>%
  group_by(treatment) %>%
  summarise(conversion_rate=mean(conversion),
            spend_mean=mean(spend),
            count=n())

結果を確認してみると男性向けメールが配信されたグループの方がconversion_rateとspendが高いことがわかります。つまりメールが配信されると売上が発生しやすくなり、平均の売上金額も高くなっていることがわかります。

summary_by_segment
# A tibble: 2 x 4
  treatment conversion_rate spend_mean count
      <dbl>           <dbl>      <dbl> <int>
1         0         0.00573      0.653 21306
2         1         0.0125       1.42  21307

この結果はRCTによって得られたデータなのでセレクションバイアスはない物と考えられます。よって今得られているデータに対して有意差検定を行えば、この介入の効果が偶発的な物なのか、そうでないのか評価することができます。
なずはベクトル化し、有意差検定を行ます。

mens_mail <- male_df %>%
  filter(treatment == 1) %>%
  pull(spend)

no_mail <- male_df %>%
  filter(treatment == 0) %>%
  pull(spend)

rct_ttest <- t.test(mens_mail, no_mail, var.equal = TRUE)

結果を確認してみるとP値は非常に小さい値となり帰無仮説(介入の効果は0)を棄却することができます。

rct_ttest

	Two Sample t-test

data:  mens_mail and no_mail
t = 5.3001, df = 42611, p-value = 1.163e-07
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 0.4851384 1.0545160
sample estimates:
mean of x mean of y 
1.4226165 0.6527894 

バイアスのあるデータの効果検証

次バイアスのある効果検証をしてみたいと思います。
潜在的に購買しそうなユーザを対象に条件を絞り込みます。

再現性を担保するためにシードを固定

set.seed(1)
obs_rate_c <- 0.5
obs_rate_t <- 0.5

データをつくります。メールが配信されてないグループからは条件に合致するデータをランダムに半分削除します。一方でメールが配信されているグルーオプでは条件に合致しないデータをランダムに半分削除しています。この操作によってあたかも潜在的に購買伊代k樹が高いと考えられるユーザに対してメールが多く配信されたデータをつくります。

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  

実際にセレクションバイアスのあるデータとRCTのデータを比較してみたいと思います。

summary_by_segment_biased <- bias_data %>%
  group_by(treatment) %>%
  summarise(conversion_rate=mean(conversion),
            spend_mean=mean(spend),
            count=n())
mens_mail_biased <- bias_data %>%
  filter(treatment == 1) %>%
  pull(spend)

no_mail_biased <- bias_data %>%
  filter(treatment == 0) %>%
  pull(spend)

rct_ttest <- t.test(mens_mail_biased, no_mail_biased, var.equal = TRUE)

先ほど行ったRCTの場合と比較すること差分が大きくなり、介入の効果を過大に評価していることがわかります。

summary_by_segment_biased
# A tibble: 2 x 4
  treatment conversion_rate spend_mean count
      <dbl>           <dbl>      <dbl> <int>
1         0         0.00498      0.548 14665
2         1         0.0134       1.53  17198

有意差検定の結果を確認するとP値はさらに小さい値となっています。このようにバイアスのある状態では誤った評価結果を導いてしまいます。

rct_ttest

	Two Sample t-test

data:  mens_mail_biased and no_mail_biased
t = 5.6708, df = 31861, p-value = 1.433e-08
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 0.6409145 1.3179784
sample estimates:
mean of x mean of y 
1.5277526 0.5483062