Skip to content

Instantly share code, notes, and snippets.

@gallochris
Created February 27, 2024 17:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gallochris/d3e8e65809b4e87bc7a05b97e05d27d2 to your computer and use it in GitHub Desktop.
Save gallochris/d3e8e65809b4e87bc7a05b97e05d27d2 to your computer and use it in GitHub Desktop.
Possession plot for basketball
# use bigballR to fetch schedule
schedule <-
bigballR::get_team_schedule(season = "2023-24", team.name = "North Carolina")
# grab miami home game pbp
unc_miami_pbp <- bigballR::get_play_by_play(schedule$Game_ID[28])
# add index to determine first half and second half
um_index <- which(unc_miami_pbp$Half_Status == 2)[1]
# denote when the half changes
# could also maybe do this smarter?
# divide up by every 10/20 possessions?
first_half_poss <- unc_miami_pbp$Poss_Num[um_index] / 2
# manipulate the data for a viz
canes_heels <- unc_miami_pbp |>
dplyr::mutate(
scoring = dplyr::case_match(Event_Result,
"made" ~ TRUE,
"missed" ~ FALSE,
NA ~ FALSE),
pts_value = dplyr::case_when(
Event_Type == "Turnover" ~ -1,
Event_Result == "missed" & Shot_Value %in% c(1, 2, 3) ~ 0,
Event_Result == "made" & Shot_Value == 1 ~ 1,
Event_Result == "made" & Shot_Value == 2 ~ 2,
Event_Result == "made" & Shot_Value == 3 ~ 3,
.default = 0
),
pts_shape = dplyr::case_match(pts_value,
-1 ~ 4,
0 ~ 48,
1 ~ 49,
2 ~ 50,
3 ~ 51)
)
# make the plot using geom_point with different shapes
# could productionize this into a function for _any_ game
# in order to do that, would need to determine points per trip
# and any annotations separately
canes_heels_plot <- canes_heels |>
ggplot2::ggplot(ggplot2::aes(
x = Poss_Num / 2,
y = Poss_Team,
color = scoring,
size = pts_value / 2
)) +
ggplot2::geom_point(ggplot2::aes(shape = factor(pts_shape)),
position = ggplot2::position_dodge(width = 0.5)) +
ggplot2::scale_color_manual(values = c("red", "darkgreen")) +
ggplot2::scale_shape_manual(values = c(4, 45, 49, 50, 51)) +
ggplot2::scale_x_continuous(breaks = seq(0, 75, 5), limits = c(0, 75)) +
ggplot2::geom_vline(xintercept = first_half_poss,
linetype = "dashed",
color = "#333333") +
ggplot2::annotate(
"rect",
fill = "yellow",
alpha = 0.1,
xmin = 126.5 / 2,
xmax = 144.5 / 2,
ymin = -Inf,
ymax = Inf
) +
ggthemes::theme_fivethirtyeight() +
ggplot2::theme(
legend.position = "none",
axis.text.y = cbbplotR::element_cbb_teams(size = 0.9),
panel.grid.major = ggplot2::element_blank(),
panel.grid.minor = ggplot2::element_blank(),
panel.background = ggplot2::element_blank()
) +
ggplot2::labs(
x = "Possessions",
y = "",
title = "North Carolina 75, Miami 71",
subtitle = "Green represents a scoring possession: three-pointers, two-pointers, free throws \nRed represents empty possessions: missed shots (-) or turnovers (x)",
caption = "Bless your chart | February 26, 2024 | data via bigballR + cbbplotR"
) +
ggplot2::annotate(
"label",
x = 10 / 2,
y = 1.5,
label = "Points per trip \nNorth Carolina 1.01 \nMiami 0.96",
size = 3,
color = "#333333",
fill = "floralwhite",
fontface = "bold"
) +
ggplot2::annotate(
"label",
x = 135 / 2,
y = 1.5,
label = "UNC: 0 made field goals \nlast 4:17 of game",
size = 2.5,
color = "#333333",
fill = "floralwhite",
fontface = "bold"
) +
ggplot2::annotate(
"label",
x = first_poss_num,
y = 2.55,
label = "Halftime",
size = 2.5,
color = "#333333",
fill = "floralwhite",
fontface = "bold"
)
# save the plot
ggplot2::ggsave(
"unc_miami_poss_plot.png",
canes_heels_plot,
w = 10,
h = 6,
dpi = 600,
type = 'cairo'
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment