効果検証入門 × Python #1|セレクションバイアスとRCT
つちのこです!
効果検証入門 [ 著=安井翔太、監修=株式会社ホクソエム ]より、因果推論を学習した記録です。
本書内ではRで書かれていますが、Pythonで学習を行いました。
こちらTJOさんもおススメしている本で、気になっていたので購入させていただきました。 因果推論系の話は学習したことがなかったのですが、非常に読みやすい本だと思います。 ただし、基本的な統計知識は持っていることが前提で書かれていますので、ご注意ください。
第一章は「セレクションバイアスとRCT」です。 着実に1章1章理解していきたいと思います。曖昧な箇所もあると思いますが、ご助言いただけると助かります。
目次
セレクションバイアスとは
ある集団に施策を実施した際に、施策を実施する対象として偏りのある集合を選択してしまうことによって生じるバイアス。
これは、本来RCT(無作為化比較試験)を行うことで、対象の施策以外の影響を平均化できる。しかし、RCT実施にはコストがかかる為、現実では潜在的に効果が高くなる集合に対して施策が実施されることが多い。 つまり、現実ではセレクションバイアスを有するデータが取得され、そのデータを分析して効果を検証するため、正しい施策の効果が測れないという問題がある。
上図では、メール配信による売上の効果を検証したい場合、無作為に配信を行うことでその他要素が売上に与える影響を平均化することができるため、配信ありグループとなしグループ間の比較より、メール配信の効果を検証することができる。
一方、現実では、メール配信にはコストがかかる為、担当者は潜在的に購入傾向が高いユーザに重点的に配信を行う。それにより、配信ありグループとなしグループ間には、メール配信以外の要素が売上に与える影響も発生し、本来検証したいメール配信による効果が正しく検証できなくなる。
データから見るセレクションバイアス
データ
本書と同様に、Kevin Hillstrom: MineThatDataのデータセットを用いる。 このデータは、1年以内にあるサイトから購入した顧客64,000人のデータセットです。これらの顧客に対して、1:1:1=メール配信しない:男性向け商品のメール配信:女性向け商品のメール配信を実施し、2週間経過後のメール配信効果を記録したもの。
import pandas as pd
# データセットの取得(Kevin Hillstrom: MineThatData)
df = pd.read_csv("http://www.minethatdata.com/Kevin_Hillstrom_MineThatData_E-MailAnalytics_DataMiningChallenge_2008.03.20.csv")
各変数の説明は以下の通り。
項目 | 説明 | 型 |
---|---|---|
recency | 最後の購入からの月数 | 数値 |
history_segment | 過去1年間に購入された実際の金額(1-7) | カテゴリ |
history | 過去1年間に購入された実際の金額 | 数値 |
mens | 過去1年間にMens商品を購入したか | Boolean |
womens | 過去1年間にWomens商品を購入したか | Boolean |
zip_code | 郵便番号を基に、分類 (Urban/Rural/Suburban) |
カテゴリ |
newbie | 過去1年間の新規顧客か | Boolean |
channel | 過去1年間に顧客が購入したチャネル (Web/Phone/Multicannel) |
カテゴリ |
segment | メール配信種類 (Mens E-Mail/Womens E-Mail/No E-Mail) |
カテゴリ |
上記のメール配信による2週間後のメール配信(介入)の結果を表す変数は以下の通り。
項目 | 説明 | 型 |
---|---|---|
visit | 2週間以内にサイトへ訪問あったか | Boolean |
conversion | 2週間以内に商品を購入したか | Boolean |
spend | 2週間で実際に購入した金額 | 数値 |
ここでは、男性向けメールの介入効果について確認する。 (女性向けメールが配信されたデータは除去する)
# 女性向けメールが配信されているデータを除去
# →男性向けメール配信 or メール配信ナシ のデータが残る
mens = df[df["segment"] != "Womens E-Mail"].copy()
# ここで、介入を表す変数をtreatmentと定義し、segmentに対してラベルエンコーディングを行う
# Mens E-Mail:1, No E-Mail:0
mens["treatment"] = 0
mens.loc[mens["segment"]=="Mens E-Mail",["treatment"]] = 1
RCTでのメール配信効果
メール配信の介入効果
まずは、介入の効果を測る指標として、2週間後の結果を表す項目(visit
, conversion
, spend
)より、訪問率、購入率、売上平均を確認する。
summary = pd.DataFrame([],columns=["visit", "conversion", "spend"],index=["No E-Mails", "Mens E-Mails"])
# 介入なし
summary.loc["No E-Mails",:] = mens.groupby("treatment").mean().loc[0,["visit", "conversion", "spend"]]
# 介入あり
summary.loc["Mens E-Mails",:] = mens.groupby("treatment").mean().loc[1,["visit", "conversion", "spend"]]
visit conversion spend 介入なし 0.106167 0.00572609 0.652789 介入あり 0.182757 0.0125311 1.42262
結果より、メール配信の介入による効果は、以下のことが確認できる。
- 訪問率が7.7%向上
- 購入率が0.68%向上
- 平均売上が$0.77向上
有意差検定
上記の結果が統計的に有意差があるのか確認する。 ここでは、帰無仮説として「介入がある集団と介入がない集団の平均の差は0である」を立て、t検定を用いて検定を行う。
from scipy import stats
# t検定(分散が等しいと仮定)
stats.ttest_ind(mens.loc[mens["treatment"]==1, "spend"].values,
mens.loc[mens["treatment"]==0, "spend"].values,
equal_var = True)
Ttest_indResult(statistic=5.300090294465472, pvalue=1.163200872605869e-07)
p値が非常に小さい値をとるので、帰無仮説は棄却される。 つまり、介入がある集団と介入がない集団には統計的に有意な差が存在することが確認できる。
セレクションバイアスを含むデータでのメール配信効果
セレクションバイアスを含むデータ
現実では、マーケティング担当者は効果的にメール配信を実施するために、潜在的に購買傾向がある対象に重点的に配信を行うだろう。 よって、ここで潜在的に購買傾向がある対象を以下の条件を満たす顧客と仮定し、介入による効果を検証する。
- history > 300(過去1年間の購入金額が$300を上回る)
- recency < 6(過去6か月以内に購入がある)
- channel = Multichannel(複数チャネルからの購入がある)
データセットの作り方としては、RCT時に作成したmens
のデータセットから、「介入あり×購買傾向なし」「介入なし×購買傾向あり」のデータを一部除外することで、セレクションバイアスを含むデータセットを作成できる。
No. | 介入 | 購買傾向 | RCT | バイアスあり |
---|---|---|---|---|
1 | あり | あり | No.1-4は同質サンプル | 購買傾向が高い →メール配信あるサンプル多 |
2 | あり | なし | No.1-4は同質サンプル | 購買傾向が低い →メール配信あるサンプル少 |
3 | なし | あり | No.1-4は同質サンプル | 購買傾向が高い →メール配信無いサンプル少 |
4 | なし | なし | No.1-4は同質サンプル | 購買傾向が低い →メール配信無いサンプル多 |
これにより、購買傾向の高い顧客に対して重点的にメール配信を行う状況を模擬したデータセットが作成可能となる。
# 購買傾向がある一定数である条件
condition = (mens["history"] > 300) | (mens["recency"] < 6) | (mens["channel"] == "Multichannel")
# バイアスデータセットの作成
bias_mens = pd.concat([mens.loc[(condition) & (mens["treatment"]==1), :],
mens.loc[(~condition) & (mens["treatment"]==1), :].sample(frac=0.5, random_state=1),
mens.loc[(condition) & (mens["treatment"]==0), :].sample(frac=0.5, random_state=1),
mens.loc[(~condition) & (mens["treatment"]==0), :]
], ignore_index=True)
メール配信の介入効果
RCTと同様にメール配信の介入効果を測る指標として、訪問率、購入率、売上平均を算出する。
bias_summary = pd.DataFrame([],columns=["visit", "conversion", "spend"],index=["No E-Mails", "Mens E-Mails"])
# 介入なし
bias_summary.loc["No E-Mails",:] = bias_mens.groupby("treatment").mean().loc[0,["visit", "conversion", "spend"]]
# 介入あり
bias_summary.loc["Mens E-Mails",:] = bias_mens.groupby("treatment").mean().loc[1,["visit", "conversion", "spend"]]
visit conversion spend 介入なし 0.0973775 0.00454022 0.557954 介入あり 0.192917 0.0135718 1.5417 結果は、以下の通りとなる。
- 訪問率が9.6%向上(RCTの7.7%より向上)
- 購入率が0.90%向上(RCTの0.68%より向上)
- 平均売上が$0.98向上(RCTの$0.77より向上)
全ての指標において、RCT時の結果を上回っているが、メール配信による効果とそもそもの潜在的な購買傾向の差(セレクションバイアス)が含まれた効果を検証しているだけである。 つまり、本来検証したかったメール配信による効果を正しく測ることが出来ていないということが言える。
有意差検定
同様に、上記の結果が統計的に有意差があるのか確認する。
# t検定(ここでは、分散が等しいと仮定)
stats.ttest_ind(bias_mens.loc[bias_mens["treatment"]==1, "spend"].values,
bias_mens.loc[bias_mens["treatment"]==0, "spend"].values,
equal_var = True)
Ttest_indResult(statistic=5.595867225527975, pvalue=2.21319841336543e-08)
ここでも、RCTと同様に帰無仮説が棄却されるため、統計的に有意差がある。 RCTとセクションバイアスを含むデータによる確認結果より、統計的に有意だからと言って正しい効果が測れている訳ではないと言える。
次回は「2章 介入効果を測るための回帰分析」について学習を行う。 ブログ書くの時間かかる(理解できてないと文章に落とし込めない)けど、意外とこういうの好きなので、今後も学習記録残します。