Howdy folks, today we’ll be breaking down how to cook up a heat map. This specific example looks at Florian Wirtz’ pass receptions in the 2023-24 Bundesliga season. We’ll be using StatsBomb data, which a gold mine for creating data visualisations in football, and a great place to start for any data nerd.
Enough faff, let’s get into it.
First, we need to install the necessary packages, and get these loaded into R:
install.packages("devtools")
install.packages("dplyr")
install.packages("extrafont")
install.packages("paletteer")
devtools::install_github("FCrSTATS/SBpitch") # Our Pitch
devtools::install_github("statsbomb/StatsBombR") # StatsBomb
library(dplyr)
library(extrafont)
library(showtext)
library(paletteer)
library(SBpitch)
library(StatsBombR)
The two main packages for today are the StatsBombR package (all our data), and the SBpitch package (for the football pitch, self-explanatory). We also have a couple of extra packages for our fonts, colours, and graphs.
Next, we’ll grab our data from StatsBomb, and filter for the 2023-24 Bundesliga competition. We’ll then load all the matches from the season:
comps <- FreeCompetitions() %>%
filter(competition_name == "1. Bundesliga" & season_name=="2023/2024")
matches <- FreeMatches(comps)
Now, we’ll grab all the events for these matches, matching the match events by the match IDs (horrible word choice, you are welcome). We’ll also clean the data using the handy code from StatsBomb:
data <- free_allevents(MatchesDF = matches, Parallel = T)
data <- allclean(data) # Data Cleaning
The player we are looking at today is Florian Wirtz, but you can use any player, team, or competition that you like. We are filtering for any ball receipt from Wirtz, and selecting the location data for his receptions:
wirtz <- data %>%
filter(player.name == "Florian Wirtz",
type.name == "Ball Receipt*") %>%
select(period, minute, player.name, location.x, location.y)
Finally, we’ll grab our colour palette. I’ve gone with a colour-blindness-friendly palette, because I’m great like that:
colour <- paletteer::paletteer_d("colorBlindness::LightBlue2DarkBlue10Steps", direction = 1)
We are now ready to graph. We will first bring in our football pitch, and change the background and line colour:
p <- create_Pitch(grass_colour = "grey15", background_colour = "grey15", line_colour = "white")
Wow! How exciting! Next we’ll add our density plot, and setting our location values using the StatsBomb data, and adding a fill. Feel free to play around with this:
p <- create_Pitch(grass_colour = "grey15", background_colour = "grey15", line_colour = "white")+
geom_density_2d_filled(data = wirtz, aes(x = location.x, y = location.y, fill = after_stat(level)),
alpha = 0.75,
contour_var = "ndensity",
breaks = seq(0.1, 1.0, length.out = 10))
It got ugly quick, but we’re progressing. Now, we’ll add our custom colours, and set some scale limits based on our data:
scale_x_continuous(limits = c(0, 120))+
scale_y_continuous(limits = c(0, 80))+
scale_fill_manual(values = c(colour), aesthetics = c("fill", "color"))
Getting there, now to make it pretty. We’ll remove the legend (they ain’t doing much), change the background colour, edit our title, subtitle, and caption texts, and add our titles:
theme(legend.position = "none",
plot.background = element_rect(colour = "gray15", fill = "gray15"),
plot.title = element_text(size = 18, family = "jost", face = "bold", color = "white", hjust = .5, vjust = -0.8),
plot.subtitle = element_text(size = 12, family = "jost", face = "bold", color = "white", hjust = .5, vjust = -0.99),
plot.caption = element_text(size = 8, family = "jost", color = "white", hjust = 0.94, vjust = 0.8)) +
labs(title = "Florian Wirtz Pass Receptions",
subtitle = "Bayer Leverkusen | 2023-24 Bundesliga",
caption = "Carr Analytics | thefalsenine.substack.com")
And there we go. If you are wanting to add a slightly larger margin, here you go:
p + theme(plot.margin = unit(c(0.5, 0.5, 0.5, 0.5), units = "cm"))
And voila:
Another gorgeous graph. If you have found any value from this tutorial, drop a like, a comment, or a share. If you’d like to check out any of the other tutorials and articles, consider subscribing, and I’ll catch you next time.
Cheers,
Log
Wirtz is all over my feed these days. But love a good and simple R tutorial. Great piece, Logan 🙏