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
221: Sports Data Visualization Techniques
When I first started working with sports analytics, I fell into a trap that almost every beginner does: I thought that "more data on the screen equals more insight." I'd take a season's worth of NBA shot coordinates and dump every single point onto a plot of the court. I thought I was providing a comprehensive view of a player's game. In reality, I was just creating a "blob" of overlapping dots that told the coach absolutely nothing.
The "More Dots Equal More Insight" Fallacy
The misconception is that a raw scatter plot of event coordinates—whether it's a shot chart in basketball or a pass map in soccer—is the gold standard for sports visualization. It feels honest because you're showing every data point. But here is why that's wrong: sports data is incredibly dense. When you plot 500 shots on a 50x47 foot court, the points overlap. You can't tell if a cluster of dots represents ten shots or a hundred. You lose the density, which is actually the piece of information you're looking for.
# The WRONG way: Just dumping points
# This creates a messy 'blob' where you can't see density
ggplot(nba_shots, aes(x = x_coord, y = y_coord)) +
geom_point(color = "blue") +
coord_fixed()
If you do this, you're forcing the viewer to guess. In a high-pressure environment like a front office or a coaching staff, "guessing" is a failure of the visualization.
Turning Noise into Spatial Intelligence
To fix this, we need to stop thinking about points and start thinking about probability zones. Instead of plotting every shot, we use hexbinning or 2D density plots. This transforms the visualization from a list of events into a heatmap of tendencies. I personally prefer stat_summary_2d because it allows us to calculate the actual field goal percentage within a specific grid square, rather than just counting how many shots were taken there.
Another critical detail I've learned the hard way: always use coord_fixed(). If you don't, R will stretch your court to fit the window, and your "corner three" will suddenly look like it's being shot from the parking lot. It's a small line of code, but it's the difference between a professional tool and a toy.
# The RIGHT way: Using binned summaries for efficiency
ggplot(nba_shots, aes(x = x_coord, y = y_coord, z = shot_made_indicator)) +
stat_summary_2d(fun = mean, bins = 20) +
scale_fill_gradient(low = "red", high = "green") +
coord_fixed() +
labs(title = "Player Shot Efficiency Map", fill = "FG%")
By mapping the z aesthetic to the outcome (1 for make, 0 for miss) and using mean as the function, the color now represents the actual percentage of shots made in that area. Now, the coach can see at a glance that the player is lethal from the left elbow but struggles in the paint, regardless of how many shots were taken.
Layering Contextual Overlays
A heatmap is great, but it's still just math on a blank canvas. To make it "sports data," you need to overlay the geometry of the game. I usually do this by creating a separate dataframe for the court lines. Instead of searching for a complex library, just define the lines as segments. It keeps your code portable and your render times fast.
- The Baseline: A simple segment from (-25, -4) to (25, -4).
- The Sidelines: Segments connecting the baselines.
- The Key: A series of rectangles or lines to define the restricted area.
When you layer these geom_segment calls on top of your density plot, the data suddenly gains a physical home. You aren't just looking at a coordinate system; you're looking at a basketball court.
📋 Practical Task
Build a High-Value Target Map for a Quarterback
You have been provided with a dataset qb_passes containing the following columns: target_x, target_y (coordinates on the field), and completion_success (1 for complete, 0 for incomplete).
Your task is to create a visualization that reveals the QB's "hot zones." Do not use a simple scatter plot of points. Instead, implement the following:
- Use
stat_summary_2dto create a heatmap where the color represents the completion percentage of passes in that area. - Ensure the color scale ranges from a "cold" color (e.g., "steelblue") for low percentage to a "hot" color (e.g., "orange") for high percentage.
- Force the aspect ratio to be equal using
coord_fixed()so the football field maintains its real-world proportions. - Add a
geom_hlineaty = 0to represent the line of scrimmage.
Your final plot should allow a coach to identify exactly where the QB is most efficient at completing passes, without the visual clutter of overlapping individual pass targets.
There are no comments for now.