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
220: Win Probability Models
I remember the first time I tried to build a win probability model for a sports project. I had my logistic regression dialed in, my training data was clean, and I felt confident. Then I ran my predictions and saw values like -1.42 and 3.11. I spent twenty minutes scratching my head, wondering why my "probability" model was returning negative numbers and values way above 100%.
# The "What is going on?" code
game_data <- data.frame(
score_diff = c(3, -7, 10, -2, 14),
time_left = c(300, 120, 60, 600, 10),
win = c(1, 0, 1, 0, 1)
)
model <- glm(win ~ score_diff + time_left, data = game_data, family = binomial)
# Predict probability for a new game state
new_game <- data.frame(score_diff = 7, time_left = 180)
prob <- predict(model, new_game)
print(prob)
# Output: [1] 1.1245 <-- Wait, 112% probability?
The Log-Odds Trap
The issue here is one of the most common "gotchas" in R's glm (Generalized Linear Model) function. By default, predict() returns the linear predictor. In the context of a binomial model, this isn't a probability; it's the log-odds (also called the logit).
Mathematically, the model calculates a linear combination of your predictors, but it hasn't passed that result through the sigmoid function yet to squash it between 0 and 1. When you see a positive number, the model thinks the outcome is likely; a negative number means it's unlikely. But for a win probability model, "likely" isn't useful—we need a specific percentage.
Switching to Response Probabilities
To fix this, you have to explicitly tell R that you want the "response" (the probability) rather than the link function's raw output. You do this by adding type = "response" to the predict call.
# The fixed version
prob_fixed <- predict(model, new_game, type = "response")
print(prob_fixed)
# Output: [1] 0.7542 <-- Now we're talking. 75.4% chance of winning.
I usually suggest making this a habit immediately. If you're using family = binomial, you almost always want type = "response". If you forget it, you're just staring at the internal math of the model rather than the actual prediction.
Evaluating Model Calibration with Brier Scores
Once you have probabilities, you can't just use "accuracy" to see if your model is good. If a model predicts a 51% chance of winning and the team wins, it was "accurate," but it wasn't very confident. If it predicts 99% and they lose, that's a huge failure.
Instead, I use the Brier Score. It's essentially the Mean Squared Error of your probabilities. It measures the distance between the predicted probability (e.g., 0.70) and the actual outcome (1 or 0). A score of 0 is a perfect model; 0.25 is basically the same as guessing 50% every time.
# Calculating a simple Brier Score
predictions <- predict(model, game_data, type = "response")
actuals <- game_data$win
brier_score <- mean((predictions - actuals)^2)
print(brier_score)
If your Brier score is high, don't just throw more data at the model. Look at your features. Are you missing the "game state"? For example, in football, a 3-point lead with 10 seconds left is very different from a 3-point lead with 10 minutes left. The interaction between score and time is where the real signal lives.
📋 Practical Task
Building a Baseball Game-State Probability Model
You have been provided with a small sample dataset of baseball game snapshots. Your goal is to build a logistic regression model that predicts the probability of a home team win based on the run_diff (Home runs minus Away runs) and the inning.
Dataset:
baseball_data <- data.frame(
run_diff = c(2, -1, 0, 5, -3, 1, -2, 4, 0, -1),
inning = c(3, 2, 9, 7, 4, 8, 5, 6, 3, 9),
win = c(1, 0, 1, 1, 0, 1, 0, 1, 0, 0)
)
Your Task:
- Create a
glmmodel using thebinomialfamily to predictwinbased onrun_diffandinning. - Create a new data frame called
test_gamerepresenting a game in the 5th inning where the home team is down by 2 runs (run_diff = -2). - Predict the win probability for this
test_game. Ensure your output is a probability between 0 and 1. - Calculate the Brier Score for the model using the original
baseball_datato determine how well the model fits the training set.
There are no comments for now.