何番煎じかわかりませんが、「効果検証入門 正しい比較のための因果推論/計量経済学の基礎」安井翔太著、株式会社ホクソエム監修をPythonで実装していきます。
この記事では該当書籍の第1章を扱います。
初学者なので記事内容に不備、不正確な点あればコメントいただけると幸いです。
##RCTとはなにか
RCTとはRandomised Controlled Trialの略であり、介入をする(施策を打つ)ユーザ郡を完全にランダムに選択したうえで実験をし、介入があった郡となかった郡の差分を分析することで介入の効果を検証する手法です。
RCTではランダムにユーザが選ばれるため、セレクションバイアスが生じにくく介入そのものの効果をより正確に検証することが可能です。
ちなみに、2019年のノーベル経済学賞は「世界の貧困削減に向けたフィールド実験」に対して与えられましたが、ここでいうフィールド実験がRCTの手法を用いてなされました。
##状況設定
さっそく実装へ写っていきたいと思います。
ここでは、このRCTについてあるECサイトのメール配信が例に取られています。
あるECサイトでランダムにメール配信(女性向け・男性向け・配信なし)を行った際、このメールの購買金額への影響はあったのか?というのを検証していきます。
##データセットについて
本文で使われているデータセットMineThatData E-Mail Analytics And Data Mining Challenge datasetを使用しています。
『このデータセットは、ECサイトのユーザに足していRCTを適用したメールマーケティングを行ったものです。』とのことです。
##環境について
- OS:Windows10
- Python:Python 3.8.5(Anaconda使っています)
- エディタ:VSCode
jupyter notebookみたいにも使えて便利ですね。
##データの読み込み
import requests
import io
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
url = 'http://www.minethatdata.com/Kevin_Hillstrom_MineThatData_E-MailAnalytics_DataMiningChallenge_2008.03.20.csv'
res = requests.get(url).content
df: pd.DataFrame = pd.read_csv(io.StringIO(res.decode('utf-8')), header=0)
(VSCodeで書いており、予測変換機能の都合からdfを生成するときに型ヒントを利用しています。)
##データの確認
まずは検証をする前にデータの中身の確認をしていきます。
全体を見る
df
>>>
recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
0 10 2) $100 - $200 142.44 1 0 Surburban 0 Phone Womens E-Mail 0 0 0.0
1 6 3) $200 - $350 329.08 1 1 Rural 1 Web No E-Mail 0 0 0.0
2 7 2) $100 - $200 180.65 0 1 Surburban 1 Web Womens E-Mail 0 0 0.0
3 9 5) $500 - $750 675.83 1 0 Rural 1 Web Mens E-Mail 0 0 0.0
4 2 1) $0 - $100 45.34 1 0 Urban 0 Web Womens E-Mail 0 0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ...
63995 10 2) $100 - $200 105.54 1 0 Urban 0 Web Mens E-Mail 0 0 0.0
63996 5 1) $0 - $100 38.91 0 1 Urban 1 Phone Mens E-Mail 0 0 0.0
63997 6 1) $0 - $100 29.99 1 0 Urban 1 Phone Mens E-Mail 0 0 0.0
63998 1 5) $500 - $750 552.94 1 0 Surburban 1 Multichannel Womens E-Mail 0 0 0.0
63999 1 4) $350 - $500 472.82 0 1 Surburban 0 Web Mens E-Mail 0 0 0.0
64000 rows × 12 columns
64000件のデータと12個の項目を持つデータであることがわかりました。
####データ型の確認
df.dtypes
>>>
recency int64
history_segment object
history float64
mens int64
womens int64
zip_code object
newbie int64
channel object
segment object
visit int64
conversion int64
spend float64
dtype: object
####様々な値の集計
df.describe().round(2)
>>>
recency history mens womens newbie visit conversion spend
count 64000.00 64000.00 64000.00 64000.00 64000.0 64000.00 64000.00 64000.00
mean 5.76 242.09 0.55 0.55 0.5 0.15 0.01 1.05
std 3.51 256.16 0.50 0.50 0.5 0.35 0.09 15.04
min 1.00 29.99 0.00 0.00 0.0 0.00 0.00 0.00
25% 2.00 64.66 0.00 0.00 0.0 0.00 0.00 0.00
50% 6.00 158.11 1.00 1.00 1.0 0.00 0.00 0.00
75% 9.00 325.66 1.00 1.00 1.0 0.00 0.00 0.00
max 12.00 3345.93 1.00 1.00 1.0 1.00 1.00 499.00
df.corr()
>>>
recency history mens womens newbie visit conversion spend
recency 1.000000 -0.246591 -0.031336 -0.026617 -0.052106 -0.074765 -0.024412 -0.016348
history -0.246591 1.000000 0.112677 0.114685 0.223279 0.065153 0.029405 0.021729
mens -0.031336 0.112677 1.000000 -0.816943 0.020900 0.006712 0.002492 0.008599
womens -0.026617 0.114685 -0.816943 1.000000 0.021346 0.051999 0.012702 0.002173
newbie -0.052106 0.223279 0.020900 0.021346 1.000000 -0.073924 -0.011331 -0.007623
visit -0.074765 0.065153 0.006712 0.051999 -0.073924 1.000000 0.230165 0.168507
conversion -0.024412 0.029405 0.002492 0.012702 -0.011331 0.230165 1.000000 0.732114
spend -0.016348 0.021729 0.008599 0.002173 -0.007623 0.168507 0.732114 1.000000
データ数は64000件、最終購入月からの平均経過月数は5.76ヶ月、昨年の購入学の平均は$242、平均購入金額は$1.05などといったことがわかりました。
相関係数を見るとSpendとConversionの相関係数が最も高く0.73となっています。その他の相関係数はほとんどが0.1を切っているような状況です。
##男性向けメールの効果検証
男性向けメールの検証のため、男性向けメール受信者とメール非受信者のデータを抽出します。
df2: pd.DataFrame = df[df.segment != 'Womens E-Mail']
df2['treatment'] = np.where(df2['segment'] == 'Mens E-Mail', 1, 0)
df_mens = df2[df2['treatment']==1]
df_nomail = df2[df2['treatment']==0]
ここで作ったdf_mensの群とdf_nomailの群の購入金額に有意差があるかを検証します。検定を実施する前に一度、データを確認します。
df_mens.describe()
>>>
recency history mens womens newbie visit conversion spend treatment
count 21307.000000 21307.000000 21307.000000 21307.000000 21307.000000 21307.000000 21307.000000 21307.000000 21307.0
mean 5.773642 242.835931 0.550946 0.551415 0.501525 0.182757 0.012531 1.422617 1.0
std 3.513350 260.355685 0.497409 0.497361 0.500009 0.386476 0.111241 17.754205 0.0
min 1.000000 29.990000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.0
25% 2.000000 63.580000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.0
50% 6.000000 157.220000 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 1.0
75% 9.000000 325.215000 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 1.0
max 12.000000 3215.970000 1.000000 1.000000 1.000000 1.000000 1.000000 499.000000 1.0
df_nomail.describe()
>>>
recency history mens womens newbie visit conversion spend treatment
count 21306.000000 21306.000000 21306.000000 21306.000000 21306.000000 21306.000000 21306.000000 21306.000000 21306.0
mean 5.749695 240.882653 0.553224 0.547639 0.501971 0.106167 0.005726 0.652789 0.0
std 3.497517 252.739362 0.497171 0.497737 0.500008 0.308059 0.075456 11.588200 0.0
min 1.000000 29.990000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.0
25% 2.000000 65.300000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.0
50% 5.000000 156.655000 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.0
75% 9.000000 325.167500 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.0
max 12.000000 3345.930000 1.000000 1.000000 1.000000 1.000000 1.000000 499.000000 0.0
Spendの額を比較すると
男性向けメール:1.422617
配信なし:0.652789
となっており、差が生じているように見えます。
これが本当に差があるかを検定により確認していきます。
##検定
ここではt検定と呼ばれる検定手法をつかいました1。Pythonではscipyのstatst.ttest_indを使うことでt検定を行うことができます。
import scipy.stats
t, p = stats.ttest_ind(df_mens.spend, df_nomail.spend, equal_var=True)
print('p-value =', p)
>>>
p-value = 1.163200872605869e-07
P-valueは2つの群の差が0である確率を示しており、今回のケースでは1.16e-07であり十分に小さくなっており、メール配信の効果はあったと言える結果がでています。
##まとめ
本項目ではRCTで得られた結果に対してt検定を用いて結果に有意差があったかを検証しました。
-
検定方法の選び方についてはまた別途まとめていきたいと思っています。 ↩