{"cells":[{"metadata":{"_uuid":"b17100e10c7e187b800dd25bdf8a442f44e58bef"},"cell_type":"markdown","source":"I was interested in looking at the formations that the punting and receiving teams used prior to snapping the ball.  I used \n* the data from the play_information file listings detaills on each of the plays\n* the video_review file listing  the players that suffered concussions and how they occurred\n* the play_player_role_data file listing the position of each player\n* all the ngs files with the spatial data on each play\n\nI had trouble loading all of these files on my computer at once so after I read the ngs files in I subsetted them to just the rows that included the data with the event \"ball_snap\" "},{"metadata":{"_uuid":"eb8a91c57c80c74ae533a523917d0a10bc3a931c","_execution_state":"idle","trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"library(tidyverse) \nlibrary(data.table)\n\npuntplays <- fread(\"../input/play_information.csv\")\nconcussed <- fread(\"../input/video_review.csv\")\nplayers <- fread(\"../input/play_player_role_data.csv\")\n\nngs16pre <- fread(\"../input/NGS-2016-pre.csv\")\nngs16pre <- ngs16pre[Event == \"ball_snap\"]\n\nngs16reg1 <- fread(\"../input/NGS-2016-reg-wk1-6.csv\")\nngs16reg1 <- ngs16reg1[Event == \"ball_snap\"]\n\nngs16reg2 <- fread(\"../input/NGS-2016-reg-wk7-12.csv\")\nngs16reg2 <- ngs16reg2[Event == \"ball_snap\"]\n\nngs16reg3 <- fread(\"../input/NGS-2016-reg-wk13-17.csv\")\nngs16reg3 <- ngs16reg3[Event == \"ball_snap\"]\n\nngs16post <- fread(\"../input/NGS-2016-post.csv\")\nngs16post <- ngs16post[Event == \"ball_snap\"]\n\nngs17pre <- fread(\"../input/NGS-2017-pre.csv\")\nngs17pre <- ngs17pre[Event == \"ball_snap\"]\n\nngs17reg1 <- fread(\"../input/NGS-2017-reg-wk1-6.csv\")\nngs17reg1 <- ngs17reg1[Event == \"ball_snap\"]\n\nngs17reg2 <- fread(\"../input/NGS-2017-reg-wk7-12.csv\")\nngs17reg2 <- ngs17reg2[Event == \"ball_snap\"]\n\nngs17reg3 <- fread(\"../input/NGS-2017-reg-wk13-17.csv\")\nngs17reg3 <- ngs17reg3[Event == \"ball_snap\"]\n\nngs17post <- fread(\"../input/NGS-2017-post.csv\")\nngs17post <- ngs17post[Event == \"ball_snap\"]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e3b5584784f7eefd35fccebc449d40c71cab0ff3"},"cell_type":"markdown","source":"I combined all of the ngs files into one, and then joined with the positional data so that I had the Role (P, PR, etc) matched with the ngs data.  On a few of the plays there were multiple timestamps that were labeled as \"ball_snap\".  I used \"slice(1)\" to select the first of these as the true time of the snap of the ball."},{"metadata":{"trusted":true,"_uuid":"cf1f08d66212d8c98dfba45ce962e5809dd4183d"},"cell_type":"code","source":"allngs <- ngs16pre %>% union(ngs16reg1) %>% union(ngs16reg2) %>% union(ngs16reg3) %>%\n  union(ngs16post) %>% union(ngs17pre) %>% union(ngs17reg1) %>% union(ngs17reg2) %>%\n  union(ngs17reg3) %>% union(ngs17post) %>% arrange(GameKey, PlayID)\n\nfirstsnap <- allngs %>% \n  inner_join(players) %>%\n  group_by(GameKey, PlayID, GSISID) %>% \n  filter(!is.na(Role)) %>% \n  slice(1) %>% ungroup()\n\nhead(firstsnap)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6b1858dc501eceb5a6e9a1624317285db7dd560f"},"cell_type":"markdown","source":"I wanted each Role to have it's own columns with the x and y coordinates.  Some plays had the same Role assigned to multiple players, so I had to rename some of the duplicates (X1-X3).  I can't use the \"spread\" function in dplyr if there are more than one of the same Role per group, so that's why I changed the values of the duplicates."},{"metadata":{"trusted":true,"_uuid":"083b98bc55dd3386eca11f791504682f78bc2865"},"cell_type":"code","source":"firstsnap$Role[duplicated(firstsnap[,c(\"GameKey\",\"PlayID\",\"Role\")])] <- \"X1\"\nfirstsnap$Role[duplicated(firstsnap[,c(\"GameKey\",\"PlayID\",\"Role\")])] <- \"X2\"\nfirstsnap$Role[duplicated(firstsnap[,c(\"GameKey\",\"PlayID\",\"Role\")])] <- \"X3\"\n\nplaylevel <- firstsnap %>% select(GameKey, PlayID, Role, x, y) %>%\n  gather(coord, value, -(GameKey:Role)) %>% \n  unite(temp, Role, coord) %>%\n  spread(temp, value) %>% arrange(GameKey,PlayID)\n\nplaylevel[1:5,1:10]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"77fdd1a08472f7b76754a0a1e1130fb05eb3b97d"},"cell_type":"markdown","source":"With one more data join, I can add in the concussion data to see which plays resulted in concussions and which did not.  Even though I had to introduce some new roles (X1-X3) in the previous step, I wasn't able to accurately identify what those Roles were.  So I excluded them here.  I also excluded plays that didn't include a Long Snapper, for reasons I'll explain further down.\n\nI also wanted to make sure that these were really \"valid\" plays, so I looked at plays where there were exactly 22 players on the field."},{"metadata":{"trusted":true,"_uuid":"2e08d35df16c33bf2fb6910228d9269bb4108b55"},"cell_type":"code","source":"formations <- puntplays %>% inner_join(playlevel) %>%\n  filter(!is.na(PLS_x)) %>% select(-c(X1_x:X3_y)) %>%\n  left_join(concussed)\n\nformations22 <- formations[rowSums(!is.na(formations\n                                          [,grep(\"_x\", names(formations))]))==22,]\n\nformations22$concussion <- \"no\"\nformations22$concussion[!is.na(formations22$GSISID)] <- \"yes\"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c6bbe55966fb2a3ec53d612b67b295d1162da0fe"},"cell_type":"markdown","source":"This part is particularly ugly, but I had to list out all of the roles in order to identify them as the kicking or receiving teams.  Also, I use \"kicking\" a lot when \"punting\" would have been more accurate.  Once I started labeling things as \"kicking\", I couldn't stop myself.\n\nI also added some subsetted lists that separated these into the columns that included the y or x directions."},{"metadata":{"trusted":true,"_uuid":"63f96458f5052525dff56da439e599ae83c6a5bb"},"cell_type":"code","source":"kicking <- c(\"GL_x\", \"GL_y\", \"GLi_x\", \"GLi_y\", \"GLo_x\", \"GLo_y\", \"GR_x\", \"GR_y\", \"GRi_x\", \"GRi_y\",\n             \"GRo_x\", \"GRo_y\", \"P_x\", \"P_y\", \"PC_x\", \"PC_y\", \"PLG_x\",  \"PLG_y\", \"PLS_x\", \"PLS_y\", \n             \"PLT_x\", \"PLT_y\", \"PLW_x\",  \"PLW_y\", \"PPR_x\", \"PPR_y\", \"PPRi_x\", \"PPRi_y\", \"PPRo_x\", \n             \"PPRo_y\", \"PRG_x\", \"PRG_y\", \"PRT_x\",  \"PRT_y\", \"PRW_x\",  \"PRW_y\", \"PPL_x\",  \"PPL_y\", \n             \"PPLi_x\", \"PPLi_y\", \"PPLo_x\", \"PPLo_y\")      \nreceiving <- c(\"PDL1_x\", \"PDL1_y\", \"PDL2_x\", \"PDL2_y\",\"PDL3_x\", \"PDL3_y\", \"PDL4_x\", \"PDL4_y\", \n               \"PDL5_x\", \"PDL5_y\", \"PDL6_x\", \"PDL6_y\", \"PDR1_x\", \"PDR1_y\", \"PDR2_x\", \"PDR2_y\", \n               \"PDR3_x\", \"PDR3_y\", \"PDR4_x\", \"PDR4_y\", \"PDR5_x\", \"PDR5_y\", \"PDR6_x\", \"PDR6_y\", \n               \"PFB_x\",\"PFB_y\", \"PLL_x\",  \"PLL_y\", \"PLL1_x\", \"PLL1_y\", \"PLL2_x\", \"PLL2_y\", \"PLL3_x\", \n               \"PLL3_y\", \"PLM_x\",  \"PLM_y\",  \"PLM1_x\", \"PLM1_y\", \"PLR_x\",  \"PLR_y\", \"PLR1_x\", \n               \"PLR1_y\", \"PLR2_x\", \"PLR2_y\", \"PLR3_x\", \"PLR3_y\", \"PR_x\",   \"PR_y\", \"VL_x\",  \"VL_y\", \n               \"VLi_x\", \"VLi_y\", \"VLo_x\",  \"VLo_y\", \"VR_x\",   \"VR_y\", \"VRi_x\",  \"VRi_y\",  \"VRo_x\", \n               \"VRo_y\", \"PDM_x\", \"PDM_y\")\n\nyreceiving <- receiving[grep(\"_y\",receiving)]\nykicking <- kicking[grep(\"_y\",kicking)]\n\nxreceiving <- receiving[grep(\"_x\",receiving)]\nxkicking <- kicking[grep(\"_x\",kicking)]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7a217d3d2a916718eb8abca6cfa9fa2e2f6a747f"},"cell_type":"markdown","source":"Now I have all of my data in a table, but it's still not nice to interpret.  I decided to use the position of the Long Snapper to determine where the ball is at the time of snap.  I don't know exactly where the measurement of the players is being taken (his head?  center of mass?  feet?) but I know that the LS is going to be the closest player to the ball.  Plus, some of the Special Teams rules deal with where the players line up relative to the Long Snapper.\nIf I subtract each players positions from the LS position, this gives me an estimate of where they were relative to the ball."},{"metadata":{"trusted":true,"_uuid":"7a05636634deef314e24351c710856250be615ec"},"cell_type":"code","source":"formations22[,c(xkicking,xreceiving)] <- formations22[,c(xkicking,xreceiving)]-formations22$PLS_x\nformations22[,c(ykicking,yreceiving)] <- formations22[,c(ykicking,yreceiving)]-formations22$PLS_y","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"dee439b2561e1ec1c191db4a5bcbf1a8c7e453f6"},"cell_type":"markdown","source":"Knowing that the Punter (P) is always behind the Long Snapper (PLS), I can figure out which direction the ball is being kicked.  If P_x is a positive that means the ball was being kicked right to left (looking at the field like a graph).  If P_x is negative, then the ball was kicked from left to right.\nFor the plays with P_x as a positive number, I flipped the signs so that would it appear that these plays were going from left to right.  This makes it easier to compare the formations.\nI do think that the direction of the play makes a difference (for example, if the Punt Returner is looking into the sun, that could lead to injury), but it was not useful to me for comparing all of the formations.  Directionality could be incorporated as a separate variable, though."},{"metadata":{"trusted":true,"_uuid":"271168e9caa683d63b1bddf8eb2f8218a1c1a294"},"cell_type":"code","source":"formations22[formations22$P_x > 0,c(kicking, receiving)] <- \n  -formations22[formations22$P_x > 0,c(kicking,receiving)]\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7f5479bf2963d35bf0aa8f8259f20642c84cb00e"},"cell_type":"markdown","source":"Now I have finally reached a point where I can look at some statistics of how the players are lined up (I just added these as columns onto my existing data set).\nFor instance, I can look at the number of players near the line of scrimmage (I excluded the punter and the punt returner as obviously not being near the line).  I can see whether those players were to the left (negative y) or to the right (positive y) of the Long Snapper.\nHere I show a summary of the incidence of concussions for each combination of players."},{"metadata":{"trusted":true,"_uuid":"9b04204c938c942d2c0710df99a1d66769fced55"},"cell_type":"code","source":"ykscrim <- ykicking[ykicking != \"P_y\"]\nyrscrim <- yreceiving[yreceiving !=\"PR_y\"]\n\n\nformations22$kscrimpos <- rowSums(formations22[,c(ykscrim)]>0, na.rm=TRUE)\nformations22$kscrimneg <- rowSums(formations22[,c(ykscrim)]<0, na.rm=TRUE)\nformations22$rscrimpos <- rowSums(formations22[,c(yrscrim)]>0, na.rm=TRUE)\nformations22$rscrimneg <- rowSums(formations22[,c(yrscrim)]<0, na.rm=TRUE)\n\nformations22 %>% group_by(kscrimpos,rscrimpos,kscrimneg,rscrimneg,concussion)%>%\nsummarize(total = n())%>%\nspread(concussion, total) %>% \nmutate(total = yes + no, inc = round(yes/total,5)) %>% \nfilter(!is.na(total))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2de54dc8ad7ad265d20aaeaf85c1186ca63508c2"},"cell_type":"markdown","source":"With mapply, both the x and y coordinates can be used to locate each player more specifically.  For instance, here I determine which players were really near the line of scrimmage (within 2 yards), and to which side of the Long Snapper they were on.  There are a lot of different combinations so I just show plays that were used 200 times or more."},{"metadata":{"trusted":true,"_uuid":"11aa3638b5fcea964e941d60049458d2e52bb78c"},"cell_type":"code","source":"formations22$klinepos <- rowSums(mapply(function(x,y) \n  ifelse(abs(formations22[,x])<2 & formations22[,y]>0,1,0),\n  xkicking, ykicking),na.rm=T)\n\nformations22$klineneg <- rowSums(mapply(function(x,y) \n  ifelse(abs(formations22[,x])<2 & formations22[,y]<0,1,0),\n  xkicking, ykicking),na.rm=T)\n\nformations22$rlinepos <- rowSums(mapply(function(x,y) \n  ifelse(abs(formations22[,x])<2 & formations22[,y]>0,1,0),\n  xreceiving, yreceiving),na.rm=T)\n\nformations22$rlineneg <- rowSums(mapply(function(x,y) \n  ifelse(abs(formations22[,x])<2 & formations22[,y]<0,1,0),\n  xreceiving, yreceiving),na.rm=T)\n\n\n\nformations22 %>% group_by(klinepos,rlinepos,klineneg,rlineneg,concussion)%>% \nsummarize(total = n())%>%\nspread(concussion, total) %>% \nmutate(total = sum(yes,no,na.rm=T), inc = round(yes/total,5)) %>% \nfilter(total >200)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9f41f13e30e9744b2572ab77191314a084b9a730"},"cell_type":"markdown","source":"Unfortunately, that's as far as I got with this.  i think I could have kept working on this dataset for another several months, but I ran out of time.  I'm very glad that this competition came along because I had been too tentative to try anything on Kaggle, but I couldn't resist an NFL dataset."}],"metadata":{"kernelspec":{"display_name":"R","language":"R","name":"ir"},"language_info":{"mimetype":"text/x-r-source","name":"R","pygments_lexer":"r","version":"3.4.2","file_extension":".r","codemirror_mode":"r"}},"nbformat":4,"nbformat_minor":1}