Load Packages

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.4.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(nflfastR)
## Warning: package 'nflfastR' was built under R version 4.4.3
library(formattable)
## Warning: package 'formattable' was built under R version 4.4.3
library(circlize)
## Warning: package 'circlize' was built under R version 4.4.3
## ========================================
## circlize version 0.4.16
## CRAN page: https://cran.r-project.org/package=circlize
## Github page: https://github.com/jokergoo/circlize
## Documentation: https://jokergoo.github.io/circlize_book/book/
## 
## If you use it in published research, please cite:
## Gu, Z. circlize implements and enhances circular visualization
##   in R. Bioinformatics 2014.
## 
## This message can be suppressed by:
##   suppressPackageStartupMessages(library(circlize))
## ========================================
library(DT)
## Warning: package 'DT' was built under R version 4.4.3

Load Play-By-Play

pbp <- load_pbp(2025)
get_team_data <- function(team_input, pbp) {
  # Get offensive rush epa
  off_rush <- pbp %>% 
    filter(posteam == team_input, rush == 1) %>%
    group_by(posteam) %>%
    summarize(o_r_epa = round(mean(epa, na.rm = TRUE), 3)) %>%
    rename(team = posteam)
  
  # Get offensive pass epa
  off_pass <- pbp %>% 
    filter(posteam == team_input, pass == 1) %>%
    group_by(posteam) %>%
    summarize(o_p_epa = round(mean(epa, na.rm = TRUE), 3)) %>%
    select(o_p_epa)
  
  # Get defensive rush epa
  def_rush <- pbp %>% 
    filter(defteam == team_input, rush == 1) %>%
    group_by(defteam) %>%
    summarize(d_r_epa = round(mean(epa, na.rm = TRUE), 3)) %>%
    select(d_r_epa)
  
  # Get defensive pass epa
  def_pass <- pbp %>% 
    filter(defteam == team_input, pass == 1) %>%
    group_by(defteam) %>%
    summarize(d_p_epa = round(mean(epa, na.rm = TRUE), 3)) %>%
    select(d_p_epa)
  
  result <- cbind(off_rush, off_pass, def_rush, def_pass)
  return(result)
}
teams <- unique(na.omit(pbp$posteam))

team_results <- tibble()
for (i in teams) {
  team_results <- bind_rows(team_results, get_team_data(i, pbp))
}

ranks <- team_results %>%
  arrange(-o_r_epa) %>%
  mutate(o_r_rank = row_number()) %>%
  arrange(-o_p_epa) %>%
  mutate(o_p_rank = row_number()) %>%
  arrange(d_r_epa) %>%
  mutate(d_r_rank = row_number()) %>%
  arrange(d_p_epa) %>%
  mutate(d_p_rank = row_number(), 
    avg_rank = round((o_r_rank+o_p_rank+d_r_rank+d_p_rank) / 4, 1)) %>%
  arrange(avg_rank)
  
datatable(ranks)
pbp2024 <- load_pbp(2024)

teams <- unique(na.omit(pbp$posteam))

team_results_2024 <- tibble()
for (i in teams) {
  team_results_2024 <- bind_rows(team_results_2024, get_team_data(i, pbp2024))
}

ranks_2024 <- team_results_2024 %>%
  arrange(-o_r_epa) %>%
  mutate(o_r_rank = row_number()) %>%
  arrange(-o_p_epa) %>%
  mutate(o_p_rank = row_number()) %>%
  arrange(d_r_epa) %>%
  mutate(d_r_rank = row_number()) %>%
  arrange(d_p_epa) %>%
  mutate(d_p_rank = row_number(), 
    avg_rank = round((o_r_rank+o_p_rank+d_r_rank+d_p_rank) / 4, 1)) %>%
  arrange(avg_rank)
  
datatable(ranks_2024)