[Video]
The “hypothesis” for an A/B testing experiment refers to?
# Load tidyverse
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.0 ✓ purrr 0.3.4
## ✓ tibble 3.0.1 ✓ dplyr 0.8.5
## ✓ tidyr 1.1.0 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
# Read in data
click_data <- read_csv("click_data.csv")
## Parsed with column specification:
## cols(
## visit_date = col_date(format = ""),
## clicked_adopt_today = col_double()
## )
click_data
## # A tibble: 3,650 x 2
## visit_date clicked_adopt_today
## <date> <dbl>
## 1 2017-01-01 1
## 2 2017-01-02 1
## 3 2017-01-03 0
## 4 2017-01-04 1
## 5 2017-01-05 1
## 6 2017-01-06 0
## 7 2017-01-07 0
## 8 2017-01-08 0
## 9 2017-01-09 0
## 10 2017-01-10 0
## # … with 3,640 more rows
# Find oldest and most recent date
min(click_data$visit_date)
## [1] "2017-01-01"
max(click_data$visit_date)
## [1] "2017-12-31"
[Video]
# Read in the data
click_data <- read_csv("click_data.csv")
## Parsed with column specification:
## cols(
## visit_date = col_date(format = ""),
## clicked_adopt_today = col_double()
## )
# Calculate the mean conversion rate by day of the week
click_data %>%
group_by(wday(visit_date)) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
## # A tibble: 7 x 2
## `wday(visit_date)` conversion_rate
## <dbl> <dbl>
## 1 1 0.3
## 2 2 0.277
## 3 3 0.271
## 4 4 0.298
## 5 5 0.271
## 6 6 0.267
## 7 7 0.256
# Read in the data
click_data <- read_csv("click_data.csv")
## Parsed with column specification:
## cols(
## visit_date = col_date(format = ""),
## clicked_adopt_today = col_double()
## )
# Calculate the mean conversion rate by week of the year
click_data %>%
group_by(week(visit_date)) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
## # A tibble: 53 x 2
## `week(visit_date)` conversion_rate
## <dbl> <dbl>
## 1 1 0.229
## 2 2 0.243
## 3 3 0.171
## 4 4 0.129
## 5 5 0.157
## 6 6 0.186
## 7 7 0.257
## 8 8 0.171
## 9 9 0.186
## 10 10 0.2
## # … with 43 more rows
# Compute conversion rate by week of the year
click_data_sum <- click_data %>%
group_by(week(visit_date)) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
# Build plot
ggplot(click_data_sum, aes(x = `week(visit_date)`,
y = conversion_rate)) +
geom_point() +
geom_line() +
scale_y_continuous(limits = c(0, 1),
labels = percent)
[Video]
You’re designing a new experiment and you have two conditions. What’s the best method for comparing your two conditions?
Let’s take a moment to learn more about the SSizeLogisticBin()
function from powerMediation
that was introduced in the slides. Look at the documentation page for the SSizeLogisticBin()
function by calling help(SSizeLogisticBin)
. The powerMediation package is pre-loaded for you. What is another way to phrase what p2
signifies?
X = 0
(the control condition).X = 1
(the test condition).X = 1
.# Load powerMediation
library(powerMediation)
# Compute and look at sample size for experiment in August
total_sample_size <- SSizeLogisticBin(p1 = 0.54,
p2 = 0.64,
B = 0.5,
alpha = 0.05,
power = 0.8)
total_sample_size
## [1] 758
# Load powerMediation
library(powerMediation)
# Compute and look at sample size for experiment in August with a 5 percentage point increase
total_sample_size <- SSizeLogisticBin(p1 = 0.54,
p2 = 0.59,
B = 0.5,
alpha = 0.05,
power = 0.8)
total_sample_size
## [1] 3085
[Video]
# Group and summarize data
experiment_data_clean_sum <- experiment_data_clean %>%
group_by(visit_date, condition) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
# Make plot of conversion rates over time
ggplot(experiment_data_clean_sum,
aes(x = visit_date,
y = conversion_rate,
color = condition,
group = condition)) +
geom_point() +
geom_line()
To analyze our results, we used the function glm()
and set family
to binomial
. Take a look at the documentation to using ?glm
to see what exactly the family
argument is.
# Load package for cleaning model results
library(broom)
# View summary of results
experiment_data_clean %>%
group_by(condition) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
## # A tibble: 2 x 2
## condition conversion_rate
## <fct> <dbl>
## 1 control 0.166
## 2 test 0.386
# Run logistic regression
experiment_results <- glm(clicked_adopt_today ~ condition,
family = "binomial",
data = experiment_data_clean) %>%
tidy()
experiment_results
## # A tibble: 2 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) -1.61 0.160 -10.1 5.38e-24
## 2 conditiontest 1.15 0.201 5.72 1.04e- 8
[Video]
You’re now designing your follow-up experiments. What’s the best path forward?
# Load package for running power analysis
library(powerMediation)
# Run logistic regression power analysis
total_sample_size <- SSizeLogisticBin(p1 = 0.39,
p2 = 0.59,
B = 0.5,
alpha = 0.05,
power = 0.8)
total_sample_size
## [1] 194
# Read in data for follow-up experiment
followup_experiment_data <- read_csv("followup_experiment_data.csv")
## Warning: Missing column names filled in: 'X1' [1]
## Parsed with column specification:
## cols(
## X1 = col_double(),
## visit_date = col_date(format = ""),
## condition = col_character(),
## clicked_adopt_today = col_double()
## )
# View conversion rates by condition
followup_experiment_data %>%
group_by(condition) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
## # A tibble: 2 x 2
## condition conversion_rate
## <chr> <dbl>
## 1 cat_hat 0.814
## 2 kitten_hat 0.876
# Run logistic regression
followup_experiment_results <- glm(clicked_adopt_today ~ condition,
family = "binomial",
data = followup_experiment_data) %>%
tidy()
followup_experiment_results
## # A tibble: 2 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 1.48 0.261 5.66 0.0000000149
## 2 conditionkitten_hat 0.479 0.404 1.18 0.236
[Video]
# Compute monthly summary
eight_month_checkin_data_sum <- eight_month_checkin_data %>%
mutate(month_text = month(visit_date, label = TRUE)) %>%
group_by(month_text, condition) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
## Warning: Factor `month_text` contains implicit NA, consider using
## `forcats::fct_explicit_na`
# Plot month-over-month results
ggplot(eight_month_checkin_data_sum,
aes(x = month_text,
y = conversion_rate,
color = condition,
group = condition)) +
geom_point() +
geom_line()
# Plot monthly summary
ggplot(eight_month_checkin_data_sum,
aes(x = month_text,
y = conversion_rate,
color = condition,
group = condition)) +
geom_point() +
geom_line() +
scale_y_continuous(limits = c(0, 1),
labels = percent) +
labs(x = "Month",
y = "Conversion Rate")
# Plot monthly summary
ggplot(eight_month_checkin_data_sum,
aes(x = month_text,
y = conversion_rate,
color = condition,
group = condition)) +
geom_point(size = 4) +
geom_line(lwd = 1) +
scale_y_continuous(limits = c(0, 1),
labels = percent) +
labs(x = "Month",
y = "Conversion Rate")
[Video]
# Compute difference over time
no_hat_data_diff <- no_hat_data_sum %>%
spread(year, conversion_rate) %>%
mutate(year_diff = `2018` - `2017`)
no_hat_data_diff
## month 2017 2018 year_diff
## 1 Apr 0.1433333 0.1366667 -0.006666666
## 2 Aug 0.5064516 0.5806452 0.074193548
## 3 Dec 0.4451613 NA NA
## 4 Feb 0.1678571 0.2250000 0.057142857
## 5 Jan 0.1774194 0.1645161 -0.012903226
## 6 Jul 0.3903226 0.3451613 -0.045161291
## 7 Jun 0.2900000 0.3066667 0.016666667
## 8 Mar 0.1290323 0.1354839 0.006451613
## 9 May 0.2516129 0.2677419 0.016129032
## 10 Nov 0.2300000 NA NA
## 11 Oct 0.2000000 NA NA
## 12 Sep 0.2966667 NA NA
# Compute summary statistics
mean(no_hat_data_diff$year_diff, na.rm = TRUE)
## [1] 0.01323157
sd(no_hat_data_diff$year_diff, na.rm = TRUE)
## [1] 0.03817146
# Load package for power analysis
library(powerMediation)
# Run power analysis for logistic regression
total_sample_size <- SSizeLogisticBin(p1 = 0.49,
p2 = 0.64,
B = 0.5,
alpha = 0.05,
power = 0.8)
total_sample_size
## [1] 341
# Load package to clean up model outputs
library(broom)
# View summary of data
followup_experiment_data_sep %>%
group_by(condition) %>%
summarize(conversion_rate = mean(clicked_adopt_today))
## # A tibble: 2 x 2
## condition conversion_rate
## <fct> <dbl>
## 1 cat_hat 0.468
## 2 kitten_hat 0.614
# Run logistic regression
followup_experiment_sep_results <- glm(clicked_adopt_today ~ condition,
family = "binomial",
data = followup_experiment_data_sep) %>%
tidy()
followup_experiment_sep_results
## # A tibble: 2 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) -0.129 0.153 -0.841 0.401
## 2 conditionkitten_hat 0.593 0.219 2.70 0.00688
Michael is a hybrid thinker and doer—a byproduct of being a StrengthsFinder “Learner” over time. With 20+ years of engineering, design, and product experience, he helps organizations identify market needs, mobilize internal and external resources, and deliver delightful digital customer experiences that align with business goals. He has been entrusted with problem-solving for brands—ranging from Fortune 500 companies to early-stage startups to not-for-profit organizations.
Michael earned his BS in Computer Science from New York Institute of Technology and his MBA from the University of Maryland, College Park. He is also a candidate to receive his MS in Applied Analytics from Columbia University.
LinkedIn | Twitter | www.michaelmallari.com/data | www.columbia.edu/~mm5470