分析A/B测试结果
目录
简介
对于这个项目,你将要了解的是电子商务网站运行的 A/B 测试的结果。你的目标是通过这个 notebook 来帮助公司弄清楚他们是否应该使用新的页面,保留旧的页面,或者应该将测试时间延长,之后再做出决定。
I - 概率
让我们先导入库,然后开始你的任务吧。
import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
%matplotlib inline
#We are setting the seed to assure you get the same answers on quizzes as we set up
random.seed(42)
1.
现在,导入 ab_data.csv
数据,并将其存储在 df
中。
a. 导入数据集,并在这里查看前几行:
df=pd.read_csv('ab_data.csv')
df.head()
b. 使用下面的单元格来查找数据集中的行数。
df.shape[0]
294478
c. 数据集中独立用户的数量。
df.user_id.nunique()
290584
d. 用户转化的比例。
df.converted.mean()
0.11965919355605512
e. new_page 与treatment 不一致的次数。
treatment = df['group'] == 'treatment'
new_page = df['landing_page'] == 'new_page'
mismatch = treatment != new_page
mismatch.sum()
3893
f. 是否有任何行存在缺失值?
df.isnull().sum()
user_id 0
timestamp 0
group 0
landing_page 0
converted 0
dtype: int64
2.
对于 treatment 不与 new_page 一致的行或 control 不与 old_page 一致的行,我们不能确定该行是否真正接收到了新的或旧的页面。我们应该如何处理这些行?
a. 现在创建一个符合测试规格要求的新数据集。将新 dataframe 存储在 df2 中。
df2 = df[~mismatch].copy()
# Double Check all of the correct rows were removed - this should be 0
df2[((df2['group'] == 'treatment') == (df2['landing_page'] == 'new_page')) == False].shape[0]
0
df2[((df2['group'] == 'control') == (df2['landing_page'] == 'old_page')) == False].shape[0]
0
3.
a. df2 中有多少唯一的 user_id?
df2.user_id.nunique()
290584
b. df2 中有一个重复的 user_id 。它是什么?
df2[df2.user_id.duplicated(keep=False)].user_id
1899 773192
2893 773192
Name: user_id, dtype: int64
c. 这个重复的 user_id 的行信息是什么?
df2.query('user_id=="773192"')
d. 删除 一个 含有重复的 user_id 的行, 但需要确保你的 dataframe 为 df2。
df2=df2.drop_duplicates(subset=['user_id'], keep='first')
sum(df2.duplicated())
0
df2.shape
(290584, 5)
4.
a. 不管它们收到什么页面,单个用户的转化率是多少?
df2.converted.mean()
0.11959708724499628
b. 假定一个用户处于 control
组中,他的转化率是多少?
df2.query('group=="control"')['converted'].mean()
0.1203863045004612
c. 假定一个用户处于 treatment
组中,他的转化率是多少?
df2.query('group=="treatment"')['converted'].mean()
0.11880806551510564
d. 一个用户收到新页面的概率是多少?
df2.query('landing_page=="new_page"').shape[0]/df2.shape[0]
0.5000619442226688
e. 使用这个问题的前两部分的结果,给出你的建议:你是否认为有证据表明一个页面可以带来更多的转化?在下面写出你的答案。
截止目前并没有证据可以证明某一页面可以带来更多的转化率
II - A/B 测试
请注意,由于与每个事件相关的时间戳,你可以在进行每次观察时连续运行假设检验。
然而,问题的难点在于,一个页面被认为比另一页页面的效果好得多的时候你就要停止检验吗?还是需要在一定时间内持续发生?你需要将检验运行多长时间来决定哪个页面比另一个页面更好?
1.
现在,你要考虑的是,你需要根据提供的所有数据做出决定。如果你想假定旧的页面效果更好,除非新的页面在类型I错误率为5%的情况下才能证明效果更好,那么,你的零假设和备择假设是什么? 你可以根据单词或旧页面与新页面的转化率
与
来陈述你的假设。
零假设: - <=0
备择假设: - >0
2.
假定在零假设中,不管是新页面还是旧页面,
and
都具有等于 转化 成功率的“真”成功率,也就是说,
与
是相等的。此外,假设它们都等于ab_data.csv 中的 转化 率,新旧页面都是如此。
每个页面的样本大小要与 ab_data.csv 中的页面大小相同。
执行两次页面之间 转化 差异的抽样分布,计算零假设中10000次迭代计算的估计值。
使用下面的单元格提供这个模拟的必要内容。如果现在还没有完整的意义,不要担心,你将通过下面的问题来解决这个问题。
a. 在零假设中, 的 convert rate(转化率) 是多少?
p_new=df2.converted.mean()
p_new
0.11959708724499628
b. 在零假设中,
的 convert rate(转化率) 是多少?
p_old=df2.converted.mean()
p_old
0.11959708724499628
c. 是多少?
n_new=df2.query('landing_page=="new_page"').shape[0]
n_new
145310
d. ?是多少?
n_old=df2.query('landing_page=="old_page"').shape[0]
n_old
145274
e. 在零假设中,使用 转化率模拟 交易,并将这些 1’s 与 0’s 存储在 new_page_converted 中。
random.seed(42)
new_page_converted=np.random.choice(2,size=n_new,p=[1-p_new,p_new])
new_page_converted
array([0, 0, 0, ..., 0, 0, 1], dtype=int64)
f. 在零假设中,使用 转化率模拟 交易,并将这些 1’s 与 0’s 存储在 old_page_converted 中。
random.seed(42)
old_page_converted=np.random.choice(2,size=n_old,p=[1-p_old,p_old])
old_page_converted
array([0, 0, 0, ..., 1, 0, 0], dtype=int64)
g. 在 (e) 与 (f)中找到 - 模拟值。
diff=new_page_converted.mean()-old_page_converted.mean()
diff
0.0004797582554228047
h. 使用**a. 到 g. ** 中的计算方法来模拟 10,000个 - 值,并将这 10,000 个值存储在 p_diffs 中。
p_diffs=[]
for i in range(10000):
p_new_diff = np.random.choice(2,size=n_new,p=[1-p_new,p_new]).mean()
p_old_diff = np.random.choice(2,size=n_old,p=[1-p_old,p_old]).mean()
p_diffs.append(p_new_diff - p_old_diff)
i. 绘制一个 p_diffs 直方图。这个直方图看起来像你所期望的吗?
p_diffs = np.array(p_diffs)
plt.hist(p_diffs)
j. 在p_diffs列表的数值中,有多大比例大于 ab_data.csv 中观察到的实际差值?
obs_diff=df2.query('landing_page=="new_page"')['converted'].mean()-df2.query('landing_page=="old_page"')['converted'].mean()
obs_diff
-0.0015782389853555567
(p_diffs>obs_diff).mean()
0.9079
k. 用文字解释一下你刚才在 **j.**中计算出来的结果。在科学研究中,这个值是什么? 根据这个数值,新旧页面的转化率是否有区别呢?
p-value,p值等于0.9079较大,我们无法拒绝零假设
l. 我们也可以使用一个内置程序 (built-in)来实现类似的结果。尽管使用内置程序可能更易于编写代码,但上面的内容是对正确思考统计显著性至关重要的思想的一个预排。填写下面的内容来计算每个页面的转化次数,以及每个页面的访问人数。使用 n_old
与 n_new
分别引证与旧页面和新页面关联的行数。
import statsmodels.api as sm
convert_old = df2.query('group=="control" & converted==1').shape[0]
convert_new = df2.query('group=="treatment" & converted==1').shape[0]
n_old = df2.query('group=="control"').shape[0]
n_new = df2.query('group=="treatment"').shape[0]
m. 现在使用 stats.proportions_ztest
来计算你的检验统计量与 p-值。这里 是使用内置程序的一个有用链接。
z_score,p_value=sm.stats.proportions_ztest([convert_old, convert_new], [n_old, n_new],alternative='smaller')
z_score,p_value
(1.3109241984234394, 0.9050583127590245)
from scipy.stats import norm
norm.cdf(z_score),norm.ppf(1-(0.05))
(0.9050583127590245, 1.6448536269514722)
n. 根据上题算出的 z-score 和 p-value,我们认为新旧页面的转化率是否有区别?它们与 j. 与 k. 中的结果一致吗?
由于z-score为1.3109小于1.64485,则我们无法拒绝零假设,这与之前的结果一致。
III - 回归分析法之一
1.
在最后一部分中,你会看到,你在之前的A / B测试中获得的结果也可以通过执行回归来获取。
a. 既然每行的值是转化或不转化,那么在这种情况下,我们应该执行哪种类型的回归?
逻辑回归
b. 目标是使用 statsmodels 来拟合你在 a. 中指定的回归模型,以查看用户收到的不同页面是否存在显著的转化差异。但是,首先,你需要为这个截距创建一个列( 原文:column) ,并为每个用户收到的页面创建一个虚拟变量列。添加一个 截距 列,一个 ab_page 列,当用户接收 treatment 时为1, control 时为0。
import statsmodels.api as sm
df2['ab_page']=df2.group.map({'treatment':1,'control':0})
df2['intercept']=1
c. 使用 statsmodels 导入你的回归模型。 实例化该模型,并使用你在 b. 中创建的2个列来拟合该模型,用来预测一个用户是否会发生转化。
logit_mod=sm.Logit(df2['converted'],df2[['intercept','ab_page']])
result=logit_mod.fit()
Optimization terminated successfully.
Current function value: 0.366118
Iterations 6
d. 请在下方提供你的模型摘要,并根据需要使用它来回答下面的问题。
result.summary()
e. 与 ab_page 关联的 p-值是多少? 为什么它与你在 II 中发现的结果不同?
提示: 与你的回归模型相关的零假设与备择假设分别是什么?它们如何与 Part II 中的零假设和备择假设做比较?
ab_page关联的p-值为0.19,而第2部分中p-值为0.9079,两种情况中p-值不同的原因是检验的方向性不同,再假设检验中的p-值(0.9079)使用的是单尾检验,而这里的p-值表示ab_page因素与转化率是否有相关性,应该为双尾检验。结果表明ab_page这个因素不太适合用来预测转化率。
f. 现在,你一定在考虑其他可能影响用户是否发生转化的因素。讨论为什么考虑将其他因素添加到回归模型中是一个不错的主意。在回归模型中添加附加项有什么弊端吗?
在实际应用中,可能会有多种因素会影响到响应变量,添加其他因素可以更好的分析影响结果的变量;但是随着附加项越多,发生错误推论的可能性就越大。比如自变量彼此相关就会造成多重共线性,导致回归系数偏离想要的方向。
g. 现在,除了测试不同页面的转化率是否会发生变化之外,还要根据用户居住的国家或地区添加一个 effect 项。你需要导入 countries.csv 数据集,并将数据集合并在适当的行上。 这里 是链接表格的文档。
这个国家项对转化有影响吗?
df3=pd.read_csv('countries.csv')
df3=df2.merge(df3,on='user_id')
df3.isnull().sum()
user_id 0
timestamp 0
group 0
landing_page 0
converted 0
ab_page 0
intercept 0
country 0
dtype: int64
df3[['CA','UK','US']]=pd.get_dummies(df3['country'])
df3=df3.drop('US',axis=1)
df3.sample(5)
df3['intercept']=1
logit_mod=sm.Logit(df3.converted,df3[['intercept','ab_page','CA','UK']])
result=logit_mod.fit()
result.summary()
Optimization terminated successfully.
Current function value: 0.366113
Iterations 6
1/np.exp(-0.0408),np.exp(0.0099)
(1.0416437559600236, 1.0099491671175422)
从结果来看国家项并不具有统计显著性,对转化率影响不大。
h. 虽然你现在已经查看了国家与页面在转化率上的个体性因素,但现在我们要查看页面与国家/地区之间的相互作用,测试其是否会对转化产生重大影响。创建必要的附加列,并拟合一个新的模型。
提供你的摘要结果,以及根据结果得出的结论。
提示:页面与国家/地区的相互作用
df3['new_CA'] = df3['new_page'] * df3['CA']
df3['new_UK'] = df3['new_page'] * df3['UK']
df3['new_page']=df3.landing_page.map({'new_page':1,'old_page':0})
df3.head()
df3['new_CA'] = df3['new_page'] * df3['CA']
df3['new_UK'] = df3['new_page'] * df3['UK']
df3['intercept']=1
logit_mod=sm.Logit(df3.converted,df3[['intercept','ab_page','CA','new_CA',]])
result=logit_mod.fit()
result.summary()
1/np.exp(-0.0123),1/np.exp(-0.0160),1/np.exp(-0.0552)
(1.0123759561005452, 1.0161286854060947, 1.0567519439306503)
df3['intercept']=1
logit_mod=sm.Logit(df3.converted,df3[['intercept','ab_page','UK','new_UK',]])
result=logit_mod.fit()
result.summary()
1/np.exp(-0.0236),np.exp(0.0046),np.exp(0.0345)
(1.023880683695766, 1.0046105962413399, 1.0351020283761143)
总结
单纯从转化率角度考虑,新旧页面并没有太大差异。看来公司想要弄清楚结果,或许要延长测试时间,再从其他方面考虑后做出决定了。