```{r load, include = FALSE} ## Importing packages library(readr) library(dplyr) library(ggplot2) library(lubridate) library(tidyr) library(scales) library(gganimate) library(tidyverse) library(cowplot) library(htmlwidgets) #Data Import video_review <- read_csv("../input/video_review.csv") player_role_data <- read_csv("../input/play_player_role_data.csv") %>% dplyr::mutate(Team = ifelse(Role %in% c('VR','VRo','VRi', 'VL','VLi','VLo', 'PDM', 'PDR1','PDR2','PDR3','PDR4','PDR5','PDR6', 'PDL1','PDL2','PDL3','PDL4','PDL5','PDL6', 'PLR','PLR1','PLR2','PLR3', 'PLM','PLM1', 'PLL','PLL1','PLL2','PLL3', 'PFB','PR'), "Return Team", "Kicking Team"), Role_Name = ifelse(substr(Role, 1, 2) %in% c("VL","VR"), "V", ifelse(substr(Role, 1, 3) %in% c("..//PDR","PDL"), "PD", ifelse(substr(Role, 1, 3) %in% c("PLL","PLR","PLM","PFB"), "PL", ifelse(substr(Role, 1, 2) %in% c("GL","GR"), "G", ifelse(substr(Role, 1, 3) %in% c("PLT","PLG","PLS","PRG","PRT", "PLW", "PRW"), "Line", ifelse(Role == "PR", "PR", ifelse(substr(Role, 1, 2) %in% c("PP", "PC"), "Protect", "P")))))))) play_finder <- video_review %>% select(Season_Year,GameKey,PlayID) #Get all the NGS data for concussion plays play_ngs <- read_csv("../input/NGS-2016-reg-wk13-17.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder) %>% rbind(read_csv("../input/NGS-2016-reg-wk1-6.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2016-reg-wk7-12.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2017-reg-wk1-6.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2017-reg-wk7-12.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2017-reg-wk13-17.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2017-post.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2017-pre.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2016-post.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) %>% rbind(read_csv("../input/NGS-2016-pre.csv", col_types = cols(Event = col_character(), Time = col_character())) %>% inner_join(play_finder)) #Get times of snap ball_snap <- play_ngs %>% filter(Event == "line_set") %>% mutate(ball_snap_time = Time) %>% select(Season_Year, GameKey, PlayID, ball_snap_time) %>% unique() #get times of end of play (tackle, fair catch, etc.) play_ends <- play_ngs %>% filter(Event %in% c("tackle", "out_of_bounds", "punt_downed", "fair_catch", "touchdown","touchback")) %>% mutate(play_end = Time) %>% select(Season_Year, GameKey, PlayID, play_end) %>% unique() ``` First, I write functions to manually get NGS data from plays I want to look at, and view the entire play overhead through gganimate, ```{r writefunctions} xmin <- 0 xmax <- 160/3 hash.right <- 38.35 hash.left <- 12 hash.width <- 3.3 ## Specific boundaries for a given play ymin <- 0 ymax <- 120 df.hash <- expand.grid(x = c(0, 23.36667, 29.96667, xmax), y = (10:110)) df.hash <- df.hash %>% filter(!(floor(y %% 5) == 0)) df.hash <- df.hash %>% filter(y < ymax, y > ymin) get_play_all <- function(gamekey, playid) { video_review %>% filter(GameKey == gamekey, PlayID == playid) %>% select(Season_Year, GameKey, PlayID) %>% inner_join(play_ngs) %>% inner_join(player_role_data) } graph_play <- function(play_NGS, label = Role) { play_NGS %>% ggplot(aes(x = xmax - y, y = x, fill = Team, label = Role)) + geom_point(size = 10, aes( x = (xmax-y), y = x, colour = Team)) + geom_text(aes(x = (xmax-y), y = x, label = Role), colour = "white", vjust = 0.36, size = 3.5) + ylab("Yardline") + xlab("Distance from Sideline") + annotate("text", x = df.hash$x[df.hash$x < 55/2], y = df.hash$y[df.hash$x < 55/2], label = "_", hjust = 0, vjust = -0.2) + annotate("text", x = df.hash$x[df.hash$x > 55/2], y = df.hash$y[df.hash$x > 55/2], label = "_", hjust = 1, vjust = -0.2) + annotate("segment", x = xmin, y = seq(max(10, ymin), min(ymax, 110), by = 5), xend = xmax, yend = seq(max(10, ymin), min(ymax, 110), by = 5)) + annotate("text", x = rep(hash.left, 11), y = seq(10, 110, by = 10), label = c("G ", seq(10, 50, by = 10), rev(seq(10, 40, by = 10)), " G"), angle = 270, size = 4) + annotate("text", x = rep((xmax - hash.left), 11), y = seq(10, 110, by = 10), label = c(" G", seq(10, 50, by = 10), rev(seq(10, 40, by = 10)), "G "), angle = 90, size = 4) + annotate("segment", x = c(xmin, xmin, xmax, xmax), y = c(ymin, ymax, ymax, ymin), xend = c(xmin, xmax, xmax, xmin), yend = c(ymax, ymax, ymin, ymin), colour = "black") + transition_time(ymd_hms(Time))} ``` So, let's look at play Season_Year 231, GameKey 231, Play 1976. This play had 4 jammers opposite of two gunners. First, let's look at the tape: ```{r tape1} viewer <- getOption("viewer") viewer("https://nfl-vod.cdn.anvato.net/league/5691/18/11/25/284954/284954_75F12432BA90408C92660A696C1A12C8_181125_284954_huber_punt_3200.mp4") ``` We see that the punter muffed the punt, and there was a massive hit to his left upon the return. Let's look at the overhead via gganimate ```{r gganimate1} graph_play(get_play_all(231, 1976)) ``` What happened on the play was the jammers held up the gunners very well, which led to a lot of kicking team members near the returner, but permitting him to make a return. The punter did muff the punt. Below is the location of every player at the time the punter "received" the ball via NGS. ```{r ggplot1} get_play_all(231, 1976) %>% filter(Event == "punt_received") %>% ggplot(aes(x = xmax - y, y = x, fill = Team, label = Role_Name)) + geom_point(size = 10, aes(x = (xmax-y), y = x, colour = Team)) + geom_text(aes(x = (xmax-y), y = x, label = Role_Name), colour = "white", vjust = 0.36, size = 3.5) + ylab("Yardline") + xlab("Distance from Sideline") + annotate("text", x = df.hash$x[df.hash$x < 55/2], y = df.hash$y[df.hash$x < 55/2], label = "_", hjust = 0, vjust = -0.2) + annotate("text", x = df.hash$x[df.hash$x > 55/2], y = df.hash$y[df.hash$x > 55/2], label = "_", hjust = 1, vjust = -0.2) + annotate("segment", x = xmin, y = seq(max(10, ymin), min(ymax, 110), by = 5), xend = xmax, yend = seq(max(10, ymin), min(ymax, 110), by = 5)) + annotate("text", x = rep(hash.left, 11), y = seq(10, 110, by = 10), label = c("G ", seq(10, 50, by = 10), rev(seq(10, 40, by = 10)), " G"), angle = 270, size = 4) + annotate("text", x = rep((xmax - hash.left), 11), y = seq(10, 110, by = 10), label = c(" G", seq(10, 50, by = 10), rev(seq(10, 40, by = 10)), "G "), angle = 90, size = 4) + annotate("segment", x = c(xmin, xmin, xmax, xmax), y = c(ymin, ymax, ymax, ymin), xend = c(xmin, xmax, xmax, xmin), yend = c(ymax, ymax, ymin, ymin), colour = "black") + ggtitle("2016 - Game 231, Play 1976") ``` While the gunners couldn't get to the returner in time and force maybe a fair catch, there are still a lot of kicking team players surrounding the returners, with a lot of space for hitting. With little help in front of the returner, a jammer came all the way back with a heavy, helmet-to-helmet block. Now, let's look at a play with two jammers. ```{r tape2} viewer("http://a.video.nfl.com//films/vodzilla/153234/Punt_by_Kasey_Redfern-w6Cpit4D-20181119_152918853_5000k.mp4") ``` This concussion occurs on an illegal blindside hit from a blocker. The blocker may have been trying to hit an elbow pad, but the collision is less heavy than the 4 jammer play above. ```{r gganimate2} graph_play(get_play_all(21, 2587)) ``` This punt was eventually downed. This play was not as fast as the other one, which is what happens when there are only two jammers. The injury occurred was on an illegal hit, and not directly the result of the 2 jammer formation.