Packages Needed

library(tidyverse)
## ── 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(cfbfastR)
library(DT)
## Warning: package 'DT' was built under R version 4.4.3

Load Play-By-Play Data

pbp_2024 <- load_cfb_pbp(seasons = 2024)

Get Players and Mean EPA

qb_players <- c("Cam Ward", "Shedeur Sanders", "Jalen Milroe", "Will Howard", "Riley Leonard", "Jaxson Dart", "Dillon Gabriel", "Quinn Ewers", "Kyle McCord", "Tyler Shough", "Kurtis Rourke", "Brady Cook", "Max Brosmer", "Graham Mertz", "Seth Henigan", "Will Rogers", "Donovan Smith", "KJ Jefferson", "DJ Uiagalelei")

passer_summary <- pbp_2024 %>%
  filter(pass == 1) %>%
  group_by(passer_player_name) %>%
  summarize(total_plays = n(), mean_epa = mean(EPA, na.rm = TRUE)) %>%
  filter(!is.na(passer_player_name), passer_player_name %in% qb_players) %>%
  rename(player = passer_player_name) %>%
  arrange(-mean_epa)

datatable(passer_summary)

Read CSV File

QB_Stats <- read.csv("QB_Rankings.csv") %>%
  arrange(rank) %>%
  mutate(pos_rank_bef = row_number())
datatable(QB_Stats)

Combine Datasets

combined_qb <- QB_Stats %>%
  left_join(passer_summary, by = "player")
datatable(combined_qb)

Get Mean Values

mean(combined_qb$pass_grade)
## [1] 79.32105
mean(combined_qb$pass_rtg)
## [1] 102.0895
mean(combined_qb$towp)
## [1] 2.773684
mean(combined_qb$btt)
## [1] 4.573684
mean(combined_qb$mean_epa)
## [1] 0.1448856

Create Values Function

get_qb_values <- function(input_df) {
  df_qb_copy <- input_df %>% mutate(
    pass_grade = round(pmax(pmin((pass_grade-70) / 2, 10), 0), 2), # 70-90, mean 80
    pass_rtg = round(pmax(pmin((pass_rtg-90) / 3, 10), 0), 2), # 90-120, mean 105
    towp = round(pmax(pmin(((100-towp)-96.5) * 5, 10), 0), 2), # 3.5-1.5, mean 2.5
    btt = round(pmax(pmin((btt-3) * 3.33, 10), 0), 2), # 3-6, mean 4.5
    mean_epa = round(pmax(pmin((mean_epa) * 33.33, 10), 0), 2), # 0-.3, mean .15
  )
  return(df_qb_copy)
}

Create Final Dataset

new_stats_qb <- get_qb_values(combined_qb) %>% 
  mutate(total = rowSums(select(., -player, -adp, -total_plays, -rank, -pos_rank_bef, -team))) %>% 
  arrange(-total) %>%
  mutate(pos = "QB", pos_rank_aft = row_number(), pos_rank_diff = pos_rank_bef-pos_rank_aft) %>%
  select(player, pass_grade, pass_rtg, towp, btt, mean_epa, total, rank, pos, team, pos_rank_bef, pos_rank_aft, pos_rank_diff)
datatable(new_stats_qb)

Get Total Value and Rank

get the dataset that only contains the total and the ranks

qb_stats_total <- new_stats_qb %>%
  select(player, total, rank, pos, team, pos_rank_bef, pos_rank_aft, pos_rank_diff)
datatable(qb_stats_total)

Download Final Rankings

write.csv(qb_stats_total, "qb_stats_total.csv", row.names = FALSE)