効果検証入門 1章をpythonでやってみた
効果検証入門〜正しい比較のための因果推論/計量経済学の基礎の学びを兼ねて、Rで書かれたコードをpythonに置き換えてみました。
コードはこちら。
書籍と数値が異なっている点もございます点ご了承ください。
間違いのご指摘等いただけると幸いです。
1章 セレクションバイアスとRCT
データセットはThe MineThatData E-Mail Analytics And Data Mining Challengeのメールマーケティングデータを使用
RCT(ランダム化比較実験)データの作成
# データの読込 import pandas as pd data = pd.read_csv("http://www.minethatdata.com/Kevin_Hillstrom_MineThatData_E-MailAnalytics_DataMiningChallenge_2008.03.20.csv")
segmentカラムのWomens E-Mailを除いて Mens E-Mail と No E-Mail のみにして、女性向けメールを除いたデータセットを作成
介入(メールの配信)の有無(Mens E-Mail なら 1、そうでなければ 0)を表す treatment 変数を追加
male_data = data[data["segment"] != "Womens E-Mail"] male_data["treatment"] = 0 male_data["treatment"].mask(male_data["segment"] == "Mens E-Mail", 1, inplace=True)
メールが配信されたグループとされなかったグループでの購入の発生確率(conversion)と購入額(spend)の平均を計算
male_data.groupby("treatment").mean()[["conversion", "spend"]]
male_data["treatment"].value_counts(sort=False)
0: 21306
1: 21307
Name: treatment, dtype: int64
男性向けメールが配信されたグループの購買データと、メールが配信されなかったグループの購買データを有意差検定
# 男性向けメールが配信されたグループの購買データ mens_mail = male_data[male_data["treatment"] == 1]["spend"] # メールが配信されなかったグループの購買データ no_mail = male_data[male_data["treatment"] == 0]["spend"] # 2群間でのt検定 from scipy import stats stats.ttest_ind(mens_mail, no_mail, equal_var=True)
Ttest_indResult(statistic=5.300090294465472, pvalue=1.163200872605869e-07)
p値は1.16e-07と非常に小さいので有意
バイアスのあるデータの作成
- 「去年の購入額であるhistoryが300より小さい場合」
- 「最後の購入であるrecencyが3より小さい場合」
- 「接触チャネル channelが複数あることを表すMultichannelである場合」
つまり、1. 過去に多額の購入があり、2. 直近も購入しており、3. 流入元が多ければ、購入に意欲的なユーザー
メールが配信されていないグループでは、3つの条件のどれかに該当するデータをランダムに半分選んで削除
→ メールが配信されていないグループは購入に意欲的なユーザーが半分削除される
→ メールが配信されていないグループは購入に意欲的でないバイアスがかかる
メールが配信されているグループでは、3つの条件に該当しないデータをランダムに半分選んで削除
→ メールが配信されているグループは購入に意欲的でないユーザーが半分削除される
→ メールが配信されているグループは購入に意欲的なバイアスがかかる
メールが配信されていないグループのデータを作成
# データをコピー biased_data = male_data.copy() # メールが配信されていないグループ fulfill_cond_data = male_data[((male_data["treatment"] == 0) & ((male_data["history"] > 300) | (male_data["recency"] < 6) | (biased_data["channel"] == "Multichannel")))] # 条件を満たすデータから半分抽出する fulfill_cond_data.sample(frac=0.5, random_state=42) # 条件を満たすデータの半分を削除する biased_data.drop(fulfill_cond_data.sample(frac=0.5, random_state=42).index, inplace=True)
メールが配信されているグループのデータを作成
# メールが配信されているグループ non_fulfill_cond_data = male_data[((biased_data["treatment"] == 1) & (male_data["history"] <= 300) & (male_data["recency"] >= 6) & (male_data["channel"] != "Multichannel"))] # 条件を満さないデータから半分抽出する non_fulfill_cond_data.sample(frac=0.5, random_state=42) # 条件を満たさないデータの半分を削除する biased_data.drop(non_fulfill_cond_data.sample(frac=0.5, random_state=42).index, inplace=True)
バイアスありのデータのconversion、spendの平均値とカウント数、バイアスなしのデータのconversion、spendの平均値とカウント数を比較
# バイアスありのデータ display(biased_data.groupby("treatment").mean()[["conversion", "spend"]]) print(biased_data["treatment"].value_counts(sort=False)) # バイアスなしのデータ print("\n比較 バイアスなしデータ") display(male_data.groupby("treatment").mean()[["conversion", "spend"]]) print(male_data["treatment"].value_counts(sort=False))
バイアスのあるデータで、男性向けメールが配信されたグループの購買データと、メールが配信されなかったグループの購買データを有意差検定
# 男性向けメールが配信されたグループの購買データ bias_mens_mail = biased_data[biased_data["treatment"] == 1]["spend"] # メールが配信されなかったグループの購買データ bias_no_mail = biased_data[biased_data["treatment"] == 0]["spend"] display(stats.ttest_ind(bias_mens_mail, bias_no_mail, equal_var=True))
Ttest_indResult(statistic=5.283787889513689, pvalue=1.273689559022462e-07)
バイアスありの場合の p値は 1.27e-07、バイアスなしの場合の p値は 1.16e-07
バイアスがあっても p値は非常に小さく、有意という結果となってしまう
# 2章のためにcsvの出力 male_data.to_csv('male_data.csv', index=False) biased_data.to_csv('biased_data.csv', index=False)
以上になります、最後までお読みいただきありがとうございました。