R package implementing a model-based approach using Gaussian mixtures for bounded data to estimate and visualize basketball shot charts.
The method is described in the paper:
Scrucca L., Karlis D. (2024) A model-based approach to shot charts estimation in basketball. Under review. arXiv pre-print available at https://arxiv.org/abs/2405.01182
You can install the development version of the package using the following code:
-
Install/update
devtools
package to the latest versioninstall.packages("devtools")
-
Install/update
GMMBasketShotCharts
packagedevtools::install_github("luca-scr/GMMBasketShotCharts")
Code to reproduce the analyses presented in Scrucca and Karlis (2024):
library(GMMBasketShotCharts)
plot_basket_court()
plot_basket_court_offensive_areas()
# Stephen Curry -------------------------------------------------------
data(stephen_curry, package = "GMMBasketShotCharts")
data = stephen_curry$data[, .(Shot, x, y)]
dens1 = gmm_basket_shot_chart(data[Shot == "made",])
summary(dens1)
plot(dens1, prob = c(0.1, 0.25, 0.5, 0.75, 0.9), palette = "OrRd") +
geom_point(data = as.data.table(dens1$data),
aes(x = x, y = y),
pch = 1, col = "firebrick4")
dens0 = gmm_basket_shot_chart(data[Shot == "missed",])
summary(dens0)
plot(dens0, prob = c(0.1, 0.25, 0.5, 0.75, 0.9), palette = "PuBu") +
geom_point(data = as.data.table(dens1$data),
aes(x = x, y = y),
pch = 1, col = "dodgerblue4")
pred = gmm_basket_shot_chart_predict(dens1, dens0, newdata = data[,.(x,y)])
data[, Probs := pred$Probs]
data[, ExpPoints := pred$ExpPoints]
cols = tableau_color_pal(palette = "Red-Blue Diverging",
type = "ordered-diverging",
direction = -1)(7)
ggplot(data) +
plot_basket_court(theme = "light", add = TRUE) +
stat_summary_hex(aes(x = x, y = y, z = Probs),
binwidth = 1) +
scale_fill_gradientn(name = NULL,
colors = cols,
guide = "colorbar",
values = scales::rescale(c(0,0.1,0.2,0.3,0.35,0.4,0.5,1),
from = c(0,1)),
limits = c(0,1)) +
labs(title = "Shot chart scoring probability")
cols2 = tableau_color_pal(palette = "Green-Blue Diverging",
type = "ordered-diverging",
direction = -1)(7)
ggplot(data) +
plot_basket_court(theme = "light", add = TRUE) +
stat_summary_hex(aes(x = x, y = y, z = ExpPoints),
binwidth = 1) +
scale_fill_gradientn(name = NULL,
colors = cols2,
values = scales::rescale(c(0,0.25,0.5,1,1.25,1.5,3),
from = c(0, 3)),
limits = c(0, 3)) +
labs(title = "Shot chart expected points")
gmm_basket_shot_chart_calibration(dens1, dens0)
# Joel Embiid ---------------------------------------------------------
data(joel_embiid, package = "GMMBasketShotCharts")
data = joel_embiid$data[, .(Shot, x, y)]
dens1 = gmm_basket_shot_chart(data[Shot == "made",])
summary(dens1)
plot(dens1, prob = c(0.1, 0.25, 0.5, 0.75, 0.9), palette = "OrRd") +
geom_point(data = as.data.table(dens1$data),
aes(x = x, y = y),
pch = 1, col = "firebrick4")
dens0 = gmm_basket_shot_chart(data[Shot == "missed",])
summary(dens0)
plot(dens0, prob = c(0.1, 0.25, 0.5, 0.75, 0.9), palette = "PuBu") +
geom_point(data = as.data.table(dens1$data),
aes(x = x, y = y),
pch = 1, col = "dodgerblue4")
pred = gmm_basket_shot_chart_predict(dens1, dens0, newdata = data[,.(x,y)])
data[, Probs := pred$Probs]
data[, ExpPoints := pred$ExpPoints]
cols = tableau_color_pal(palette = "Red-Blue Diverging",
type = "ordered-diverging",
direction = -1)(7)
ggplot(data) +
plot_basket_court(theme = "light", add = TRUE) +
stat_summary_hex(aes(x = x, y = y, z = Probs),
binwidth = 1) +
scale_fill_gradientn(name = NULL,
colors = cols,
guide = "colorbar",
values = scales::rescale(c(0,0.1,0.2,0.3,0.35,0.4,0.5,1),
from = c(0,1)),
limits = c(0,1)) +
labs(title = "Shot chart scoring probability")
cols2 = tableau_color_pal(palette = "Green-Blue Diverging",
type = "ordered-diverging",
direction = -1)(7)
ggplot(data) +
plot_basket_court(theme = "light", add = TRUE) +
stat_summary_hex(aes(x = x, y = y, z = ExpPoints),
binwidth = 1) +
scale_fill_gradientn(name = NULL,
colors = cols2,
values = scales::rescale(c(0,0.25,0.5,1,1.25,1.5,3),
from = c(0, 3)),
limits = c(0, 3)) +
labs(title = "Shot chart expected points")
gmm_basket_shot_chart_calibration(dens1, dens0)