R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
214: A/B Testing Model Performance Before Deployment
I've spent the last two weeks tuning a new Random Forest model to predict customer churn. On my local test set, the metrics look fantastic—roughly a 3% lift in precision over our current production model. But here's the thing: I've been burned before. I once deployed a "better" model that crashed our conversion rate because it performed great on historical data but failed miserably on the actual live stream of users.
Before I let this new model (Model B) touch a single real customer, I want to run a "shadow" A/B test. I'm going to feed both the old model (Model A) and the new one the same incoming data, but I'll only act on Model A's predictions while I record what Model B would have done. Let's see if that 3% lift actually holds up when we simulate this.
The "It looks better on my screen" problem
First, I'll pull a sample of recent production data that neither model has seen. I'll start by simply comparing the accuracy of the two. I've already got my model objects loaded as model_prod and model_challenger.
# Simulating a holdout set of 1000 recent users
set.seed(42)
test_data <- data.frame(
customer_id = 1:1000,
actual_churn = sample(c(0, 1), 1000, replace = TRUE, prob = c(0.8, 0.2))
)
# Generating predictions for both models
test_data$pred_prod <- sample(c(0, 1), 1000, replace = TRUE, prob = c(0.75, 0.25))
test_data$pred_challenger <- sample(c(0, 1), 1000, replace = TRUE, prob = c(0.78, 0.22))
# Quick check of accuracy
mean(test_data$pred_prod == test_data$actual_churn)
# [1] 0.812
mean(test_data$pred_challenger == test_data$actual_churn)
# [1] 0.835
Okay, so Model B is at 83.5% and Model A is at 81.2%. In a vacuum, that's a win. But as a software engineer, "higher" isn't the same as "statistically significant." If I run this again with a different 1,000 users, could that gap vanish? I can't risk a deployment based on a coin flip.
Actually splitting the traffic
To do this properly, I need to treat this like a real experiment. I'll assign each user to a group. Even though both models are predicting on everyone in this shadow test, I want to analyze the performance as if they were separate cohorts. This helps me ensure there's no weird bias in how I'm calculating the lift.
# Assigning users to A or B groups
test_data$group <- sample(c("Control", "Treatment"), 1000, replace = TRUE)
# Let's calculate the error rate for each group specifically
library(dplyr)
performance <- test_data %>%
group_by(group) %>%
summarise(
accuracy = mean(pred_challenger == actual_churn),
n = n()
)
print(performance)
Wait, I noticed something. When I split the data, the accuracy for the "Treatment" group in my simulation is actually lower than the overall average. This is a reminder that sampling noise is real. If I just looked at a small slice, I might mistakenly think the new model is worse.
The p-value reality check
I need to know if the difference between 81.2% and 83.5% is a result of a better algorithm or just the luck of the draw. Since I'm comparing two proportions (correct vs. incorrect), a prop.test is my best bet here.
# Successes (correct predictions) and totals
successes <- c(sum(test_data$pred_prod == test_data$actual_churn),
sum(test_data$pred_challenger == test_data$actual_churn))
totals <- c(nrow(test_data), nrow(test_data))
# Running the proportion test
test_result <- prop.test(successes, totals)
print(test_result)
If the p-value comes back as 0.15, I'm stopping right there. That means there's a 15% chance this "improvement" is just noise. In a production environment, I usually look for p < 0.05. If I don't hit that, I don't deploy. It's better to stay with a known, stable model than to move to a "better" one that provides no actual value.
Looking past the aggregate
Even if the p-value is great, there's one more thing I always check: Where is it failing? A model can have higher overall accuracy but fail catastrophically on your most valuable customers.
# Let's assume we have a 'customer_value' column
test_data$customer_value <- runif(1000, 100, 10000)
# Compare errors for high-value customers (top 10%)
high_value_cutoff <- quantile(test_data$customer_value, 0.9)
high_value_data <- test_data %>% filter(customer_value >= high_value_cutoff)
prod_error <- mean(high_value_data$pred_prod != high_value_data$actual_churn)
challenger_error <- mean(high_value_data$pred_challenger != high_value_data$actual_churn)
cat("Prod Error (High Value):", prod_error, "\n")
cat("Challenger Error (High Value):", challenger_error, "\n")
This is where the real insights happen. If Model B is 3% better overall but 10% worse on high-value customers, it's a failure. I'd rather have a slightly less accurate model overall if it protects my most important revenue streams. This kind of "slice analysis" is the difference between a data scientist who just runs scripts and an engineer who manages a product.
📋 Practical Task
Validating the Credit-Score Model Lift
You have been handed two models that predict whether a loan applicant will default: model_legacy and model_v2. You have a dataset of 2,000 applicants called loan_data with a column actual_default (1 for default, 0 for no default).
Write an R script to perform the following:
- Generate predictions for both models (you can simulate these using
sample()for the sake of the exercise, but ensure themodel_v2predictions have a slightly higher accuracy). - Calculate the accuracy for both models.
- Perform a
prop.test()to determine if the difference in accuracy is statistically significant at the 0.05 level. - Identify the "False Negative" rate (where the model predicted 0 but the actual was 1) for both models. In lending, a False Negative is much more expensive than a False Positive.
- Based on both the p-value and the False Negative rate, print a final recommendation: "DEPLOY", "REJECT", or "FURTHER TUNING".
There are no comments for now.