网站数据用户行为分析 ---- A/B测试

news/2024/12/22 7:05:47/

分析A/B测试结果

目录

  • 简介
  • I - 概率
  • II - A/B 测试
  • III - 回归

简介

为了得出电子商务网站运行的 A/B 测试的结果,帮助公司弄清楚是否应该使用新的页面,保留旧的页面,或者应该将测试时间延长,之后再做出决定。

I - 概率

import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
%matplotlib inlinerandom.seed(42)

a. 导入数据集,并在这里查看前几行。

df = pd.read_csv('ab_data.csv')
df.head()
user_idtimestampgrouplanding_pageconverted
08511042017-01-21 22:11:48.556739controlold_page0
18042282017-01-12 08:01:45.159739controlold_page0
26615902017-01-11 16:55:06.154213treatmentnew_page0
38535412017-01-08 18:28:03.143765treatmentnew_page0
48649752017-01-21 01:52:26.210827controlold_page1

b. 探索数据集。

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 294478 entries, 0 to 294477
Data columns (total 5 columns):
user_id         294478 non-null int64
timestamp       294478 non-null object
group           294478 non-null object
landing_page    294478 non-null object
converted       294478 non-null int64
dtypes: int64(2), object(3)
memory usage: 11.2+ MB
df.shape
(294478, 5)

c. 数据集中独立用户的数量。

df['user_id'].nunique()
290584

d. 用户转化的比例。

df[df['converted'] == 1].count()[0] / df.count()[0]
0.11965919355605512

e. new_pagetreatment 不一致的次数。

df[(df['landing_page'] == 'old_page') & (df['group'] == 'treatment')].count()[0]
1965

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. 将删除不确定是否接收后的数据存储在 df2 中。

df_nc = df[(df['landing_page'] == 'new_page') & (df['group'] == 'control')]df_ot = df[(df['landing_page'] == 'old_page') & (df['group'] == 'treatment')]df_notclear = pd.concat([df_nc, df_ot])
df2 = df.drop(index=df_notclear.index)
# 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.head()
user_idtimestampgrouplanding_pageconverted
08511042017-01-21 22:11:48.556739controlold_page0
18042282017-01-12 08:01:45.159739controlold_page0
26615902017-01-11 16:55:06.154213treatmentnew_page0
38535412017-01-08 18:28:03.143765treatmentnew_page0
48649752017-01-21 01:52:26.210827controlold_page1

3. 使用 df2 与下面的单元格来回答课堂中的 测试3

a. df2 中有290584唯一的 user_id?

df2["user_id"].nunique()
290584

b. df2 中有一个重复的 773192 的ID

dup_user_id = df2["user_id"][df2["user_id"].duplicated()].get_values()[0]print("df2 中有一个重复的 {} 的ID".format(dup_user_id))
df2 中有一个重复的 773192 的ID

c. 观察这个重复的 user_id 的行信息。

df2[df2["user_id"] == dup_user_id]
user_idtimestampgrouplanding_pageconverted
18997731922017-01-09 05:37:58.781806treatmentnew_page0
28937731922017-01-14 02:55:59.590927treatmentnew_page0

d. 删除 一个 含有重复的 user_id 的行, 但需要确保你的 dataframe 为 df2

df2.drop(index=df2[df2["user_id"] == dup_user_id].index[0], inplace=True)

4.

a. 不管它们收到什么页面,单个用户的转化率是多少?

def tran_nub(data):tran = data[data['converted'] == 1].count()[0] / data.count()[0]    return tran
print("任意页面,单个用户的转化率是{:.4f}%。".format(tran_nub(df2)*100))
任意页面,单个用户的转化率是11.9597%。

b. 假定一个用户处于 control 组中的转化率

control_group = df[df['group'] == 'control']print("控制组中,单个用户的转化率是{:.4f}%。".format(tran_nub(control_group)*100));
控制组中,单个用户的转化率是12.0399%。

c. 假定一个用户处于 treatment 组中的转化率

treatment_group = df[df['group'] == 'treatment']print("测试组中,单个用户的转化率是{:.4f}%。".format(tran_nub(treatment_group)*100));
测试组中,单个用户的转化率是11.8920%。

d. 一个用户收到新页面的概率

rec_newpage = (df2['landing_page'] == 'new_page').mean()print("一个用户收到新页面的概率为{:.4f}%。".format(rec_newpage*100))
一个用户收到新页面的概率为50.0062%。

e. 没有证据表明新页面可以带来更多的转化.

II - A/B 测试

由于与每个事件相关的时间戳,进行每次观察时连续运行假设检验。

然而,问题的难点在于,一个页面被认为比另一页页面的效果好得多的时候你就要停止检验吗?还是需要在一定时间内持续发生?你需要将检验运行多长时间来决定哪个页面比另一个页面更好?

1. 现在,根据提供的所有数据做出决定。如果想假定旧的页面效果更好,除非新的页面在类型I错误率为5%的情况下才能证明效果更好,做出零假设和备择假设 p o l d p_{old} pold p n e w p_{new} pnew

H0: P_new - P_old <= 0

H1: P_new - P_old > 0

2. 假定在零假设中,不管是新页面还是旧页面, p n e w p_{new} pnew and p o l d p_{old} pold 都具有等于 转化 成功率的“真”成功率,也就是说, p n e w p_{new} pnew p o l d p_{old} pold 是相等的。

执行两次页面之间 转化 差异的抽样分布,计算零假设中10000次迭代计算的估计值。

使用下面的单元格提供这个模拟的必要内容。如果现在还没有完整的意义,不要担心,你将通过下面的问题来解决这个问题。你可以通过做课堂中的 测试 5 来确认你掌握了这部分内容。

a. 在零假设中, p n e w p_{new} pnewconvert rate(转化率) 是11.9597% 。

P_new = df2['converted'].mean()
print(P_new)
0.11959708724499628

b. 在零假设中, p o l d p_{old} poldconvert rate(转化率) 是是11.9597%

P_old = P_new
print(P_old)
0.11959708724499628

c. n n e w n_{new} nnew 是多少?

n_new = df2[(df2['landing_page']=='new_page')].count()[0]
print(n_new)
145310

d. n o l d n_{old} nold?是多少?

n_old = df2[(df2['landing_page']=='old_page')].count()[0]
print(n_old)
145274

e. 在零假设中,使用 p n e w p_{new} pnew 转化率模拟 n n e w n_{new} nnew 交易,并将这些 n n e w n_{new} nnew 1’s 与 0’s 存储在 new_page_converted 中。(提示:可以使用 numpy.random.choice。)

new_page_converted = np.random.choice([0,1],size=n_new,p=[(1-P_new), P_new])
print(new_page_converted)
[0 0 0 ... 0 0 0]

f. 在零假设中,使用 p o l d p_{old} pold 转化率模拟 n o l d n_{old} nold 交易,并将这些 n o l d n_{old} nold 1’s 与 0’s 存储在 old_page_converted 中。

old_page_converted = np.random.choice([0,1],size=n_old,p=[(1-P_old), P_old])
print(old_page_converted)
[0 0 0 ... 0 0 0]

g. 在 (e) 与 (f)中找到 p n e w p_{new} pnew - p o l d p_{old} pold 模拟值。

p_diff = new_page_converted.mean() - old_page_converted.mean()
print(p_diff)
0.0002731376695282173

h. 使用**a. 到 g. ** 中的计算方法来模拟 10,000个 p n e w p_{new} pnew - p o l d p_{old} pold 值,并将这 10,000 个值存储在 p_diffs 中。

p_diffs = []
for _ in range(10000):new_page_converted = np.random.choice([0,1],size=n_new,p=[1-P_new, P_new])old_page_converted = np.random.choice([0,1],size=n_old,p=[1-P_old, P_old])p_diff =  new_page_converted.mean() - old_page_converted.mean()p_diffs.append(p_diff)

i. 绘制一个 p_diffs 直方图。

p_diffs = np.array(p_diffs)
plt.hist(p_diffs)

在这里插入图片描述

j. 在p_diffs列表的数值中,有90.44%大于原数据 中观察到的实际差值

plt.hist(p_diffs)
obs_diff = df2[df2['group'] == 'treatment']['converted'].mean() - df2[df2['group'] == 'control']['converted'].mean()
plt.axvline(obs_diff, color='red')

在这里插入图片描述

(p_diffs > obs_diff).mean()
0.9046

k. 在 **j.**中计算出来的结果,新旧页面的转化率有区别

  • p-value = 0.9036.
  • 根据p-value的值无法拒绝零假设.

l. 我们也可以使用一个内置程序 (built-in)来实现类似的结果。尽管使用内置程序可能更易于编写代码,但上面的内容是对正确思考统计显著性至关重要的思想的一个预排。填写下面的内容来计算每个页面的转化次数,以及每个页面的访问人数。使用 n_oldn_new 分别引证与旧页面和新页面关联的行数。

import statsmodels.api as smconvert_old = df2[df2['group']=='control']['converted'].sum()
convert_new = df2[df2['group']=='treatment']['converted'].sum()
n_old = df2[df2['group']=='control'].count()[0]
n_new = df2[df2['group']=='treatment'].count()[0]

m. 现在使用 stats.proportions_ztest 来计算你的检验统计量与 p-值。这里 是使用内置程序的一个有用链接。

z_score, p_value = sm.stats.proportions_ztest([convert_old, convert_new], [n_old, n_new], alternative='smaller')
print(z_score, p_value)
1.3109241984234394 0.9050583127590245
from scipy.stats import normprint(norm.cdf(z_score), norm.ppf(1-(0.05)))
0.9050583127590245 1.6448536269514722

n. * z-score 1.3109241984234394 < 1.6448536269514722, 在95%的置信区间上.

  • 不能拒绝零假设,所以老页面转化率至少和新页面一样好.
  • j 和 k 结果一致

III - 回归分析法之一

1. 之前的A / B测试中获得的结果也可以通过执行回归来获取。

a. 既然每行的值是转化或不转化,那么在这种情况下,我们应该使用逻辑回归

b. 目标是使用 statsmodels 来拟合你在 a. 中指定的回归模型,以查看用户收到的不同页面是否存在显著的转化差异。为每个用户收到的页面创建一个虚拟变量列。添加一个 截距 列,一个 ab_page 列,当用户接收 treatment 时为1, control 时为0。

df2[['control', 'treatment']] = pd.get_dummies(df2['group'])
df2['ab_page'] = df2['treatment']

c. 使用 statsmodels 导入回归模型。 实例化该模型,并使用你在 b. 中创建的2个列来拟合该模型,用来预测一个用户是否会发生转化。

df2['intercept'] = 1
logit_mod = sm.Logit(df2['converted'], df2[['intercept', 'ab_page']]) # logit模型拟合
results = logit_mod.fit()
Optimization terminated successfully.Current function value: 0.366118Iterations 6

d. 请在下方提供你的模型摘要,并根据需要使用它来回答下面的问题。

results.summary()
Logit Regression Results
Dep. Variable:converted No. Observations: 290584
Model:Logit Df Residuals: 290582
Method:MLE Df Model: 1
Date:Mon, 08 Jul 2019 Pseudo R-squ.: 8.077e-06
Time:22:31:55 Log-Likelihood: -1.0639e+05
converged:True LL-Null: -1.0639e+05
LLR p-value: 0.1899
coefstd errzP>|z|[0.0250.975]
intercept -1.9888 0.008 -246.669 0.000 -2.005 -1.973
ab_page -0.0150 0.011 -1.311 0.190 -0.037 0.007

e. p-value = 0.190,

  • 零假设为ab_page = 0, 备择假设为 ab_page != 0. 与零假设和备择假设不同.所以导致p-value 不同.

f. 现在,你一定在考虑其他可能影响用户是否发生转化的因素。讨论为什么考虑将其他因素添加到回归模型中是一个不错的主意。在回归模型中添加附加项有什么弊端吗?

在这里给出你的答案。

单一元素对预测人的行为是困难的, 将其他因素添加到回归模型中可以更准确的预测人的行为.弊端是这些因素可能并不是相互独立的,可能相互之间会产生影响

g. 现在,除了测试不同页面的转化率是否会发生变化之外,还要根据用户居住的国家或地区添加一个 effect 项。

df_countries = pd.read_csv('countries.csv')
df_countries.head()
user_idcountry
0834778UK
1928468US
2822059UK
3711597UK
4710616UK
df3 = df_countries.set_index('user_id').join(df2.set_index('user_id'), how='inner')
df3.head()
countrytimestampgrouplanding_pageconvertedcontroltreatmentab_pageintercept
user_id
834778UK2017-01-14 23:08:43.304998controlold_page01001
928468US2017-01-23 14:44:16.387854treatmentnew_page00111
822059UK2017-01-16 14:04:14.719771treatmentnew_page10111
711597UK2017-01-22 03:14:24.763511controlold_page01001
710616UK2017-01-16 13:14:44.000513treatmentnew_page00111
# 创建虚拟列
df3[['CA', 'UK', 'US']] = pd.get_dummies(df3['country'])
df3 = df3.drop(['CA'], axis=1)
# 逻辑回归
df3['intercept'] = 1
logit_mod = sm.Logit(df3['converted'], df3[['intercept', 'US', 'UK']])
results = logit_mod.fit()         # logit模型拟合
results.summary()
Optimization terminated successfully.Current function value: 0.366116Iterations 6
Logit Regression Results
Dep. Variable:converted No. Observations: 290584
Model:Logit Df Residuals: 290581
Method:MLE Df Model: 2
Date:Mon, 08 Jul 2019 Pseudo R-squ.: 1.521e-05
Time:22:35:49 Log-Likelihood: -1.0639e+05
converged:True LL-Null: -1.0639e+05
LLR p-value: 0.1984
coefstd errzP>|z|[0.0250.975]
intercept -2.0375 0.026 -78.364 0.000 -2.088 -1.987
US 0.0408 0.027 1.518 0.129 -0.012 0.093
UK 0.0507 0.028 1.786 0.074 -0.005 0.106
  • US和UK的p-value > 0.05, 说明国家差异对转化率影响不显著.

h. 考虑国家与页面在转化率上的个体性因素,但查看页面与国家/地区之间的相互作用,测试其是否会对转化产生重大影响。创建必要的附加列,并拟合一个新的模型。

df3['new_CA'] = df3['new_page'] * df3['CA']
df3['new_UK'] = df3['new_page'] * df3['UK']
df3['UK_ab_page'] = df3['UK'] * df3['ab_page']
df3.head()
countrytimestampgrouplanding_pageconvertedcontroltreatmentab_pageinterceptUKUSUK_ab_page
user_id
834778UK2017-01-14 23:08:43.304998controlold_page01001100
928468US2017-01-23 14:44:16.387854treatmentnew_page00111010
822059UK2017-01-16 14:04:14.719771treatmentnew_page10111101
711597UK2017-01-22 03:14:24.763511controlold_page01001100
710616UK2017-01-16 13:14:44.000513treatmentnew_page00111101
# 逻辑回归
df3['intercept'] = 1
logit_mod = sm.Logit(df3['converted'], df3[['intercept', 'ab_page', 'US', 'UK', 'UK_ab_page']])
results = logit_mod.fit()   # logit模型拟合
results.summary()
Optimization terminated successfully.Current function value: 0.366110Iterations 6
Logit Regression Results
Dep. Variable:converted No. Observations: 290584
Model:Logit Df Residuals: 290579
Method:MLE Df Model: 4
Date:Mon, 08 Jul 2019 Pseudo R-squ.: 3.125e-05
Time:22:36:47 Log-Likelihood: -1.0639e+05
converged:True LL-Null: -1.0639e+05
LLR p-value: 0.1557
coefstd errzP>|z|[0.0250.975]
intercept -2.0257 0.027 -75.518 0.000 -2.078 -1.973
ab_page -0.0236 0.013 -1.785 0.074 -0.049 0.002
US 0.0407 0.027 1.515 0.130 -0.012 0.093
UK 0.0335 0.031 1.070 0.285 -0.028 0.095
UK_ab_page 0.0344 0.026 1.306 0.192 -0.017 0.086
  • UK_ab_page p-value=0.192 > 0.05, 说明国家-页面的差异对转化率的影响不显著.

结论

  • 不应该运行新页面

http://www.ppmy.cn/news/772476.html

相关文章

bzoj1899 zjoi 午餐

http://www.elijahqi.win/archives/543 描述 上午的训练结束了&#xff0c;THU ACM小组集体去吃午餐&#xff0c;他们一行N人来到了著名的十食堂。这里有两个打饭的窗口&#xff0c;每个窗口同一时刻只能给一个人打饭。由于每个人的口味&#xff08;以及胃口&#xff09;不同…

1899最大和难题

1899: 985的最大和难题 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 329 Solved: 41 Submit Status Web Board Description 985有2 * n - 1个整数&#xff0c;他每次可以将其中n个数变号&#xff0c;操作次数不限&#xff0c;问他可以得到的最大和。 Input 第一行输入…

A. Division?

time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Codeforces separates its users into 44 divisions by their rating: For Division 1: 1900≤rating1900≤ratingFor Division 2: 1600≤rating≤18…

bzoj 1899 贪心+dp

思路&#xff1a;这个贪心排顺序我居然没看出来。 吃饭时间长的在前面&#xff0c; 用反证法很容易得出。 剩下的就是瞎dp啦。 #include<bits/stdc.h> #define LL long long #define fi first #define se second #define mk make_pair #define PII pair<int, int> …

FJUT ACM 1899 Largest Rectangle in a Histogram

#include<bits/stdc.h> using namespace std; typedef long long ll; /** 【思路】&#xff1a;其实一开始维护一个单调的栈&#xff0c; 这个栈存储序号&#xff0c;然后判断是不是可以填入&#xff0c; 如果可以填进去&#xff0c;就是维护一个单调递增的栈 如果输入一…

深入理解Gin框架中Trie树的实现原理

本文将详细介绍Gin框架中Trie树的实现原理&#xff0c;并提供简单的代码示例帮助读者更好地理解。我们将从基本概念开始&#xff0c;逐步构建类似Gin框架中的Trie树&#xff0c;并演示如何使用该Trie树进行路由匹配。通过本文的阅读&#xff0c;读者将能够深入理解Gin框架中Tri…

数据结构错题集 第八章 排序

8.1 3 B 稳定性问题&#xff1a; 是按关键字排序的 数值一样的两个数是两个不同的关键字 顺序可能不同 4.记住公式即可 8.2 B D与初始序列无关 选择排序&#xff1a;在n个中选择最小的 放在第一个 在n-1个中 选择第二小的放在第二个 快速排序 越有序 反而越复杂化 直接插入…

基尼gini系数-决策树

CART树采用基尼系数分割&#xff0c;而不是信息增益。