{"metadata":{"kernelspec":{"name":"ir","display_name":"R","language":"R"},"language_info":{"name":"R","codemirror_mode":"r","pygments_lexer":"r","mimetype":"text/x-r-source","file_extension":".r","version":"4.0.5"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Instantaneous Kick Received Analysis\n\nBy Eric Matthew Rupinski\nRutgers University \n\n1.\tIntroduction:\n\nWhen it comes to analyzing plays in today’s game, too often people form their opinions and thoughts almost entirely on how the play concluded. Is the conclusion of the play important? Of course! But often people forget the importance of analyzing the decisions and habits of players at the start of the play, because more often than not, individuals tend to display patterns. Though these patterns are beneficial for analyzing and predicting player performance, more importantly; they provide insight into the implicit interconnection of the game.  Furthermore, there’s no aspect of football that embodies the importance of initial player performance like kickoffs and kick returns. \n\nI chose to dive deeper into the mechanism of kick returns, specifically analyzing kick returns at the moment the kick returner receives the ball. My goal when researching this subset of the data, was not only to predict the number of return yards at the instantaneous moment the kick was received, but also to try and find unnoticed or underappreciated relationships between the tracking data and the number of return yards. My analysis consists of two major components. The first component was the process of creating the most beneficial data set to model. The second component was optimizing the formula of the most beneficial variables to predict the number of return yards.  The first component is not only crucial for optimizing a regression model, but also for understanding the relationship among all these various measures of the tracking data, and how these measures can be utilized to impact performance. The second component is crucial for finding the best balance of the predictors as possible, to be able to fully understand relationship amongst the tracking data. Altogether, I analyzed the composition of the tracking data in relation to the number of return yards, I modeled that relationship with various models and packages in R to optimize the accuracy of the model, and finally, I created an expected return yards variable from my model. \n\n2.\tProcedure:\n\n2a. Data \n\nThough several data sources were used in the process of creating the final data frame, the tracking data was the focus of my analysis, with the exception of the ‘kickReturnYardage” from the “plays” data source.  Most of the work done, with any other data source besides the tracking data, was with the purpose of clarifying variables in the tracking, such as changing the team status from “home/away” to each player’s specific team. The following provided data sources were utilized: All the tracking data, the “play” data frame, the “game” data frame, and finally, the “PFF Scouting” data frame. \n\nSeveral variables were created for the purpose of analyzing aspects of the tracking data that were not explicitly shown but were most likely impactful. Also, several key variables were created to sort through specific conditions with ease. Though the created predictor variables appear at various points in the analysis, the variables were created to represent the following aspects of the data:\n-\tA categorical combination of the exact position on the kicking team (L1, L2, …. R2,R1), along with a unique level for the kick returner (labeled “KR” and then a numerical value for the rest of the receiving team that represents the proximity to the kick returner at the point of receiving the kick\n-\tA general Euclidean variable that represents every player’s distance from the Kick Returner, individually \n-\tSeveral combinations of descriptive statistics (measuring central tendency, variance, and total size) , that describe Euclidean distances for specific subsets of players based on their team and rank in distance from the kick returner. The following contains the factors for the combinations\no\tProximity subset, which includes the 2 closest players, the 4 closest players AFTER the initial 2, and the grand total\no\tThe totals, means, and standard deviations were calculated for each of those subsets\no\tThe subsets were separated by team, so the process was done for both teams.\n-\tFor the kicking team only: the Euclidean process above was repeated for the following variables: speed, acceleration, orientation, and direction\n-\tMissed tacklers were converted to a total number of missed tackles for each play\n\n\nAdditionally, several configurations of the data were created to emphasize certain variables in each case. The data set differences are the following:\n-\tA data frame where the Euclidean distances from the kick returner were ranked, and then pivoted so each observation was one play, at the moment the kick was received. The kick returner had all of their tracking statistics, but only Euclidean distances were represented for each individual, along with a dummy variable for each ranking to display whether they were on the kicking team or receiving team. \n-\tA data frame where only the kick returner and the kicking team had their tracking data represented,\n-\tThe main data frame for the bulk of the computations with the following set up:\no\tContains all the variables that are mathematical representations of the distribution of specific player subsets.\no\tThe kicking team is sorted by precise position (and not just Euclidean distance)\no\tThe receiving team players are included, and are labeled by ranking of the distance to the kick returner at the point of the kick being received. \nAfter applying the same methodology to the various data frames, the best resulting data set consists of only position-based tracking statistics for the kicking team and the kick returner, the remainder of the kick return team was represented summary statistics of a subset the return team and the kicking team.  \n\nIn connection was the most advantageous model, the following variables were the present in the final form of the data: (All variables are at the point that the kick is received by the kick returner):\n-\tX = the position of the kick returner\n-\tTMT = the number of hypothetical missed tackles during the play\n-\tRTC2 = Total Euclidean for Receiving Team (First 2 closest players besides kick returner)\n-\tRSDE = The Euclidian’s total standard deviation for the kicking team\n-\tRMC2 = Mean Euclidean for Kick Team (First 2 closest players)\n-\treturnDirectionActual_C= when the ball is being returned center\n-\tKTC3T6 = The total Euclidean for the 3rd closest to 6th closest players on the kicking team\n-\tKTC2 = Total Euclidean for Kick Team (First 2 closest players)\n-\tKME = The mean Euclidean distance of the kicking team\n-\tKMC3T6 = the men Euclidean distance of the 3rd closest to 6th closest player on the kicking team\n-\tKMC2 = The mean Euclidean distance of the closest 2 players on the kicking team\n-\tEurR5= Distance from R5 to the kick returner\n-\tEucR4= Distance from R4 to the kick returner\n-\tEucR3= Distance from R3 to the kick returner\n-\tEucR2= Distance from R2 to the kick returner\n-\tEucR1= Distance from R1 to the kick returner\n-\tEucL5= Distance from L5 to the kick returner\n-\tEucL4= Distance from L4 to the kick returner\n-\tEucL3 = Distance from L3 to the kick returner\n-\tEucL2= Distance from L2 to the kick returner\n-\tEucL1 = Distance from L1 to the kick returner\n-\tEucK = Distance from the kicker to the kick returner\n-\tAccKME = The mean acceleration of the kicking team\n\n","metadata":{}},{"cell_type":"code","source":"#Read in libraries:\nlibrary(here)\nlibrary(ggplot2)\nlibrary(gganimate)\nlibrary(udpipe)\nlibrary(randomForest)\nlibrary(tidyverse)\nlibrary(lme4)\nlibrary(factoextra)\nlibrary(moments)\nlibrary(forecast)\nlibrary(fastDummies)\nlibrary(NeuralNetTools)\nlibrary(caret)\nlibrary(glmnet)\nlibrary(elasticnet)\n\n#Load data\ndf_tracking_1 = read.csv('/kaggle/input/nfl-big-data-bowl-2022/tracking2018.csv')\ndf_tracking_2 = read.csv('/kaggle/input/nfl-big-data-bowl-2022/tracking2019.csv')\ndf_tracking_3 = read.csv('/kaggle/input/nfl-big-data-bowl-2022/tracking2020.csv')\ndf_plays =  read.csv('/kaggle/input/nfl-big-data-bowl-2022/plays.csv')\ndf_PFFScouting =  read.csv('/kaggle/input/nfl-big-data-bowl-2022/PFFScoutingData.csv')\ndf_games = read.csv('/kaggle/input/nfl-big-data-bowl-2022/games.csv')\n\n\n#Combining tracking data sets:-\ndf_t <- rbind(df_tracking_2,df_tracking_3)\n\n#Standardizing tracking data so its always in direction of kicking team.\ndf_tracking <- df_t %>%\n                mutate(x = ifelse(playDirection == \"left\", 120-x, x),\n                       y = ifelse(playDirection == \"left\", 160/3 - y, y))","metadata":{"execution":{"iopub.status.busy":"2022-01-11T02:20:29.698726Z","iopub.execute_input":"2022-01-11T02:20:29.701205Z","iopub.status.idle":"2022-01-11T02:26:14.3355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**The following code is to set up the several combinations of data sets to determine the best subset:**","metadata":{}},{"cell_type":"code","source":"#creating data frame that will only includes Kick Returns that were actually returned. \ndf_first_ko <- df_tracking %>%\n    #joining the scouting data\n    left_join(df_plays, by = c(\"gameId\", \"playId\")) %>%\n    #filtering for kickoff plays only\n    filter(specialTeamsPlayType == \"Kickoff\") %>%\n  #filtering for only kicks that were returned\n  filter(kickReturnYardage != \"NA\") %>%\n  #Subsetting desireable variables\n  select(., time:playDirection, possessionTeam:kickBlockerId,kickReturnYardage)\n\n#Creating a new variable where I change a categorical level of the position variable to differentiate the kick returner from the rest (The \"A\" prefix was added to be able to run code later that matches the positioning of the variable)\ndf_first_ko_1 <- df_first_ko %>% group_by(gameId,playId,frameId) %>%  mutate(a_pos = replace(position,nflId==returnerId,\"AKR\"))\n\n#Setting aside this separate data set for a later step\ndf_Kickoffs_A <- df_first_ko_1 %>% select(., -time,-(nflId:team),-(gameId:a_pos)) %>% filter(.,event==\"tackle\")\n\n#Arranging the data by the new position variable, so kick returner is at the top \ndf_first_ko_2 <- df_first_ko_1 %>% arrange(.,a_pos) %>% \n  #Then grouping by each unique frame in each unique play\n  group_by(gameId,playId,frameId) %>%\n  #Then creating the distance that each player/observation is from the Kick Returner\n   mutate(Euclidean = as.matrix(dist(cbind(x, y)))[1, ]) %>%\n  #ungrouping the data\n  ungroup()\n\n#Data set for later\n df_ko_rf_a <- df_first_ko_2\n\n#Grouping the data again by each unqiue frame\ndf_first_ko_3 <- df_first_ko_2 %>% group_by(gameId,playId,frameId) %>%\n  #Creating a unique ID for every other player in each frame (including football =23)\n  mutate(UniPlayInd = row_number()) %>%\n  ungroup()\n\n#Matching each unique distance to its unique frameId and relative player relationship\nTestEuc<- df_first_ko_3 %>% pivot_wider(.,id_cols = c(gameId,playId,frameId,UniPlayInd), names_from = UniPlayInd, names_prefix = \"Euc\", values_from = Euclidean)\n\n#Converting Home/Away to Binary\n  #Might need to change method of encoding teams\ndf_first_ko_3$HomeTrue <- df_first_ko_3$team == \"home\"\ndf_first_ko_3$HomeTrue <- as.integer(df_first_ko_3$HomeTrue)\n\n#Home/away Hot encode\nHome_Away_Table <- df_first_ko_3 %>% pivot_wider(.,id_cols = c(gameId,playId,frameId,UniPlayInd), names_from = UniPlayInd, names_prefix = \"Home\", values_from = HomeTrue)\n\n#Combining tables \n df_first_ko_4 <- df_first_ko_3 %>% left_join(.,TestEuc, by = c(\"gameId\",\"playId\",\"frameId\")) %>% left_join(.,Home_Away_Table, by = c(\"gameId\",\"playId\",\"frameId\"))\n \n\n # Converting None to NA for events in Kickoff Data set\ndf_first_ko_4$event<- recode_factor(df_first_ko_4$event, None = NA_character_)\ndf_first_ko_4$event<- recode_factor(df_first_ko_4$event, kickoff = NA_character_)\n \n\n# Filtering out all other observation besides Kick Returners\n df_first_ko_5 <- df_first_ko_4 %>% filter(., a_pos == \"AKR\") %>%\n \n #Cleaning up unnessecary columns:\n select(., -time, -(displayName:team),- (playDirection:specialTeamsResult),-(returnerId:kickBlockerId), -(a_pos:Euc1)) %>% \n   #Filtering to only have the moment the KR catches the ball.\n   filter(., event ==\"kick_received\") \n \n\n#A function that creates a variable importance score\nI_SCORE_FUNC <- function(RF_TEST){\ni_scores <- varImp(RF_TEST ,conditional=TRUE)\n#Gathering rownames in 'var'  and converting it to the factor\n#to provide 'fill' parameter for the bar chart. \ni_scores <- i_scores %>% tibble::rownames_to_column(\"var\") \ni_scores$var<- i_scores$var %>% as.factor()\n#Plotting the bar and polar charts for comparing variables\ni_bar <- ggplot(data = i_scores) + \n  geom_bar(\n    stat = \"identity\",#it leaves the data without count and bin\n    mapping = aes(x = var, y=Overall, fill = var), \n    show.legend = FALSE,\n    width = 1\n  ) + \n  labs(x = NULL, y = NULL)\ni_bar + coord_polar() + theme_minimal()\ni_bar + coord_flip() + theme_minimal()\n}\n\n\n#Setting up second data set t\n\n#setting up team specific data sets\nDS_KO_2 <- df_ko_rf_a %>% \n  \n  #Join with games data set\n  inner_join(df_games)  %>% \n  \n  #Put specific team names for each player based off home/away\n  mutate(Teams = ifelse(team==\"home\",homeTeamAbbr,visitorTeamAbbr)) %>% \n  \n  #Creating variable to show what team is recieving the the ball during the KO\n  mutate(RecTeam = if_else(possessionTeam==Teams,\"Kick\",\"Rec\")) %>% \n  \n  #filter for only observations with the kicking team AND the KR\n  filter(.,RecTeam == \"Kick\" | a_pos == \"AKR\") %>% \n  \n  #Arrange by Euclidean\n  arrange(.,Euclidean)  %>% \n  \n  #Delete the football from the observations \n  filter(., displayName != \"football\") %>% \n  \n  #Subset every play to the moment the KR receives the ball\n  filter(., event ==\"kick_received\") %>%\n  \n  #Select Variables of Importance\n  select(., -time,-event,-(displayName:team),-(playDirection:kickBlockerId),-(season:Teams)) %>%\n  \n  #Group by gameId and playId\n  group_by(gameId,playId) %>% \n  \n  #Create a Unique Identifier for players on the kicking team (and KR)\n  mutate(UniPlayInd = row_number()) %>% filter(., UniPlayInd < 13 )\n\n#Create dummy tables \n\n#Euclidean Table\nXB <- DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Euc\", values_from = Euclidean)\n\n#Speed Table\nXC <- DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Speed\", values_from = s)\n\n#Acceleration Table\nXD <- DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Acc\", values_from = a)\n \n#Orientation Table\nXE <- DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Ori\", values_from = o)\n\n#Subsetting main data to only have the observation focused on the KR\nXA <- subset(DS_KO_2, a_pos == \"AKR\")\n\n#Creating Full Team Data (FT)\nFT <- XA %>% \n  \n  #left join all the dummy tables \n  left_join(XB) %>% left_join(XC) %>% left_join(XD) %>% left_join(XE) %>% \n  \n  #Join with PFF scouting data set\n  left_join(df_PFFScouting) %>% \n  \n  #Select only variables of interest \n  select(., -(nflId:playId),-(a_pos:Euc1),-Speed1,-Acc1,-Ori1,-(snapDetail:operationTime),-(kickDirectionIntended:kickDirectionActual),-(missedTackler:tackler),-(gunners:kickContactType)) %>% \n  \n  #Filter out missing values of hangtime and observations with no KR yards\n  filter(., hangTime != \"NA\") %>% filter(.,kickReturnYardage != \"NA\")\n\n#Delete all NAs from the Data set\nFT<- na.exclude(FT)\n\n#creating data frame that will only includes Kick Returns that were actually returned. \ndf_K <- df_tracking %>%\n  \n    #joining the scouting data\n    left_join(df_plays, by = c(\"gameId\", \"playId\")) %>%\n  \n    #filtering for kickoff plays only\n    filter(specialTeamsPlayType == \"Kickoff\") %>%\n  \n  #filtering for only kicks that were returned\n  filter(kickReturnYardage != \"NA\") %>%\n  \n  #Subsetting desireable variables\n  select(., time:playDirection, possessionTeam:kickBlockerId,kickReturnYardage) %>% \n  \n  #Creating Unique Indefintifer based off nflId, gameId, and playId\n  mutate(UKI = unique_identifier(., fields = c(\"nflId\", \"gameId\",\"playId\"))) %>% \n  \n  #Create Unique Identifier for each unique play across all games and seasons\n  mutate(UPI = unique_identifier(., fields = c(\"gameId\",\"playId\"))) %>%\n  \n  #Joining with PFF scouting data table\n  left_join(df_PFFScouting) %>%\n  \n  #joining data with games data set\n  inner_join(df_games)  %>% \n\n  #Sorting teams by home and away for each game into one column\n  mutate(Teams = ifelse(team==\"home\",homeTeamAbbr,visitorTeamAbbr)) %>% \n  \n  #Sorting by which team is the Kicking and Receiving team for each game \n  mutate(RecTeam = if_else(possessionTeam==Teams,\"Kick\",\"Rec\")) %>% \n\n  #Creating a new variable where I change a categorical level of the position variable to differentiate the kick returner from the rest (The \"A\" prefix was added to be able to run code later that matches the positioning of the variable)\n  group_by(UPI,frameId) %>%  mutate(a_pos = replace(position,nflId==returnerId,\"AKR\")) \n\n#Arranging the data by the new position variable, so kick returner is at the top \ndf_KOP <- df_K %>% arrange(.,a_pos) %>% \n  \n  #Subsetting the data to the moment the Kick Returner receives the kick\n filter(., event == \"kickoff\")   %>% \n  \n  #Subset to only include the Kicking Team\n  filter(.,RecTeam == \"Kick\") %>% \n  \n  #Creating categorical variable to differentiate between Kicker and non-kicker\n  mutate(KickerYN = ifelse(nflId==kickerId,\"A\",\"B\")) %>% \n  \n  #Cleaning up errors in the data to ensure only players in KickerYN level A , is a kicker\n  filter(., returnerId != \"NA\") %>%\n  \n  #Arrange with the kickers at the top\n  arrange(., KickerYN) %>% \n  \n  #Reset group and filter at the point of the kickoff and subset by each unique play \n   group_by(UPI) %>% \n  \n  #Creating a variable to find the distance from the kicker\n  mutate(DistKick = y - first(y)) %>%  \n  \n  #ungrouping the data and filter to delete the football out of the data \n  ungroup () %>% filter(., displayName != \"football\") %>%\n  \n  #Arrange by the Distance Kick\n  arrange(DistKick) %>% \n  \n  #Group by each play each unique play \n  group_by(UPI)  %>%  \n  \n  #Ranking by each distance for each play to determine the position from the kicker, setting up KOP (KOP = Kickoff position)\n  mutate(KOP = rank(DistKick)) %>% \n  \n  #Recoding the factors to match the position on the kickoff \n  mutate(KOP = as.factor(KOP), KOP = fct_recode(KOP, \"L1\" =\"1\", \"L2\"=\"2\",\"L3\"=\"3\",\"L4\"=\"4\",\"L5\"=\"5\", \"K\"=\"6\",\"R5\"=\"7\",\"R4\"=\"8\",\"R3\"=\"9\",\"R2\"=\"10\",\"R1\"=\"11\")) \n\n#Selecting the unique identifier and the Kickoff Position, and ungrouping the data\ndf_KOP <- df_KOP %>% select(., UKI, KOP)  %>% ungroup()\n\n#Create data set for later use \ndf_FDS <- df_KOP\n\n#Setting up main kickoff tracking dataset \n# Creating Inital Kickoff Tracking (KOT_I) by joining main tracking and Kickoff Position by unique identifier \ndf_KOT_I <- df_K %>% left_join(df_KOP, by = \"UKI\") %>%\n  \n  #Filter by the moment the kick was receieved by the kick returner\n  filter(., event == \"kick_received\") \n\n#Converting kickoff position to character and replacing NAs with 0s\ndf_KOT_I$KOP <- df_KOT_I$KOP %>% as.character() %>% replace_na(0)\n\n#Creating Main Kickoff tracking data set \ndf_KOT<- df_KOT_I %>% \n  \n  #Mutating Kickoff Position to add unique level of Kick Returner\n  mutate(KOP = replace(KOP,nflId==returnerId,\"KR\")) %>%\n  \n  #Filtering out the football from the tracking data and arrange by position \n  filter(., displayName != \"football\") %>% arrange(.,a_pos) %>% \n  \n  #Renaming the key variables that were altered by the table join\n  rename(.,UPI = UPI.x ) %>% \n  \n  #Then grouping by each unique frame in each unique play\n  group_by(UPI) %>%\n  \n  #Then creating the distance that each player/observation is from the Kick Returner and ungroup\n  mutate(Euclidean = as.matrix(dist(cbind(x, y)))[1, ]) %>% ungroup()\n\n#Creating cleaning data set to fill in Missing Kick returner position values by proximity to KR\ndf_clean <- df_KOT %>%\n  \n  #Filtering for absent Position values\n  filter(., KOP == \"0\") %>% \n  \n  #Selecting only variables needed to fill KOP values \n  select(., x:dir,nflId,frameId:playDirection,kickReturnYardage:a_pos,KOP:Euclidean) %>%\n  \n  #Arrange by the Euclidean values \n  arrange(Euclidean) %>% \n  \n  #Group by each play each unique play \n  group_by(UPI) %>% \n  \n  #Ranking by each distance for each play to determine the position from the KR\n  mutate(KOP = rank(Euclidean)) %>%\n  \n  #Filtering out plays where data had a typo and didn't follow previous code\n  filter(., KOP < 11) %>% filter(., Euclidean > 0.0) %>%\n  \n  #Selecting Kick Return Euclidean Position and unique Identifier\n  select(., UKI, KOP)\n\n#Creating Merge data set to combine all values of Kickoff Position into one column \ndf_KRKO <- df_KOT %>% left_join(df_clean, by=\"UKI\")%>% \n  \n  #Identifying NAs in original KOP column to show where df_clean$KOP values will go\n  mutate(KOP.x = replace(KOP.x, KOP.x==0, NA)) %>% \n  \n  #Defining new Total KOP (TKOP), that uses both KOP columns to create the complete column\n  transform(., TKOP = ifelse(!is.na(KOP.x), KOP.x, KOP.y)) %>%\n  \n  #Selecting variables of interest\n  select(., x:dir,frameId:playDirection,kickReturnYardage:UPI.x,hangTime:kickoffReturnFormation, Teams:a_pos,Euclidean,TKOP) %>% \n  \n  #Filter out any plays where TKOP could not be identified\n  filter(., TKOP != \"NA\") %>% \n  \n  #Rename variables that were altered by the table join\n  rename(.,UPI = UPI.x )%>%\n  \n  #Arranging the data set by Euclidean Distances\n  arrange(RecTeam,Euclidean) %>%\n  \n  #Grouping by \n  group_by(UPI) %>% \n  \n  #Creating Unique Ident variable for only the Kicking team based on Euclidean Position from the KR \n  mutate(UNI_KICK = row_number()) %>% \n  \n  #Getting rid of play that didn't sort correctly \n  filter(.,UNI_KICK < 23) %>% ungroup() %>%\n  \n  #Group by unique play id, and filtering the lower bound to make sure every play has exactly 22 obs.\n  group_by(UPI) %>% filter(n()>20) %>%\n  \n  #arrange by UNI_KICK\n  arrange(UNI_KICK) %>%\n\n  #Creating Total Euclidean for Kick Team\n  mutate(KTE = sum(Euclidean[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(KTC2 = sum(Euclidean[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(KTC3T6 = sum(Euclidean[UNI_KICK[c(3:6)]])) %>% \n  \n    #Creating Total Euclidean for Return Team\n  mutate(RTE = sum(Euclidean[UNI_KICK[c(12:22)]]))  %>% \n  \n  #Creating Total Euclidean for Return Team (First 2 closest players, EXCLUDING KR)\n  mutate(RTC2 = sum(Euclidean[UNI_KICK[c(13:14)]])) %>%\n  \n   #Creating Total Euclidean for Return Team (3-7th closest players, EXCLUDING KR)\n  mutate(RTC3T6 = sum(Euclidean[UNI_KICK[c(15:18)]])) %>% \n  \n   #Creating Mean Euclidean for Kick Team\n  mutate(KME = mean(Euclidean[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(KMC2 = mean(Euclidean[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(KMC3T6 = mean(Euclidean[UNI_KICK[c(3:6)]])) %>% \n  \n    #Creating Mean Euclidean for Kick Team\n  mutate(RME = mean(Euclidean[UNI_KICK[c(12:22)]]))  %>% \n  \n  #Creating Mean Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(RMC2 = mean(Euclidean[UNI_KICK[c(13:14)]])) %>%\n  \n   #Creating Mean Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(RMC3T6 = mean(Euclidean[UNI_KICK[c(15:18)]])) %>% \n  \n    #Creating sd Euclidean for Kick Team\n  mutate(KSDE = sd(Euclidean[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(KSDC2 = sd(Euclidean[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(KSDC3T6 = sd(Euclidean[UNI_KICK[c(3:6)]])) %>% \n  \n    #Creating sd Euclidean for Kick Team\n  mutate(RSDE = sd(Euclidean[UNI_KICK[c(12:22)]]))  %>% \n  \n  #Creating sd Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(RSDC2 = sd(Euclidean[UNI_KICK[c(13:14)]])) %>%\n  \n   #Creating sd Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(RSD3T6 = sd(Euclidean[UNI_KICK[c(15:18)]])) %>% \n\n#Creating Total a for Kick Team\n  mutate(AccKTE = sum(a[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total a for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(AccKTC2 = sum(a[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total a for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(AccKTC3T6 = sum(a[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean a for Kick Team\n  mutate(AccKME = mean(a[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean a for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(AccKMC2 = mean(a[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean a for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(AccKMC3T6 = mean(a[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd a for Kick Team\n  mutate(AccKSDE = sd(a[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd a for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(AccKSDC2 = sd(a[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd a for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(AccKSDC3T6 = sd(a[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Total s for Kick Team\n  mutate(SPKTE = sum(s[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total s for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(SPKTC2 = sum(s[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total s for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(SPKTC3T6 = sum(s[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean s for Kick Team\n  mutate(SPKME = mean(s[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean s for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(SPKMC2 = mean(s[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean s for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(SPKMC3T6 = mean(s[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd s for Kick Team\n  mutate(SPKSDE = sd(s[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd s for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(SPKSDC2 = sd(s[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd s for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(SPKSDC3T6 = sd(s[UNI_KICK[c(3:6)]])) %>% \n    \n    #Creating Total dir for Kick Team\n  mutate(DirKTE = sum(dir[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total dir for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(DirKTC2 = sum(dir[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total dir for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(DirKTC3T6 = sum(dir[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean dir for Kick Team\n  mutate(DirKME = mean(dir[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean dir for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(DirKMC2 = mean(dir[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean dir for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(DirKMC3T6 = mean(dir[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd dir for Kick Team\n  mutate(DirKSDE = sd(dir[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd dir for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(DirKSDC2 = sd(dir[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd dir for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(DirKSDC3T6 = sd(dir[UNI_KICK[c(3:6)]])) %>% \n    \n    #Creating Total o for Kick Team\n  mutate(oKTE = sum(o[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total o for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(oKTC2 = sum(o[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total o for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(oKTC3T6 = sum(o[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean o for Kick Team\n  mutate(oKME = mean(o[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean o for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(oKMC2 = mean(o[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean o for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(oKMC3T6 = mean(o[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd o for Kick Team\n  mutate(oKSDE = sd(o[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd o for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(oKSDC2 = sd(o[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd o for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(oKSDC3T6 = sd(o[UNI_KICK[c(3:6)]]))  %>%\n  \n  #Getting Rid of non-important variables \n  select(., -(assistTackler:Teams)) %>% \n  \n  #Seperating out list of missed tacklers into a separate column for each missed tackle\n separate(missedTackler,into = c(\"MT1\",\"MT2\",\"MT3\",\"MT4\"),sep = \";\") %>% \n  \n  #Replace missing values in missed tackle columns and Penalty yards column\n  replace_na(list(MT1 = 0, MT2 =0, MT3 = 0, MT4 = 0, penaltyYards = 0)) %>%\n  \n  #Binary Encoding the Missed Tackle Columns to show just the prescence of the ith missed tackle\n  mutate(NMT1 = ifelse(MT1 == 0 ,0,1)) %>% mutate(NMT2 = ifelse(MT2 == 0 ,0,1)) %>%\n  mutate(NMT3 = ifelse(MT3 == 0 ,0,1)) %>% mutate(NMT4 = ifelse(MT4 == 0 ,0,1)) %>%\n  \n#Add together the binary values of each missed tackle across the rows to get total # of missed tackles on each play, labeled Total Missed Tackles (TMT)\n  mutate(TMT = NMT1+NMT2+NMT3+NMT4) %>%\n  \n  #Take out columns of non-importance at this point \n  dplyr::select(., - (MT1:MT4),-(NMT1:NMT4)) %>%\n  \n  #exclude any NA values\n  na.exclude() %>% \n  \n  #filter for only the KR\n  filter(., a_pos==\"AKR\") %>% \n  \n  #Getting rid of variables not needed \n  select(., -dis,-(frameId:playId), -(UKI:UPI), -(RecTeam:UNI_KICK), -playDirection, -(kickType:returnDirectionIntended))  %>% \n  \n  #Creating dummy column for categortical return direction actual\n  dummy_columns(., select_columns = \"returnDirectionActual\") %>% \n  \n  #Delete the categorical variable from the data \n  select(., -returnDirectionActual)\n\n# Creating data set with Kickoff position and Euclidean distances \nKOEUC <- df_K %>% \n  \n  #Left join with the point in the data set from before thats ideal to build on\n  left_join(df_FDS, by = \"UKI\") %>% \n  \n  #filter at the moment of the KR receiving the ball\n  filter(., event == \"kick_received\") \n\n#adjust kickoff position as character variable, and replace NAs with 0 \nKOEUC$KOP <- KOEUC$KOP %>% as.character() %>% replace_na(0)\n\n#Adjusting KOEUC to prepare for pivot\nAKOEUC <- KOEUC %>% \n  \n  #Set the KR was a unqiue individual on the return team\n  mutate(KOP = replace(KOP,nflId==returnerId,\"KR\")) %>%\n  \n  #filter out the football from the data and arrange by the KR first \n  filter(., displayName != \"football\") %>% arrange(.,a_pos) %>% \n  \n  #Rename a variable that was change in the table join \n   rename(.,UPI = UPI.x ) %>% \n  \n  #Then grouping by each unique UPI \n  group_by(UPI) %>%\n  \n  #Then creating the distance that each player/observation is from the Kick Returner\n   mutate(Euclidean = as.matrix(dist(cbind(x, y)))[1, ]) %>% ungroup()\n\n#Create another table to merge with AKOEUC called NFD\nNFD <- AKOEUC %>% \n  \n  #filter for every player on the return team besides the KR\n  filter(., KOP == \"0\") %>% \n  \n  #select variables of interest \n  select(., x:dir,nflId,frameId:playDirection,kickReturnYardage:a_pos,KOP:Euclidean) %>%\n  \n  #arrange by the Euclidean\n  arrange(Euclidean) %>% \n  \n  #Group by UPI\n  group_by(UPI) %>%  \n  \n  #Ranking by each distance for each play to determine the position from the KR\n  mutate(KOP = rank(Euclidean)) %>%\n  \n  #filter for any outlier or mistake in the code\n  filter(., KOP < 11) %>%  filter(., Euclidean > 0.0) %>%\n  \n  #select the new filled KOP variable \n  select(., UKI, KOP) \n\n#Data set to combine both data conditions \nKOPDS <- AKOEUC %>% \n  \n  #Merge filled return tea data set with filled kicking team data set \n  left_join(NFD, by=\"UKI\")%>%\n  \n  #replace 0s with NA in original kickoff position \n  mutate(KOP.x = replace(KOP.x, KOP.x==0, NA)) %>% \n  \n   #merge the two columns who have complementary missing values \n  transform(., TKOP = ifelse(!is.na(KOP.x), KOP.x, KOP.y)) %>% \n  \n  #select variables of interest \n  select(., x:dir,frameId:playDirection,kickReturnYardage:a_pos,Euclidean,TKOP) %>% \n  \n  #filter out any missing value of Kickoff position\n  filter(., TKOP != \"NA\") %>% \n  \n  #Rename variables that were changed when the tables joing \n  rename(.,UPI = UPI.x ) %>%\n  \n  #filter by unique play and only include plays with correct amount of players \n  group_by(UPI) %>% filter(n()==22)\n\n#pivot wider for specific values based on Kickoff Position \n\n#Euclidean table\nTA <- KOPDS %>% pivot_wider(.,id_cols = UPI, names_from = TKOP  , names_prefix = \"Euc\", values_from = Euclidean) %>% na.exclude()\n\n#Speed table \nTB <- KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"Sp\", values_from = s) %>% na.exclude()\n\n#Acceleration table \nTC <- KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"Acc\", values_from = a) %>% na.exclude()\n\n#Orientation table\nTD <- KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"Ori\", values_from = o) %>% na.exclude()\n\n# Data frame together (DFT), left join every table to matching UPI value \n#DFT <- TA %>% left_join(TB) %>% left_join(TC) %>% left_join(TD) %>% left_join(TE)  %>% \n  #left_join(TF) %>% left_join(TG) %>% left_join(TH)\n\nDFT <- TA %>% left_join(TB) %>% left_join(TC) %>% left_join(TD) \n\n#Merge DFT with Euclidean values\ndf_NT <- df_KRKO %>% left_join(DFT, by=\"UPI\") %>% \n  \n  #delete UPI and an empty value from the data set\n  select(.,-UPI, -EucKR)\n\n#Getting linear dependence \nlc <- findLinearCombos(df_NT)\n\n#data from without linear dependence \ndf_WC <- df_NT[, -lc$remove] \n  #essentially just removed the variables that represented the mean\n\n# Filtering out correlated variables \ndescrCor <-  cor(df_WC)\nhighCorr <- sum(abs(descrCor[upper.tri(descrCor)]) > .999)\nhighlyCorDescr <- findCorrelation(descrCor, cutoff = .75)\ndf_WC <- df_WC[,-highlyCorDescr]\ndescrCor2 <- cor(df_WC)\n\n#Transforming main data set\npreProcValues2 <- preProcess(df_NT, method = c(\"center\",\"scale\",\"YeoJohnson\"))\ntrain_d <- predict(preProcValues2, df_NT)\n\npreProcValues3 <- preProcess(df_WC, method = c(\"center\",\"scale\",\"YeoJohnson\"))\ntrain_d1 <- predict(preProcValues3, df_WC)\n\n#Creating subset of less variables \ntrain_pos <- train_d %>% select(., -(KTE:oKSDC3T6))\n\n#Creating even smaller subset\nsb2 <- train_d %>% select(., -Euc1,-Euc2,-Euc3,-Euc4,-Euc5,-Euc6,-Euc7,-Euc8,-Euc9,-Euc10,-Acc1,-Acc2,-Acc3,-Acc4,-Acc5,-Acc6,-Acc7,-Acc8,-Acc9,-Acc10,-Sp1,-Sp2,-Sp3,-Sp4,-Sp5,-Sp6,-Sp7,-Sp8,-Sp9,-Sp10)\n","metadata":{"execution":{"iopub.status.busy":"2022-01-11T02:26:14.338946Z","iopub.execute_input":"2022-01-11T02:26:14.370492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Show main inital data set:\nhead(train_d)\n#Show first main subset\nhead(train_pos)\n#Show final main data set\nhead(sb2)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The models tended to perform better when the data was centered, scaled, and transformed using YeoJohnson’s transformations. ","metadata":{}},{"cell_type":"markdown","source":"2b. Models\n\tThere were two rounds of model implementation. The first round contained a wide variety of models, that were all tuned a similar way using the caret package. These models include:\n    \n1.\tNeural Networks\n2.\tGlmnet (Lasso and elastic nnet regression)\n3.\tElastic Net regression \n4.\tRidge regression\n5.\tRandom forests\n\nCountless models were attempted with a variety of transformations and variable contents. By far the best performing model came from the variables mentioned above along with variables representing the speed, acceleration, and orientation of every member on the field. The resulting subset of variables above was the resulting model that was subset as small as possible, while still retaining almost all the model accuracy. The most practical model was found to be a random forest model with a R^2 = 0.2763, as it only contained 23 predictors, compared to the ~ 100 predictors for the remaining models. The highest R^2 observed was an elastic net regression with an R^2 = 0.3. Every model was assessed with 10-fold cross validation, with 3 repeats. Those following models are presented below:\n","metadata":{}},{"cell_type":"markdown","source":"**NOTE: ONLY MODELS FOR FINAL SUBSET INCLUDED DUE TO LOADING ERROR IN KAGGLE**","metadata":{}},{"cell_type":"code","source":"#control function \nctrl <- rfeControl(functions = rfFuncs,\n                   method = \"cv\",\n                   verbose = FALSE)\n#Models\n\n#Setting up parameter tuning \nfitControl <- trainControl(## 10-fold CV\n                           method = \"repeatedcv\",\n                           number = 10,\n                           repeats = 3)\n#Feature selection with random forest\nlmProfile6 <- rfe(x=train_pos[,-7], y=train_pos$kickReturnYardage,rfeControl = ctrl)\nlmProfile7 <- rfe(x=train_d[,-7], y=train_d$kickReturnYardage,rfeControl = ctrl)\nlmProfile9<- rfe(x=sb2[,-7], y=sb2$kickReturnYardage,rfeControl = ctrl)\n#Random Forest models for both main data \nRFM6 <- train(kickReturnYardage~., data = train_pos, method = \"rf\", trControl = fitControl)\nRFM7<- train(kickReturnYardage~., data = train_d, method = \"rf\", trControl = fitCont\nRFM6\nRFM7\n\nRFM11<- train(kickReturnYardage~KTC2+KMC2+EucL5+RMC2+RTC2+EucR1+Ori10+EucL3+TMT+RSDE+EucL2+returnDirectionActual_C+x+KME+EucR3+KMC3T6+KTC3T6+EucR4+EucR5, data = sb2, method = \"rf\", trControl = fitControl)\nRFM11\n\nset.seed(163)\nRFK1<- randomForest(kickReturnYardage~KTC2+KMC2+EucL5+RMC2+RTC2+EucR1+EucL3+TMT+RSDE+EucL2+x+KME+EucR3+KMC3T6+KTC3T6+EucR4+EucR5+EucR2+EucL1+EucL4+EucK+AccKME+returnDirectionActual_C, data = sb2, mtry=2)\n# train_d \nNN_M5 <- train(kickReturnYardage~., data = train_pos, \n               method = \"nnet\", trControl = fitControl,\n               linout = TRUE)\nNN_M5\n# train_d \nGNET_M5 <- train(kickReturnYardage~., data = train_pos, \n               method = \"glmnet\", trControl = fitControl)\nGNET_M5\n# train_d \nEN_M5 <- train(kickReturnYardage~., data = train_pos, \n               method = \"enet\", trControl = fitControl,\n               linout = TRUE)\nEN_M5\n# train_d \nRD_M5 <- train(kickReturnYardage~., data = train_pos, \n               method = \"ridge\", trControl = fitControl)\nRD_M5","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Creating Prediction Data for all data sets \ndf_p <- df_tracking_1\ndf_tracking_N <- df_p %>%\n                mutate(x = ifelse(playDirection == \"left\", 120-x, x),\n                       y = ifelse(playDirection == \"left\", 160/3 - y, y))\n\n#First Data Set\n\n#creating data frame that will only includes Kick Returns that were actually returned. \nPRED_df_first_ko <- df_tracking_N %>%\n    #joining the scouting data\n    left_join(df_plays, by = c(\"gameId\", \"playId\")) %>%\n    #filtering for kickoff plays only\n    filter(specialTeamsPlayType == \"Kickoff\") %>%\n  #filtering for only kicks that were returned\n  filter(kickReturnYardage != \"NA\") %>%\n  #Subsetting desireable variables\n  select(., time:playDirection, possessionTeam:kickBlockerId,kickReturnYardage)\n\n#Creating a new variable where I change a categorical level of the position variable to differentiate the kick returner from the rest (The \"A\" prefix was added to be able to run code later that matches the positioning of the variable)\nPRED_df_first_ko_1 <- PRED_df_first_ko %>% group_by(gameId,playId,frameId) %>%  mutate(a_pos = replace(position,nflId==returnerId,\"AKR\"))\n\n#Setting aside this separate data set for a later step\nPRED_df_Kickoffs_A <- PRED_df_first_ko_1 %>% select(., -time,-(nflId:team),-(gameId:a_pos)) %>% filter(.,event==\"tackle\")\n\n#Arranging the data by the new position variable, so kick returner is at the top \nPRED_df_first_ko_2 <- PRED_df_first_ko_1 %>% arrange(.,a_pos) %>% \n  #Then grouping by each unique frame in each unique play\n  group_by(gameId,playId,frameId) %>%\n  #Then creating the distance that each player/observation is from the Kick Returner\n   mutate(Euclidean = as.matrix(dist(cbind(x, y)))[1, ]) %>%\n  #ungrouping the data\n  ungroup()\n\n#Data set for later\n PRED_df_ko_rf_a <- PRED_df_first_ko_2\n\n#Grouping the data again by each unqiue frame\nPRED_df_first_ko_3 <- PRED_df_first_ko_2 %>% group_by(gameId,playId,frameId) %>%\n  #Creating a unique ID for every other player in each frame (including football =23)\n  mutate(UniPlayInd = row_number()) %>%\n  ungroup()\n\n#Matching each unique distance to its unique frameId and relative player relationship\nPRED_TestEuc<- PRED_df_first_ko_3 %>% pivot_wider(.,id_cols = c(gameId,playId,frameId,UniPlayInd), names_from = UniPlayInd, names_prefix = \"Euc\", values_from = Euclidean)\n\n#Converting Home/Away to Binary\n  #Might need to change method of encoding teams\nPRED_df_first_ko_3$HomeTrue <- PRED_df_first_ko_3$team == \"home\"\nPRED_df_first_ko_3$HomeTrue <- as.integer(PRED_df_first_ko_3$HomeTrue)\n\n#Home/away Hot encode\nPRED_Home_Away_Table <- PRED_df_first_ko_3 %>% pivot_wider(.,id_cols = c(gameId,playId,frameId,UniPlayInd), names_from = UniPlayInd, names_prefix = \"Home\", values_from = HomeTrue)\n\n#Combining tables \n PRED_df_first_ko_4 <- PRED_df_first_ko_3 %>% left_join(.,PRED_TestEuc, by = c(\"gameId\",\"playId\",\"frameId\")) %>% left_join(.,PRED_Home_Away_Table, by = c(\"gameId\",\"playId\",\"frameId\"))\n \n\n # Converting None to NA for events in Kickoff Data set\nPRED_df_first_ko_4$event<- recode_factor(PRED_df_first_ko_4$event, None = NA_character_)\nPRED_df_first_ko_4$event<- recode_factor(PRED_df_first_ko_4$event, kickoff = NA_character_)\n \n\n# Filtering out all other observation besides Kick Returners\n PRED_df_first_ko_5 <- PRED_df_first_ko_4 %>% filter(., a_pos == \"AKR\") %>%\n \n #Cleaning up unnessecary columns:\n select(., -time, -(displayName:team),- (playDirection:specialTeamsResult),-(returnerId:kickBlockerId), -(a_pos:Euc1)) %>% \n   #Filtering to only have the moment the KR catches the ball.\n   filter(., event ==\"kick_received\") \n \n#Data set 2 \n \n#setting up team specific data sets\nPRED_DS_KO_2 <- PRED_df_ko_rf_a %>% \n  \n  #Join with games data set\n  inner_join(df_games)  %>% \n  \n  #Put specific team names for each player based off home/away\n  mutate(Teams = ifelse(team==\"home\",homeTeamAbbr,visitorTeamAbbr)) %>% \n  \n  #Creating variable to show what team is recieving the the ball during the KO\n  mutate(RecTeam = if_else(possessionTeam==Teams,\"Kick\",\"Rec\")) %>% \n  \n  #filter for only observations with the kicking team AND the KR\n  filter(.,RecTeam == \"Kick\" | a_pos == \"AKR\") %>% \n  \n  #Arrange by Euclidean\n  arrange(.,Euclidean)  %>% \n  \n  #Delete the football from the observations \n  filter(., displayName != \"football\") %>% \n  \n  #Subset every play to the moment the KR receives the ball\n  filter(., event ==\"kick_received\") %>%\n  \n  #Select Variables of Importance\n  select(., -time,-event,-(displayName:team),-(playDirection:kickBlockerId),-(season:Teams)) %>%\n  \n  #Group by gameId and playId\n  group_by(gameId,playId) %>% \n  \n  #Create a Unique Identifier for players on the kicking team (and KR)\n  mutate(UniPlayInd = row_number()) %>% filter(., UniPlayInd < 13 )\n\n#Create dummy tables \n\n#Euclidean Table\nPRED_XB <- PRED_DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Euc\", values_from = Euclidean)\n\n#Speed Table\nPRED_XC <- PRED_DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Speed\", values_from = s)\n\n#Acceleration Table\nPRED_XD <- PRED_DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Acc\", values_from = a)\n \n#Orientation Table\nPRED_XE <- PRED_DS_KO_2 %>% pivot_wider(.,id_cols = c(gameId,playId), names_from = UniPlayInd  , names_prefix = \"Ori\", values_from = o)\n\n#Subsetting main data to only have the observation focused on the KR\nPRED_XA <- subset(PRED_DS_KO_2, a_pos == \"AKR\")\n\n#Creating Full Team Data (FT)\nPRED_FT <- PRED_XA %>% \n  \n  #left join all the dummy tables \n  left_join(PRED_XB) %>% left_join(PRED_XC) %>% left_join(PRED_XD) %>% left_join(PRED_XE) %>% \n  \n  #Join with PFF scouting data set\n  left_join(df_PFFScouting) %>% \n  \n  #Select only variables of interest \n  select(., -(nflId:playId),-(a_pos:Euc1),-Speed1,-Acc1,-Ori1,-(snapDetail:operationTime),-(kickDirectionIntended:kickDirectionActual),-(missedTackler:tackler),-(gunners:kickContactType)) %>% \n  \n  #Filter out missing values of hangtime and observations with no KR yards\n  filter(., hangTime != \"NA\") %>% filter(.,kickReturnYardage != \"NA\")\n\n#Delete all NAs from the Data set\nPRED_FT<- na.exclude(PRED_FT)\n\n\n#Third data set\n#creating data frame that will only includes Kick Returns that were actually returned. \nPRED_df_K <- df_tracking_N %>%\n  \n    #joining the scouting data\n    left_join(df_plays, by = c(\"gameId\", \"playId\")) %>%\n  \n    #filtering for kickoff plays only\n    filter(specialTeamsPlayType == \"Kickoff\") %>%\n  \n  #filtering for only kicks that were returned\n  filter(kickReturnYardage != \"NA\") %>%\n  \n  #Subsetting desireable variables\n  select(., time:playDirection, possessionTeam:kickBlockerId,kickReturnYardage) %>% \n  \n  #Creating Unique Indefintifer based off nflId, gameId, and playId\n  mutate(UKI = unique_identifier(., fields = c(\"nflId\", \"gameId\",\"playId\"))) %>% \n  \n  #Create Unique Identifier for each unique play across all games and seasons\n  mutate(UPI = unique_identifier(., fields = c(\"gameId\",\"playId\"))) %>%\n  \n  #Joining with PFF scouting data table\n  left_join(df_PFFScouting) %>%\n  \n  #joining data with games data set\n  inner_join(df_games)  %>% \n\n  #Sorting teams by home and away for each game into one column\n  mutate(Teams = ifelse(team==\"home\",homeTeamAbbr,visitorTeamAbbr)) %>% \n  \n  #Sorting by which team is the Kicking and Receiving team for each game \n  mutate(RecTeam = if_else(possessionTeam==Teams,\"Kick\",\"Rec\")) %>% \n\n  #Creating a new variable where I change a categorical level of the position variable to differentiate the kick returner from the rest (The \"A\" prefix was added to be able to run code later that matches the positioning of the variable)\n  group_by(UPI,frameId) %>%  mutate(a_pos = replace(position,nflId==returnerId,\"AKR\")) \n\n#Head of df_K\nhead(PRED_df_K)\n\n\n#Arranging the data by the new position variable, so kick returner is at the top \nPRED_df_KOP <- PRED_df_K %>% arrange(.,a_pos) %>% \n  \n  #Subsetting the data to the moment the Kick Returner receives the kick\n filter(., event == \"kickoff\")   %>% \n  \n  #Subset to only include the Kicking Team\n  filter(.,RecTeam == \"Kick\") %>% \n  \n  #Creating categorical variable to differentiate between Kicker and non-kicker\n  mutate(KickerYN = ifelse(nflId==kickerId,\"A\",\"B\")) %>% \n  \n  #Cleaning up errors in the data to ensure only players in KickerYN level A , is a kicker\n  filter(., returnerId != \"NA\") %>%\n  \n  #Arrange with the kickers at the top\n  arrange(., KickerYN) %>% \n  \n  #Reset group and filter at the point of the kickoff and subset by each unique play \n   group_by(UPI) %>% \n  \n  #Creating a variable to find the distance from the kicker\n  mutate(DistKick = y - first(y)) %>%  \n  \n  #ungrouping the data and filter to delete the football out of the data \n  ungroup () %>% filter(., displayName != \"football\") %>%\n  \n  #Arrange by the Distance Kick\n  arrange(DistKick) %>% \n  \n  #Group by each play each unique play \n  group_by(UPI)  %>%  \n  \n  #Ranking by each distance for each play to determine the position from the kicker, setting up KOP (KOP = Kickoff position)\n  mutate(KOP = rank(DistKick)) %>% \n  \n  #Recoding the factors to match the position on the kickoff \n  mutate(KOP = as.factor(KOP), KOP = fct_recode(KOP, \"L1\" =\"1\", \"L2\"=\"2\",\"L3\"=\"3\",\"L4\"=\"4\",\"L5\"=\"5\", \"K\"=\"6\",\"R5\"=\"7\",\"R4\"=\"8\",\"R3\"=\"9\",\"R2\"=\"10\",\"R1\"=\"11\")) \n\n#Selecting the unique identifier and the Kickoff Position, and ungrouping the data\nPRED_df_KOP <- PRED_df_KOP %>% select(., UKI, KOP)  %>% ungroup()\n\n#head df_KOP\nhead(PRED_df_KOP)\n\n#Create data set for later use \nPRED_df_FDS <- PRED_df_KOP\n\n#Setting up main kickoff tracking dataset \n# Creating Inital Kickoff Tracking (KOT_I) by joining main tracking and Kickoff Position by unique identifier \nPRED_df_KOT_I <- PRED_df_K %>% left_join(PRED_df_KOP, by = \"UKI\") %>%\n  \n  #Filter by the moment the kick was receieved by the kick returner\n  filter(., event == \"kick_received\") \n\n#Converting kickoff position to character and replacing NAs with 0s\nPRED_df_KOT_I$KOP <- PRED_df_KOT_I$KOP %>% as.character() %>% replace_na(0)\n\n#head df_KOT_I\nhead(PRED_df_KOT_I)\n\n#Creating Main Kickoff tracking data set \nPRED_df_KOT<- PRED_df_KOT_I %>% \n  \n  #Mutating Kickoff Position to add unique level of Kick Returner\n  mutate(KOP = replace(KOP,nflId==returnerId,\"KR\")) %>%\n  \n  #Filtering out the football from the tracking data and arrange by position \n  filter(., displayName != \"football\") %>% arrange(.,a_pos) %>% \n  \n  #Renaming the key variables that were altered by the table join\n  rename(.,UPI = UPI.x ) %>% \n  \n  #Then grouping by each unique frame in each unique play\n  group_by(UPI) %>%\n  \n  #Then creating the distance that each player/observation is from the Kick Returner and ungroup\n  mutate(Euclidean = as.matrix(dist(cbind(x, y)))[1, ]) %>% ungroup()\n\n#head of df_KOT\nhead(PRED_df_KOT)\n\n#Creating cleaning data set to fill in Missing Kick returner position values by proximity to KR\nPRED_df_clean <- PRED_df_KOT %>%\n  \n  #Filtering for absent Position values\n  filter(., KOP == \"0\") %>% \n  \n  #Selecting only variables needed to fill KOP values \n  select(., x:dir,nflId,frameId:playDirection,kickReturnYardage:a_pos,KOP:Euclidean) %>%\n  \n  #Arrange by the Euclidean values \n  arrange(Euclidean) %>% \n  \n  #Group by each play each unique play \n  group_by(UPI) %>% \n  \n  #Ranking by each distance for each play to determine the position from the KR\n  mutate(KOP = rank(Euclidean)) %>%\n  \n  #Filtering out plays where data had a typo and didn't follow previous code\n  filter(., KOP < 11) %>% filter(., Euclidean > 0.0) %>%\n  \n  #Selecting Kick Return Euclidean Position and unique Identifier\n  select(., UKI, KOP)\n\n #print df_clean\n head(PRED_df_clean)\n\n#Creating Merge data set to combine all values of Kickoff Position into one column \nPRED_df_KRKO <- PRED_df_KOT %>% left_join(PRED_df_clean, by=\"UKI\")%>% \n  \n  #Identifying NAs in original KOP column to show where df_clean$KOP values will go\n  mutate(KOP.x = replace(KOP.x, KOP.x==0, NA)) %>% \n  \n  #Defining new Total KOP (TKOP), that uses both KOP columns to create the complete column\n  transform(., TKOP = ifelse(!is.na(KOP.x), KOP.x, KOP.y)) %>%\n  \n  #Selecting variables of interest\n  select(., x:dir,frameId:playDirection,kickReturnYardage:UPI.x,hangTime:kickoffReturnFormation, Teams:a_pos,Euclidean,TKOP) %>% \n  \n  #Filter out any plays where TKOP could not be identified\n  filter(., TKOP != \"NA\") %>% \n  \n  #Rename variables that were altered by the table join\n  rename(.,UPI = UPI.x )%>%\n  \n  #Arranging the data set by Euclidean Distances\n  arrange(RecTeam,Euclidean) %>%\n  \n  #Grouping by \n  group_by(UPI) %>% \n  \n  #Creating Unique Ident variable for only the Kicking team based on Euclidean Position from the KR \n  mutate(UNI_KICK = row_number()) %>% \n  \n  #Getting rid of play that didn't sort correctly \n  filter(.,UNI_KICK < 23) %>% ungroup() %>%\n  \n  #Group by unique play id, and filtering the lower bound to make sure every play has exactly 22 obs.\n  group_by(UPI) %>% filter(n()>20) %>%\n  \n  #arrange by UNI_KICK\n  arrange(UNI_KICK) %>%\n\n  #Creating Total Euclidean for Kick Team\n  mutate(KTE = sum(Euclidean[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(KTC2 = sum(Euclidean[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(KTC3T6 = sum(Euclidean[UNI_KICK[c(3:6)]])) %>% \n  \n    #Creating Total Euclidean for Return Team\n  mutate(RTE = sum(Euclidean[UNI_KICK[c(12:22)]]))  %>% \n  \n  #Creating Total Euclidean for Return Team (First 2 closest players, EXCLUDING KR)\n  mutate(RTC2 = sum(Euclidean[UNI_KICK[c(13:14)]])) %>%\n  \n   #Creating Total Euclidean for Return Team (3-7th closest players, EXCLUDING KR)\n  mutate(RTC3T6 = sum(Euclidean[UNI_KICK[c(15:18)]])) %>% \n  \n   #Creating Mean Euclidean for Kick Team\n  mutate(KME = mean(Euclidean[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(KMC2 = mean(Euclidean[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(KMC3T6 = mean(Euclidean[UNI_KICK[c(3:6)]])) %>% \n  \n    #Creating Mean Euclidean for Kick Team\n  mutate(RME = mean(Euclidean[UNI_KICK[c(12:22)]]))  %>% \n  \n  #Creating Mean Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(RMC2 = mean(Euclidean[UNI_KICK[c(13:14)]])) %>%\n  \n   #Creating Mean Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(RMC3T6 = mean(Euclidean[UNI_KICK[c(15:18)]])) %>% \n  \n    #Creating sd Euclidean for Kick Team\n  mutate(KSDE = sd(Euclidean[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(KSDC2 = sd(Euclidean[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(KSDC3T6 = sd(Euclidean[UNI_KICK[c(3:6)]])) %>% \n  \n    #Creating sd Euclidean for Kick Team\n  mutate(RSDE = sd(Euclidean[UNI_KICK[c(12:22)]]))  %>% \n  \n  #Creating sd Euclidean for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(RSDC2 = sd(Euclidean[UNI_KICK[c(13:14)]])) %>%\n  \n   #Creating sd Euclidean for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(RSD3T6 = sd(Euclidean[UNI_KICK[c(15:18)]])) %>% \n\n#Creating Total a for Kick Team\n  mutate(AccKTE = sum(a[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total a for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(AccKTC2 = sum(a[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total a for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(AccKTC3T6 = sum(a[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean a for Kick Team\n  mutate(AccKME = mean(a[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean a for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(AccKMC2 = mean(a[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean a for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(AccKMC3T6 = mean(a[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd a for Kick Team\n  mutate(AccKSDE = sd(a[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd a for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(AccKSDC2 = sd(a[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd a for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(AccKSDC3T6 = sd(a[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Total s for Kick Team\n  mutate(SPKTE = sum(s[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total s for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(SPKTC2 = sum(s[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total s for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(SPKTC3T6 = sum(s[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean s for Kick Team\n  mutate(SPKME = mean(s[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean s for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(SPKMC2 = mean(s[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean s for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(SPKMC3T6 = mean(s[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd s for Kick Team\n  mutate(SPKSDE = sd(s[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd s for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(SPKSDC2 = sd(s[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd s for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(SPKSDC3T6 = sd(s[UNI_KICK[c(3:6)]])) %>% \n    \n    #Creating Total dir for Kick Team\n  mutate(DirKTE = sum(dir[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total dir for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(DirKTC2 = sum(dir[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total dir for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(DirKTC3T6 = sum(dir[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean dir for Kick Team\n  mutate(DirKME = mean(dir[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean dir for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(DirKMC2 = mean(dir[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean dir for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(DirKMC3T6 = mean(dir[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd dir for Kick Team\n  mutate(DirKSDE = sd(dir[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd dir for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(DirKSDC2 = sd(dir[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd dir for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(DirKSDC3T6 = sd(dir[UNI_KICK[c(3:6)]])) %>% \n    \n    #Creating Total o for Kick Team\n  mutate(oKTE = sum(o[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Total o for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(oKTC2 = sum(o[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Total o for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(oKTC3T6 = sum(o[UNI_KICK[c(3:6)]])) %>% \n  \n#Creating Mean o for Kick Team\n  mutate(oKME = mean(o[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating Mean o for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(oKMC2 = mean(o[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating Mean o for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(oKMC3T6 = mean(o[UNI_KICK[c(3:6)]])) %>%\n\n  #Creating sd o for Kick Team\n  mutate(oKSDE = sd(o[UNI_KICK[c(1:11)]]))  %>% \n  \n  #Creating sd o for Kick Team (First 2 closest players, EXCLUDING KR)\n  mutate(oKSDC2 = sd(o[UNI_KICK[c(1:2)]])) %>%\n  \n   #Creating sd o for Kick Team (3-7th closest players, EXCLUDING KR)\n  mutate(oKSDC3T6 = sd(o[UNI_KICK[c(3:6)]]))  %>%\n  \n  \n  #Getting Rid of non-important variables \n  select(., -(assistTackler:Teams)) %>% \n  \n  #Seperating out list of missed tacklers into a separate column for each missed tackle\n separate(missedTackler,into = c(\"MT1\",\"MT2\",\"MT3\",\"MT4\"),sep = \";\") %>% \n  \n  #Replace missing values in missed tackle columns and Penalty yards column\n  replace_na(list(MT1 = 0, MT2 =0, MT3 = 0, MT4 = 0, penaltyYards = 0)) %>%\n  \n  #Binary Encoding the Missed Tackle Columns to show just the prescence of the ith missed tackle\n  mutate(NMT1 = ifelse(MT1 == 0 ,0,1)) %>% mutate(NMT2 = ifelse(MT2 == 0 ,0,1)) %>%\n  mutate(NMT3 = ifelse(MT3 == 0 ,0,1)) %>% mutate(NMT4 = ifelse(MT4 == 0 ,0,1)) %>%\n  \n#Add together the binary values of each missed tackle across the rows to get total # of missed tackles on each play, labeled Total Missed Tackles (TMT)\n  mutate(TMT = NMT1+NMT2+NMT3+NMT4) %>%\n  \n  #Take out columns of non-importance at this point \n  dplyr::select(., - (MT1:MT4),-(NMT1:NMT4)) %>%\n  \n  #exclude any NA values\n  na.exclude() %>% \n  \n  #filter for only the KR\n  filter(., a_pos==\"AKR\") %>% \n  \n  #Getting rid of variables not needed \n  select(., -dis,-(frameId:playId), -(UKI:UPI), -(RecTeam:UNI_KICK), -playDirection, -(kickType:returnDirectionIntended))  %>% \n  \n  #Creating dummy column for categortical return direction actual\n  dummy_columns(., select_columns = \"returnDirectionActual\") %>% \n  \n  #Delete the categorical variable from the data \n  select(., -returnDirectionActual)\n\n# Creating data set with Kickoff position and Euclidean distances \nPRED_KOEUC <- PRED_df_K %>% \n  \n  #Left join with the point in the data set from before thats ideal to build on\n  left_join(PRED_df_FDS, by = \"UKI\") %>% \n  \n  #filter at the moment of the KR receiving the ball\n  filter(., event == \"kick_received\") \n\n#adjust kickoff position as character variable, and replace NAs with 0 \nPRED_KOEUC$KOP <- PRED_KOEUC$KOP %>% as.character() %>% replace_na(0)\n\n#head of KOEUC\nhead(KOEUC)\n\n#Adjusting KOEUC to prepare for pivot\nPRED_AKOEUC <- PRED_KOEUC %>% \n  \n  #Set the KR was a unqiue individual on the return team\n  mutate(KOP = replace(KOP,nflId==returnerId,\"KR\")) %>%\n  \n  #filter out the football from the data and arrange by the KR first \n  filter(., displayName != \"football\") %>% arrange(.,a_pos) %>% \n  \n  #Rename a variable that was change in the table join \n   rename(.,UPI = UPI.x ) %>% \n  \n  #Then grouping by each unique UPI \n  group_by(UPI) %>%\n  \n  #Then creating the distance that each player/observation is from the Kick Returner\n   mutate(Euclidean = as.matrix(dist(cbind(x, y)))[1, ]) %>% ungroup()\n\n#head of AKOEUC\nhead(AKOEUC)\n\n#Create another table to merge with AKOEUC called NFD\nPRED_NFD <- PRED_AKOEUC %>% \n  \n  #filter for every player on the return team besides the KR\n  filter(., KOP == \"0\") %>% \n  \n  #select variables of interest \n  select(., x:dir,nflId,frameId:playDirection,kickReturnYardage:a_pos,KOP:Euclidean) %>%\n  \n  #arrange by the Euclidean\n  arrange(Euclidean) %>% \n  \n  #Group by UPI\n  group_by(UPI) %>%  \n  \n  #Ranking by each distance for each play to determine the position from the KR\n  mutate(KOP = rank(Euclidean)) %>%\n  \n  #filter for any outlier or mistake in the code\n  filter(., KOP < 11) %>%  filter(., Euclidean > 0.0) %>%\n  \n  #select the new filled KOP variable \n  select(., UKI, KOP) \n\n#head of NDF\nhead(NFD)\n\n#Data set to combine both data conditions \nPRED_KOPDS <- PRED_AKOEUC %>% \n  \n  #Merge filled return tea data set with filled kicking team data set \n  left_join(PRED_NFD, by=\"UKI\")%>%\n  \n  #replace 0s with NA in original kickoff position \n  mutate(KOP.x = replace(KOP.x, KOP.x==0, NA)) %>% \n  \n   #merge the two columns who have complementary missing values \n  transform(., TKOP = ifelse(!is.na(KOP.x), KOP.x, KOP.y)) %>% \n  \n  #select variables of interest \n  select(., x:dir,frameId:playDirection,kickReturnYardage:a_pos,Euclidean,TKOP) %>% \n  \n  #filter out any missing value of Kickoff position\n  filter(., TKOP != \"NA\") %>% \n  \n  #Rename variables that were changed when the tables joing \n  rename(.,UPI = UPI.x ) %>%\n  \n  #filter by unique play and only include plays with correct amount of players \n  group_by(UPI) %>% filter(n()==22)\n\n#pivot wider for specific values based on Kickoff Position \n\n#Euclidean table\nPRED_TA <- PRED_KOPDS %>% pivot_wider(.,id_cols = UPI, names_from = TKOP  , names_prefix = \"Euc\", values_from = Euclidean) %>% na.exclude()\n\n#Speed table \nPRED_TB <- PRED_KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"Sp\", values_from = s) %>% na.exclude()\n\n#Acceleration table \nPRED_TC <- PRED_KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"Acc\", values_from = a) %>% na.exclude()\n\n#Orientation table\nPRED_TD <- PRED_KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"Ori\", values_from = o) %>% na.exclude()\n\n#x table \n#TE <- KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"x\", values_from = x) %>% na.exclude()\n\n#y table\n#TF <- KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"y\", values_from = y) %>% na.exclude()\n\n#distance table \n#TG <- KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"dis\", values_from = dis) %>% na.exclude()\n\n#direction table \n#TH <- KOPDS %>% pivot_wider(.,id_cols = c(UPI), names_from = TKOP  , names_prefix = \"dir\", values_from = dir) %>% na.exclude()\n\n# Data frame together (DFT), left join every table to matching UPI value \n#DFT <- TA %>% left_join(TB) %>% left_join(TC) %>% left_join(TD) %>% left_join(TE)  %>% left_join(TF) %>% left_join(TG) %>% left_join(TH)\n\nPRED_DFT <- PRED_TA %>% left_join(PRED_TB) %>% left_join(PRED_TC) %>% left_join(PRED_TD) \n#head of DFT\nhead(DFT)\n\n#Merge DFT with Euclidean values\nPRED_df_NT <- PRED_df_KRKO %>% left_join(PRED_DFT, by=\"UPI\") %>% \n  \n  #delete UPI and an empty value from the data set\n  select(., -EucKR)\n\n#Checking for zero variance variables\nPRED_ZV <- nearZeroVar(PRED_df_NT, saveMetrics= TRUE)\n  #Results = No variables with 0 variance \n\n#Original data \n\n#Getting linear dependence \nPRED_lc <- findLinearCombos(PRED_df_NT)\n#data from without linear dependence \nPRED_df_WC <- PRED_df_NT[, -PRED_lc$remove] \n  #essentially just removed the variables that represented the mean\n# Filtering out correlated variables \nPRED_descrCor <-  cor(PRED_df_WC)\nPRED_highCorr <- sum(abs(PRED_descrCor[upper.tri(PRED_descrCor)]) > .999)\nPRED_highlyCorDescr <- findCorrelation(PRED_descrCor, cutoff = .75)\nPRED_df_WC <- PRED_df_WC[,-PRED_highlyCorDescr]\nPRED_descrCor2 <- cor(PRED_df_WC)\n\n#Narrowed down data \n\n#Transforming cleaned data sets\nPRED_preProcValues2 <- preProcess(PRED_df_NT[,-1], method = c(\"center\",\"scale\",\"YeoJohnson\"))\nPRED_train_d <- predict(PRED_preProcValues2, PRED_df_NT)\n\n#Creating subset of less variables \nPRED_train_pos <- PRED_train_d %>% select(., -(KTE:oKSDC3T6))\n\nPRED_sb2 <- PRED_train_d %>% select(., -Euc1,-Euc2,-Euc3,-Euc4,-Euc5,-Euc6,-Euc7,-Euc8,-Euc9,-Euc10,-Acc1,-Acc2,-Acc3,-Acc4,-Acc5,-Acc6,-Acc7,-Acc8,-Acc9,-Acc10,-Sp1,-Sp2,-Sp3,-Sp4,-Sp5,-Sp6,-Sp7,-Sp8,-Sp9,-Sp10)\n","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Creating Predictions:\n\nPred_GNET <- predict(GNET_M5, PRED_train_pos[,-1])\nPred_EM <- predict(EN_M5, PRED_train_pos[,-1])\nPred_RD <- predict(RD_M5, PRED_train_pos[,-1])\nPred_RFM <- predict(RFM6, PRED_train_pos[,-1])\nNEW_PRED_RFM <- predict(RFK1, PRED_sb2)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Create +/- variable\n\n#Creating data set for Plus/mius\ndf_merge_pred_1 <- PRED_train_pos %>% \n  \n  #Bind together all the predictions\n  cbind(Pred_GNET) %>% cbind(Pred_EM) %>% cbind(Pred_RD) %>% cbind(Pred_RFM) %>% cbind(NEW_PRED_RFM)\n\n#Join together predictions and original data set\ndf_final_merge <- PRED_df_K %>% left_join(df_merge_pred_1, by = \"UPI\") %>% \n  \n  #Filter by kick received, only the Kick Receiver, and No missing values\n  filter(., event == \"kick_received\") %>% filter(.,a_pos==\"AKR\") %>% filter(., Pred_RFM != \"NA\") %>% \n  \n  #Select Variables of interest\n  select(., displayName, nflId, gameId, playId, UPI, kickReturnYardage.y, Pred_GNET:NEW_PRED_RFM) %>%\n  \n  #Calculate squared differences from real value \n  mutate(NG = (Pred_GNET -kickReturnYardage.y)^2 ) %>% mutate(NEM= (Pred_EM -kickReturnYardage.y)^2 ) %>% mutate(NRD = (Pred_RD -kickReturnYardage.y)^2 ) %>% mutate(NRFM = (Pred_RFM -kickReturnYardage.y)^2 ) %>% mutate(FINAL_RFM = (NEW_PRED_RFM -kickReturnYardage.y)^2 ) %>% \n  #Create Plus_Minus Variable with best variable\n  mutate(Plus_Minus =  - kickReturnYardage.y)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"** 2c. Plus/Minus Expected**\n \nAn expected return yardage prediction was created based solely off the data in the instantaneous moment the kick returner had received the ball. The following 10 returners were deemed to be the highest over the expected kick return yards over the entire 2018 season. ","metadata":{}},{"cell_type":"code","source":"head(df_final_merge)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**2d. Variable importance (As shown below):**","metadata":{}},{"cell_type":"code","source":"#plot random forest\nI_SCORE_FUNC(RFK1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The first major component of the analysis was to measure the importance of the variables in some of the various versions of the data set. This measure was determined by random Forests importance measures, and caret’s feature selection measures. Based off several attempts at modeling various combinations of the tracking data, the following has become apparent. First, the most influential variable is the kick is the mean distance from the front 2 players on the kicking team. Second, the positional data, represented as the Euclidean distance, tends to mean far more than the orientation, speed, or even speed of the kicking team players. Third, the individual Euclidean distance only seems to be very influential at the kicking team level, the receiving team Euclidean distance appears more useful in the form of the summary statistics of its general distribution. Fourth, the average distance from the kick returner appears to be far more important than the total Euclidean distance of the entire kicking team, which means that the importance of the distances relies on each players relative distance more than the grand total, with the exception of the fastest 2 players on the kicking team. Though speed of the fastest players on the kicking team is vital, the remainder of the kicking team must be in the same range of proximity to the kick returner to ensure success tackling the kick returner. Next, obviously missed tackled have a significant impact on the amount of return yards, but interestingly enough, not as much as the general distribution of the distances from the kick returner and from each other on the kicking team.  Additionally, it appears that the players whose Euclidean distances that matter the most on the kicking team, tend to line up near the middle of the field, such as L5, L4, R4, R5. So, it would appear that ideally the fastest players on the kicking team line up in the positions closest to the kicker. Finally, the only movement statistic that was significant was the mean acceleration of every player on the kicking team, which means that cohesiveness as a unit might mean more than the individual speeds of the player.\n\nFor the receiving team, it appears that the spread of the distances amongst the blockers appears to carry the most weight. The most important blockers appear to definitely be the two closest blockers, as they need to be close enough at the point of receiving the ball.  \n\nFinally for the kick returner, the position on the field he receives the ball at matters, and returning the ball in the middle of the field tends to have a slightly negative impact on the expected return yards. \n\nDiscussion:\n\nIt should first be noted that the model’s power is not high enough to soley predict the amount of return yards, but it is strong enough to show the importance of conditions of the play as soon as the ball is received. Close to 30% of the circumstances needed to determine the true value is determined before the possession even changes.  \n\nAdditionally, this information provides general strategic insight into kickoff and kick return plays. The kicking team should have their two fastest players generally more to the inside of the formation, though the general emphasis for the kicking team is cohesion, which most likely prevents several holes from opening mid-play as a result of greatly varying speeds and distances. Acceleration is a bonus on top of cohesion. The return team should attempt to take the ball outside, closest to their 2 closest blockers, and the distance between the return team players should be less than average.  \n\nIn the future, it would be interesting to study the influence that orientation, speed, and acceleration have every frame after the kick is received, but this is a beneficial starting point. ","metadata":{}}]}