setwd("C:/Users/pptallon/Dropbox/G/Teaching/Loyola College/IS 470 Sports Analytics/Fall 2026/")
source("https://raw.githubusercontent.com/ptallon/SportsAnalytics_Fall2026/refs/heads/main/SharedCode.R")
load_packages(c("data.table", "dplyr", "ggplot2", "tidytext", "scales"))
y1 <- fread("NFLBDB2022/tracking2018.csv")
y2 <- fread("NFLBDB2022/tracking2019.csv")
y3 <- fread("NFLBDB2022/tracking2020.csv")
trellis_df <- rbind(y1, y2, y3)
rm(y1, y2, y3)
plays <- fread("NFLBDB2022/plays.csv")
players <- fread("NFLBDB2022/players.csv")
games <- fread("NFLBDB2022/games.csv")
plays_df <- left_join(plays, players, by = c("kickerId" = "nflId"))
plays_df <- left_join(plays_df, games, by = c("gameId"))
kicker_df <- plays_df |>
select(displayName, season, kickLength,
specialTeamsPlayType, specialTeamsResult) |>
filter(specialTeamsPlayType == "Field Goal",
specialTeamsResult != "Non-Special Teams Result") |>
group_by(displayName, season) |>
summarise(Attempts = n(),
Kicks_made = sum(specialTeamsResult == "Kick Attempt Good"),
Kicks_missed = sum(specialTeamsResult == "Kick Attempt No Good"),
Accuracy = percent(Kicks_made / Attempts, accuracy = 0.1),
#Min_length = min(kickLength, na.rm = T),
.groups = "keep") |>
group_by(season) |>
mutate(year_rank = min_rank(desc(Accuracy))) %>%
ungroup() |>
group_by(displayName) |>
mutate(all_seasons = percent(sum(Kicks_made) / sum(Attempts), accuracy = 0.1),
seasons_played = n()) |>
ungroup() |>
filter(seasons_played == 3) |>
mutate(Rank = dense_rank(desc(all_seasons))) |>
arrange(Rank, season) |>
data.frame()
kicker_df$Accuracy <- as.numeric(sub("%", "", kicker_df$Accuracy))
ggplot(kicker_df |> filter(Rank <= 10) ,
aes(x = displayName, y = Accuracy, fill = factor(season))) +
geom_col(position = "dodge") +
geom_text(aes(label = percent(Accuracy/100, accuracy = 1)),
position = position_dodge(width=0.9),
vjust = -0.4,
hjust = 0.5,
size = 2.3
) +
scale_x_discrete(labels = function(x) gsub(" ", "\n", x) ) +
labs( x = "Kicker Name",
y = "Accuracy (Good Kicks as a Percentage of All Kicks)",
fill = "Season",
title = "Top 10 NFL Kickers in 2018-2020"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))

# Keep top 10 kickers from each season
top10 <- kicker_df %>%
filter(Attempts >= mean(Attempts)) %>%
group_by(season) %>%
mutate(season_rank = min_rank(desc(Accuracy))) %>%
ungroup() %>%
filter(season_rank <= 10) %>%
data.frame()
ggplot(top10,
aes(x = reorder_within(displayName, Accuracy, season),
y = Accuracy,
fill = Kicks_made)) +
geom_col() +
coord_flip() +
facet_wrap(~ season, scales = "free_y") +
scale_x_reordered() +
scale_y_continuous(
limits = c(0, 110),
breaks = seq(0, 100, by = 20),
labels = scales::label_number(suffix = "%")
) +
scale_fill_gradient2(
low = "darkred",
mid = "lightyellow",
high = "darkgreen",
midpoint = mean(top10$Kicks_made),
name = "Kicks Made"
) +
geom_text(
aes(label = percent(Accuracy*0.01, accuracy = 1)),
color = "black",
hjust = -0.2,
size = 2.0
) +
geom_text(
aes(label = paste("Kicks made: ",Kicks_made,"of",Attempts), y = 30),
color = "black",
hjust = 0.5,
size = 2.0
) +
labs(
title = "Top 10 Kickers by Accuracy by Season (Ranked by Annual Accuracy)",
subtitle = "(Based on kickers with at least the average number of attempts over all years)",
x = "Kicker",
y = "Accuracy",
fill = "Kicks Made"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, face="bold"),
plot.subtitle = element_text(
size = 9,
face = "plain",
hjust = 0.5
),
axis.text.x = element_text(size = 8))
