This report is intended to present RB’s and QB’s player statistics using visualizations. All data used in this project is from nflscrapR, which can be found here: https://github.com/ryurko/nflscrapR-data
Expected points added is used often in this report. More information on EPA can be found here: http://www.advancedfootballanalytics.com/index.php/home/stats/stats-explained/expected-points-and-epa-explained
Loading required packages and importing 2018 play-by-play data
#install.packages("tidyverse")
library(tidyverse)
theme_set(theme_bw())
pbp_18 <- readr::read_csv("https://raw.githubusercontent.com/ryurko/nflscrapR-data/master/play_by_play_data/regular_season/reg_pbp_2018.csv")
Creating the dataset for running backs in 2018
min_rush_cnt <- 160
rushing <- pbp_18 %>%
filter(rush_attempt == 1, !is.na(rusher_player_name),!is.na(epa))%>%
group_by(rusher_player_name) %>%
summarise(Rush_Attempts= n(),
Total_Yards = sum(yards_gained),
Touchdowns = sum(touchdown),
Average_Yards = round(mean(yards_gained),2),
Average_EPA = round(mean(epa),2)) %>%
filter(Rush_Attempts >= min_rush_cnt) %>%
arrange(desc(Rush_Attempts)) %>%
rename(Player = rusher_player_name)
#Ordered by number of rush attempts
| Player | Rush_Attempts | Total_Yards | Touchdowns | Average_Yards | Average_EPA |
|---|---|---|---|---|---|
| E.Elliott | 304 | 1434 | 6 | 4.72 | 0.03 |
| D.Johnson | 300 | 1145 | 7 | 3.82 | -0.13 |
| S.Barkley | 263 | 1309 | 11 | 4.98 | -0.01 |
| T.Gurley | 260 | 1255 | 17 | 4.83 | 0.15 |
| A.Peterson | 251 | 1042 | 7 | 4.15 | -0.06 |
| J.Howard | 250 | 935 | 9 | 3.74 | -0.07 |
| C.Carson | 247 | 1151 | 9 | 4.66 | 0.04 |
| J.Mixon | 237 | 1168 | 8 | 4.93 | -0.01 |
| P.Barber | 234 | 871 | 5 | 3.72 | -0.13 |
| C.McCaffrey | 219 | 1098 | 7 | 5.01 | 0.04 |
| D.Henry | 215 | 1059 | 12 | 4.93 | 0.08 |
| J.Conner | 215 | 973 | 12 | 4.53 | 0.04 |
| L.Miller | 210 | 973 | 5 | 4.63 | -0.02 |
| S.Michel | 209 | 931 | 6 | 4.45 | 0.02 |
| A.Kamara | 197 | 889 | 14 | 4.51 | 0.13 |
| M.Mack | 197 | 912 | 9 | 4.63 | 0.08 |
| P.Lindsay | 193 | 1037 | 9 | 5.37 | 0.07 |
| N.Chubb | 192 | 996 | 8 | 5.19 | 0.01 |
| K.Hunt | 181 | 824 | 7 | 4.55 | 0.04 |
| M.Gordon | 176 | 887 | 10 | 5.04 | 0.08 |
| C.Hyde | 172 | 571 | 5 | 3.32 | -0.18 |
| D.Martin | 172 | 723 | 4 | 4.20 | -0.10 |
| T.Coleman | 167 | 800 | 4 | 4.79 | -0.03 |
| L.McCoy | 161 | 514 | 3 | 3.19 | -0.18 |
In 2018, 24 RBs had over 160 rush attempts.Ezekiel Elliot and Saquon Barkley rushed for 1434 and 1309 yards to top the group.
rushing %>%
ggplot(aes(Rush_Attempts,Average_Yards,alpha = Touchdowns)) +
geom_point() +
labs(title ="Average yards per run by running backs in 2018",
subtitle = "160 Rush Attempt Minimum ",
x = "2018 Rush Attempts",
y = "Average Yards Per Run",
caption= "Data from nflscrapR") +
guides(fill = guide_legend(reverse=FALSE))+
scale_fill_discrete(name = "Touchdown")+
geom_text(aes(label = Player), vjust = 1, hjust = 1, check_overlap = TRUE)
Todd Gurley led all RBs with 17 touchdowns. Phillip Lindsay, Nick Chubb,Christian McCaffery and Melvin Gordon all hovered over a 5 yard per run average.
rushing %>%
ggplot(aes(Rush_Attempts,Average_EPA)) +
geom_point() +
labs(title ="Average expected points added per run by running backs in 2018",
subtitle = "160 Rush Attempt Minimum ",
x = "2018 Rush Attempts",
y = "Average EPA Per Run",
caption= "Data from nflscrapR") +
geom_text(aes(label = Player), vjust = 1, hjust = 1, check_overlap = FALSE)
Todd Gurley and Alvin Kamara lead the pack with 0.153 and 0.126 expected points added per run respectively.
Highest EPA per run attempt
rushing %>%
select(Player,Average_EPA) %>%
arrange(desc(Average_EPA))%>%
head(10) %>%
knitr::kable()
| Player | Average_EPA |
|---|---|
| T.Gurley | 0.15 |
| A.Kamara | 0.13 |
| D.Henry | 0.08 |
| M.Mack | 0.08 |
| M.Gordon | 0.08 |
| P.Lindsay | 0.07 |
| C.Carson | 0.04 |
| C.McCaffrey | 0.04 |
| J.Conner | 0.04 |
| K.Hunt | 0.04 |
Lowest EPA per run attempt
rushing %>%
select(Player,Average_EPA) %>%
arrange(Average_EPA)%>%
head(10) %>%
knitr::kable()
| Player | Average_EPA |
|---|---|
| C.Hyde | -0.18 |
| L.McCoy | -0.18 |
| D.Johnson | -0.13 |
| P.Barber | -0.13 |
| D.Martin | -0.10 |
| J.Howard | -0.07 |
| A.Peterson | -0.06 |
| T.Coleman | -0.03 |
| L.Miller | -0.02 |
| S.Barkley | -0.01 |
Creating the dataset for 2018 Quarterbacks
passingepa <- pbp_18 %>%
filter( !is.na(air_yards), !is.na(epa),qb_dropback == 1) %>%
group_by(passer_player_name) %>%
summarise(Avg_AirYards= round(mean(air_yards),2),
Avg_EPA = round(mean(epa),2),
Touchdowns = sum(touchdown),
Total_Dropbacks = n()) %>%
filter(Total_Dropbacks >= 300) %>%
arrange(desc(Avg_AirYards)) %>%
rename(Player = passer_player_name)
Air yards is the distance the ball is thrown past the line of scrimmage
passingepa %>%
ggplot(aes(Total_Dropbacks,Avg_AirYards)) +
geom_point() +
geom_text(aes(label = Player, vjust = 1, hjust = 1, check_overlap = FALSE)) +
labs(title = 'Average Air Yards by Quarterbacks in 2018',
subtitle = "300 Droback Minimum",
x = 'Total QB Dropbacks',
y = 'Average Airyards per Dropback ',
caption = 'Data from nflscrapR')
Highest air yards average
passingepa %>%
arrange(desc(Avg_AirYards))%>%
head(10) %>%
knitr::kable()
| Player | Avg_AirYards | Avg_EPA | Touchdowns | Total_Dropbacks |
|---|---|---|---|---|
| J.Allen | 10.91 | -0.05 | 11 | 316 |
| J.Winston | 10.82 | 0.26 | 20 | 376 |
| B.Mayfield | 9.37 | 0.16 | 29 | 482 |
| S.Darnold | 9.22 | 0.06 | 19 | 409 |
| R.Wilson | 9.06 | 0.37 | 37 | 413 |
| P.Mahomes | 8.98 | 0.46 | 51 | 576 |
| A.Rodgers | 8.81 | 0.25 | 25 | 590 |
| M.Trubisky | 8.74 | 0.21 | 24 | 431 |
| J.Goff | 8.68 | 0.34 | 32 | 556 |
| J.Flacco | 8.61 | 0.10 | 12 | 374 |
Lowest air yards average
passingepa %>%
arrange(Avg_AirYards)%>%
head(10) %>%
knitr::kable()
| Player | Avg_AirYards | Avg_EPA | Touchdowns | Total_Dropbacks |
|---|---|---|---|---|
| D.Carr | 6.95 | 0.18 | 21 | 541 |
| M.Stafford | 6.95 | 0.18 | 23 | 552 |
| B.Bortles | 7.06 | 0.01 | 13 | 398 |
| D.Brees | 7.18 | 0.38 | 32 | 488 |
| C.Newton | 7.19 | 0.20 | 25 | 463 |
| K.Cousins | 7.28 | 0.15 | 33 | 600 |
| E.Manning | 7.35 | 0.19 | 22 | 573 |
| A.Luck | 7.42 | 0.23 | 42 | 632 |
| C.Keenum | 7.45 | 0.09 | 18 | 577 |
| D.Prescott | 7.54 | 0.23 | 22 | 524 |
Patrick Mahomes led the league in passing touchdowns and yards gained per dropback.
passingepa %>%
ggplot(aes(Total_Dropbacks,Avg_EPA)) +
geom_point() +
geom_text(aes(label = Player, vjust = 1, hjust = 1, check_overlap = TRUE)) +
labs(title = 'Expected Points Added by Quarterbacks in 2018',
subtitle = "300 Droback Minimum",
x = 'Total QB Dropbacks',
y = 'Average EPA per Pass ',
caption = 'Data from nflscrapR')
Patrick Mahomes was also the leader of EPA per dropback
Highest EPA per Dropback
passingepa %>%
select(Player,Avg_EPA) %>%
arrange(desc(Avg_EPA))%>%
head(10) %>%
knitr::kable()
| Player | Avg_EPA |
|---|---|
| P.Mahomes | 0.46 |
| P.Rivers | 0.38 |
| D.Brees | 0.38 |
| R.Wilson | 0.37 |
| J.Goff | 0.34 |
| M.Ryan | 0.34 |
| D.Watson | 0.30 |
| C.Wentz | 0.29 |
| J.Winston | 0.26 |
| B.Roethlisberger | 0.26 |
Lowest EPA per Dropback
passingepa %>%
select(Player,Avg_EPA) %>%
arrange(Avg_EPA)%>%
head(10) %>%
knitr::kable()
| Player | Avg_EPA |
|---|---|
| J.Rosen | -0.15 |
| J.Allen | -0.05 |
| B.Bortles | 0.01 |
| S.Darnold | 0.06 |
| A.Smith | 0.09 |
| C.Keenum | 0.09 |
| J.Flacco | 0.10 |
| K.Cousins | 0.15 |
| B.Mayfield | 0.16 |
| D.Carr | 0.18 |
Focusing on returning AFC South Quarterbacks and their pass locations
First step is to create a data set that filters for all QB dropbacks excluding penalties
afc_qbdropbacks <- pbp_18 %>%
filter(passer_player_name == "D.Watson" | passer_player_name == "A.Luck" | passer_player_name == "M.Mariota",!is.na(pass_location), qb_dropback == 1,penalty == 0) %>%
mutate_at(vars(incomplete_pass),factor)
Counting completed passes by pass location
afc_qbdropbacks %>%
group_by(passer_player_name,pass_location) %>%
count(incomplete_pass)%>%
knitr::kable()
| passer_player_name | pass_location | incomplete_pass | n |
|---|---|---|---|
| A.Luck | left | 0 | 164 |
| A.Luck | left | 1 | 63 |
| A.Luck | middle | 0 | 105 |
| A.Luck | middle | 1 | 48 |
| A.Luck | right | 0 | 168 |
| A.Luck | right | 1 | 78 |
| D.Watson | left | 0 | 125 |
| D.Watson | left | 1 | 57 |
| D.Watson | middle | 0 | 95 |
| D.Watson | middle | 1 | 26 |
| D.Watson | right | 0 | 131 |
| D.Watson | right | 1 | 62 |
| M.Mariota | left | 0 | 95 |
| M.Mariota | left | 1 | 37 |
| M.Mariota | middle | 0 | 66 |
| M.Mariota | middle | 1 | 23 |
| M.Mariota | right | 0 | 73 |
| M.Mariota | right | 1 | 35 |
Visualizing the data
afc_qbdropbacks %>%
filter(qb_dropback == 1,penalty == 0 ) %>%
ggplot(aes(passer_player_name,fill= incomplete_pass)) +
geom_bar() +
facet_wrap(~pass_location) +
labs(title = "Returning AFC South Quarterbacks Pass Attempt Locations 2018 ",
x = "Pass Location",
y = "Pass Attempts",
caption= "Data from nflscrapR") +
guides(fill = guide_legend(reverse=FALSE))+
scale_fill_discrete(name = "Pass Result", labels = c("Completed", "Incompleted"))
pbp_18 %>%
filter(passer_player_name == "A.Luck", !is.na(pass_location), !is.na(air_yards)) %>%
ggplot(aes(pass_location,air_yards)) +
geom_boxplot()+
coord_flip()+
labs(title= 'Air Yards by Pass Location from Andrew Luck in 2018' ,
x = 'Pass Location',
y = 'Air Yards',
caption= 'Data from nflscrapR')