使用 R 编程进行 AB 测试
拆分测试是 A/B 测试的另一个名称,它是一种常见或通用的方法。当人们想要测试新功能或产品时,它会在线使用。这里的主要议程是设计一个实验,提供可重复的结果并稳健地做出是否启动它的明智决定。通常,此测试包括通过代表变体 A 和 B 来比较两个网页,因为访问者数量相似,变体给出的转化率会更好。总体而言,这是一项实验,通过向实时访问者展示同一网页的两个或更多变体,将它们进行比较,并通过该实验确定哪个变体在给定目标方面表现更好。 A/B 测试不仅限于网页使用或限制,它还可以用于电子邮件、弹出窗口、注册表单、应用程序等。让我们 查看案例研究的例子。那么让我们用R语言来实现AB测试。
案例分析
假设我们有来自两个酒店预订网站的 A/B 测试结果,(注意:数据不是真实的)。首先,我们需要对数据进行测试分析;其次,我们需要从第一步获得的数据中得出结论,最后一步,我们向产品或管理团队提出建议或建议。
数据集摘要
从这里下载数据集。
- 变体 A 来自控制组,它告诉网站上现有的功能或产品。
- 变体 B 来自实验组,用于检查功能或产品的新版本,以查看用户是否喜欢它或是否增加了转化(预订)。
- Converted 是基于给定的数据集,有两个类别是由逻辑值定义的。当客户完成预订时它将显示为真,当客户访问网站但未进行预订时它将显示为假。
检验假设
- 零假设:版本 A 和 B 具有相同的转化或推动客户预订的概率。换句话说,A和B版本之间没有区别或没有影响
- 备择假设:版本 A 和 B 具有不同的转换概率或推动客户预订,并且 A 和 B 版本之间存在差异。在推动客户预订方面,版本 B 优于版本 A。 PExp_B! = Pcont_A。
R中的分析
1.准备数据集并加载包含用于分析的相关包的tidyverse库。
R
# load the library
library(tidyverse)
# set up your own directory
setwd(“~egot_\\Projects\\ABTest”)
# Using read.csv base import function
ABTest <- read.csv("Website Results.csv",
header = TRUE)
# save in your own directory
save(ABTest, file = "~rda\\ABTest.rda")
R
# Let's filter out conversions for variant_A
conversion_subset_A <- ABTest %>%
filter(variant == "A" & converted == "TRUE")
# Total Number of Conversions for variant_A
conversions_A <- nrow(conversion_subset_A)
# Number of Visitors for variant_A
visitors_A <- nrow(ABTest %>%
filter(variant == "A"))
# Conversion_rate_A
conv_rate_A <- (conversions_A/visitors_A)
print(conv_rate_A) # 0.02773925
# Let's take a subset of conversions for variant_B
conversion_subset_B <- ABTest %>%
filter(variant == "B" & converted == "TRUE")
# Number of Conversions for variant_B
conversions_B <- nrow(conversion_subset_B)
# Number of Visitors for variant_B
visitors_B <- nrow(ABTest %>%
filter(variant == "B"))
# Conversion_rate_B
conv_rate_B <- (conversions_B/visitors_B)
print(conv_rate_B) # 0.05068493
R
uplift <- (conv_rate_B - conv_rate_A) / conv_rate_A * 100
uplift # 82.72%
R
# Pooled sample proportion for variants A & B
p_pool <- (conversions_A + conversions_B) / (visitors_A +
visitors_B)
print(p_pool) # 0.03928325
# Let's compute Standard error for variants A & B (SE_pool)
SE_pool <- sqrt(p_pool * (1 - p_pool) * ((1 / visitors_A) +
(1 / visitors_B)))
print(SE_pool) # 0.01020014
# Let's compute the margin of error for the pool
MOE <- SE_pool * qnorm(0.975)
print(MOE) # 0.0199919
# Point Estimate or Difference in proportion
d_hat <- conv_rate_B - conv_rate_A
R
# Compute the Z-score so we
# can determine the p-value
z_score <- d_hat / SE_pool
print(z_score) # 2.249546
R
# Let's compute p_value
# using the z_score value
p_value <- pnorm(q = -z_score,
mean = 0,
sd = 1) * 2
print(p_value) # 0.02447777
R
# Let's compute Confidence interval for the
# pool using pre-calculated results
ci <- c(d_hat - MOE, d_hat + MOE)
ci # 0.002953777 0.042937584
# Using same steps as already shown,
# let's compute the confidence
# interval for variants A separately
X_hat_A <- conversions_A / visitors_A
se_hat_A <- sqrt(X_hat_A * (1 - X_hat_A) / visitors_A)
ci_A <- c(X_hat_A - qnorm(0.975) * se_hat_A, X_hat_A
+ qnorm(0.975) * se_hat_A)
print(ci_A) # 0.01575201 0.03972649
# Using same steps as already shown,
# let's compute the confidence
# interval for variants B separately
X_hat_B <- conversions_B / visitors_B
se_hat_B <- sqrt(X_hat_B * (1 - X_hat_B) / visitors_B)
ci_B <- c(X_hat_B - qnorm(0.975) * se_hat_B,
X_hat_B + qnorm(0.975) * se_hat_B)
print(ci_B) # 0.03477269 0.06659717
R
vis_result_pool <- data.frame(
metric = c(
'Estimated Difference',
'Relative Uplift(%)',
'pooled sample proportion',
'Standard Error of Difference',
'z_score',
'p-value',
'Margin of Error',
'CI-lower',
'CI-upper'),
value = c(
conv_rate_B - conv_rate_A,
uplift,
p_pool,
SE_pool,
z_score,
p_value,
MOE,
ci_lower,
ci_upper
))
vis_result_pool
2. 让我们过滤变体 A 和 B 的转化并计算它们相应的转化率
电阻
# Let's filter out conversions for variant_A
conversion_subset_A <- ABTest %>%
filter(variant == "A" & converted == "TRUE")
# Total Number of Conversions for variant_A
conversions_A <- nrow(conversion_subset_A)
# Number of Visitors for variant_A
visitors_A <- nrow(ABTest %>%
filter(variant == "A"))
# Conversion_rate_A
conv_rate_A <- (conversions_A/visitors_A)
print(conv_rate_A) # 0.02773925
# Let's take a subset of conversions for variant_B
conversion_subset_B <- ABTest %>%
filter(variant == "B" & converted == "TRUE")
# Number of Conversions for variant_B
conversions_B <- nrow(conversion_subset_B)
# Number of Visitors for variant_B
visitors_B <- nrow(ABTest %>%
filter(variant == "B"))
# Conversion_rate_B
conv_rate_B <- (conversions_B/visitors_B)
print(conv_rate_B) # 0.05068493
输出:
0.02773925
0.05068493
3.让我们使用转化率 A 和 B 计算相对提升。提升是增加的百分比
电阻
uplift <- (conv_rate_B - conv_rate_A) / conv_rate_A * 100
uplift # 82.72%
输出:
82.72%
B 比 A 好 83%。这足以决定获胜者。
4. 让我们计算变体 A 和 B 的合并概率、标准误差、误差幅度和比例差异(点估计)
电阻
# Pooled sample proportion for variants A & B
p_pool <- (conversions_A + conversions_B) / (visitors_A +
visitors_B)
print(p_pool) # 0.03928325
# Let's compute Standard error for variants A & B (SE_pool)
SE_pool <- sqrt(p_pool * (1 - p_pool) * ((1 / visitors_A) +
(1 / visitors_B)))
print(SE_pool) # 0.01020014
# Let's compute the margin of error for the pool
MOE <- SE_pool * qnorm(0.975)
print(MOE) # 0.0199919
# Point Estimate or Difference in proportion
d_hat <- conv_rate_B - conv_rate_A
输出:
0.03928325
0.01020014
0.0199919
5. 让我们计算 z-score
电阻
# Compute the Z-score so we
# can determine the p-value
z_score <- d_hat / SE_pool
print(z_score) # 2.249546
输出:
2.249546
6. 使用这个 z-score,我们可以通过查找表或使用以下代码快速确定 p 值:
电阻
# Let's compute p_value
# using the z_score value
p_value <- pnorm(q = -z_score,
mean = 0,
sd = 1) * 2
print(p_value) # 0.02447777
输出:
0.02447777
7. 让我们计算池的置信区间
电阻
# Let's compute Confidence interval for the
# pool using pre-calculated results
ci <- c(d_hat - MOE, d_hat + MOE)
ci # 0.002953777 0.042937584
# Using same steps as already shown,
# let's compute the confidence
# interval for variants A separately
X_hat_A <- conversions_A / visitors_A
se_hat_A <- sqrt(X_hat_A * (1 - X_hat_A) / visitors_A)
ci_A <- c(X_hat_A - qnorm(0.975) * se_hat_A, X_hat_A
+ qnorm(0.975) * se_hat_A)
print(ci_A) # 0.01575201 0.03972649
# Using same steps as already shown,
# let's compute the confidence
# interval for variants B separately
X_hat_B <- conversions_B / visitors_B
se_hat_B <- sqrt(X_hat_B * (1 - X_hat_B) / visitors_B)
ci_B <- c(X_hat_B - qnorm(0.975) * se_hat_B,
X_hat_B + qnorm(0.975) * se_hat_B)
print(ci_B) # 0.03477269 0.06659717
输出:
0.002953777 0.042937584
0.01575201 0.03972649
0.03477269 0.06659717
8. 让我们在数据框(表)中可视化到目前为止计算的结果:
电阻
vis_result_pool <- data.frame(
metric = c(
'Estimated Difference',
'Relative Uplift(%)',
'pooled sample proportion',
'Standard Error of Difference',
'z_score',
'p-value',
'Margin of Error',
'CI-lower',
'CI-upper'),
value = c(
conv_rate_B - conv_rate_A,
uplift,
p_pool,
SE_pool,
z_score,
p_value,
MOE,
ci_lower,
ci_upper
))
vis_result_pool
输出:
metric value
1 Estimated Difference 0.02294568
2 Relative Uplift(%) 82.71917808
3 pooled sample proportion 0.03928325
4 Standard Error of Difference 0.01020014
5 z_score 2.24954609
6 p-value 0.02447777
7 Margin of Error 0.01999190
8 CI-lower 0.00000000
9 CI-upper 0.04589136
建议与结论
- 变体 A 有 20 次转化和 721 次点击,而变体 B 有 37 次转化和 730 次点击。
- 基于变体 A 转换率的 82.72% 的相对提升为 2.77%,B 为 5.07%。因此,变体 B 比 A 好 82.72%。
- 对于该分析,计算的 P 值为 0.02448。因此,测试结果具有很强的统计意义。
- 从上述结果可以看出具有很强的统计意义。您应该拒绝零假设并继续启动。
- 因此,接受变体 B,您可以将其 100% 滚动给用户。
如果您想了解完整的分析和数据集详细信息,请单击此 Github 链接。
限制
它是转换优化的工具之一,它不是一个独立的解决方案,它不会解决我们所有的转换问题,也无法解决您处理杂乱数据时遇到的问题,您需要执行的不仅仅是一个A/B 测试以提高转化率。