{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# A few things before we start..\n\n1. I don't watch American Football. I care more about round ball football (soccer) and Australian Rules Football.  \n2. I will focus more on punts, because I like Pat McAfee, and I like Australian punters. Also, as Jon Bois said, [kickoffs are stupid and bad.](https://www.youtube.com/watch?v=t_SsIKgwvz4)\n\nWith that out of the way..","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib as mpl\nimport matplotlib.pyplot as plt\nimport os\nimport plotly.express as px","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:16:29.054296Z","iopub.execute_input":"2021-11-29T01:16:29.055155Z","iopub.status.idle":"2021-11-29T01:16:29.060357Z","shell.execute_reply.started":"2021-11-29T01:16:29.055109Z","shell.execute_reply":"2021-11-29T01:16:29.059283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.getcwd()\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:16:29.062027Z","iopub.execute_input":"2021-11-29T01:16:29.062262Z","iopub.status.idle":"2021-11-29T01:16:29.078476Z","shell.execute_reply.started":"2021-11-29T01:16:29.062235Z","shell.execute_reply":"2021-11-29T01:16:29.077567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"games = pd.read_csv('../input/nfl-big-data-bowl-2022/games.csv')","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:16:29.079940Z","iopub.execute_input":"2021-11-29T01:16:29.080879Z","iopub.status.idle":"2021-11-29T01:16:29.093274Z","shell.execute_reply.started":"2021-11-29T01:16:29.080834Z","shell.execute_reply":"2021-11-29T01:16:29.092568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scouting_data = pd.read_csv('../input/nfl-big-data-bowl-2022/PFFScoutingData.csv')\nplays = pd.read_csv('../input/nfl-big-data-bowl-2022/plays.csv')\nplayers = pd.read_csv('../input/nfl-big-data-bowl-2022/players.csv')\ntracking_2020 = pd.read_csv('../input/nfl-big-data-bowl-2022/tracking2020.csv')","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:16:29.095523Z","iopub.execute_input":"2021-11-29T01:16:29.096723Z","iopub.status.idle":"2021-11-29T01:16:58.626752Z","shell.execute_reply.started":"2021-11-29T01:16:29.096675Z","shell.execute_reply":"2021-11-29T01:16:58.625857Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plays_joined = plays.merge(scouting_data,how=\"left\", left_index=True, right_index=True, suffixes=('', '_y'))\nplays_joined = plays_joined.merge(games,how=\"left\", on=\"gameId\")\n\n\n#Cut plays to 2020 season only\n\npunts = plays_joined[(plays_joined[\"season\"]==2020) & (plays[\"specialTeamsPlayType\"]==\"Punt\")]\n\n#Convert ID and Jersey Number to int instead of float.\nto_convert = ['nflId', 'jerseyNumber']\nfor col in to_convert:\n    tracking_2020[col] = tracking_2020[col].astype('Int64')","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:16:58.628338Z","iopub.execute_input":"2021-11-29T01:16:58.628945Z","iopub.status.idle":"2021-11-29T01:16:59.213574Z","shell.execute_reply.started":"2021-11-29T01:16:58.628892Z","shell.execute_reply":"2021-11-29T01:16:59.212671Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's see what events are there in the tracking data.","metadata":{}},{"cell_type":"code","source":"tracking_2020['event'].unique()","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:16:59.214989Z","iopub.execute_input":"2021-11-29T01:16:59.216099Z","iopub.status.idle":"2021-11-29T01:17:00.098580Z","shell.execute_reply.started":"2021-11-29T01:16:59.216048Z","shell.execute_reply":"2021-11-29T01:17:00.097702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Whoops, looks like there's a typo. Let's fix that, shall we?","metadata":{}},{"cell_type":"code","source":"tracking_2020['event'] = tracking_2020['event'].replace({\"field_goal_miseed\":\"field_goal_missed\"})","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:00.099951Z","iopub.execute_input":"2021-11-29T01:17:00.100241Z","iopub.status.idle":"2021-11-29T01:17:02.047963Z","shell.execute_reply.started":"2021-11-29T01:17:00.100208Z","shell.execute_reply":"2021-11-29T01:17:02.046880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualization of plays\n\nThis code improves upon ones in notebooks that have already been published with this dataset. This visualization code allows you to not only see players, but also see their roles based on play and PFF data. You can see who the gunners are, who the vises are, who made the tackle, who missed the tackle, etc.\n\nExpand the cell below to see the code.","metadata":{}},{"cell_type":"code","source":"def draw_play(game_id, play_id):\n    tracking_data = tracking_2020.loc[(tracking_2020['gameId'] == game_id) & (tracking_2020['playId'] == play_id)]\n    tracking_data = tracking_data.fillna({'jerseyNumber':0, 'nflId': 0})\n    \n    event = tracking_data[['frameId', 'event']].drop_duplicates().reset_index()\n    \n    \n    game_data = games[(games['gameId'] == game_id)]\n    scout_data = scouting_data[(scouting_data['gameId'] == game_id) & (scouting_data['playId'] == play_id)]\n    play_data = plays[(plays['gameId'] == game_id) & (plays['playId'] == play_id)]\n    \n    home_team = game_data['homeTeamAbbr'].item()\n    visitor_team = game_data['visitorTeamAbbr'].item()\n    \n    # Grab gunners, vises, kicker, returner, tackler, assist tacklers, missed tacklers.    \n    \n    # new column, merge Team and jerseyNumber to match PFF Scouting Data.\n    tracking_data[\"teamJerseyCombined\"] = \"\"\n    tracking_data[\"teamJerseyCombined\"] = tracking_data.agg('{0[team]} {0[jerseyNumber]}'.format, axis=1)\n    \n    def split_and_return_teams(df, col):\n        if type(df[col].item()) != str:\n            return []\n        result = df[col].item().split(\";\")\n        for i in range(len(result)):\n            result[i] = result[i].strip().replace(home_team, \"home\")\\\n                .replace(visitor_team,\"away\")\n        return result\n    \n    gunners = split_and_return_teams(scout_data, \"gunners\")\n    vises = split_and_return_teams(scout_data, \"vises\")\n    tackler = split_and_return_teams(scout_data, \"tackler\")\n    missed_tackler = split_and_return_teams(scout_data, \"missedTackler\")\n    assist_tackler = split_and_return_teams(scout_data, \"assistTackler\")\n    rushers = split_and_return_teams(scout_data, \"puntRushers\")\n    safeties = split_and_return_teams(scout_data, \"specialTeamsSafeties\")\n    try:\n        kicker = int(play_data['kickerId'].item())\n    except:\n        kicker = \"\"\n    try:\n        returner = int(play_data['returnerId'].item())\n    except:\n        returner = \"\"\n    conditions = np.array([\n        tracking_data['nflId'] == kicker,\n        tracking_data['nflId'] == returner,\n        tracking_data[\"teamJerseyCombined\"].isin(gunners),\n        tracking_data[\"teamJerseyCombined\"].isin(vises),\n        tracking_data[\"teamJerseyCombined\"].isin(tackler),\n        tracking_data[\"teamJerseyCombined\"].isin(missed_tackler),\n        tracking_data[\"teamJerseyCombined\"].isin(assist_tackler),\n        tracking_data[\"teamJerseyCombined\"].isin(rushers),\n        tracking_data[\"teamJerseyCombined\"].isin(safeties),\n        tracking_data[\"team\"] == \"Football\"\n    ], dtype=bool)\n    \n    \n    choices = [\"Kicker\", \"Returner\", \"Gunner\", \"Vise\", \"Tackler\", \"Missed Tackler\", \"Assist Tackler\",\n               \"Punt Rushers\", \"Safety\", \"Football\"]\n    \n    tracking_data[\"role\"]=\"\"\n    \n    tracking_data = tracking_data.reset_index(drop=True)\n    roles = np.select(conditions, choices, default=\"Normal\")\n    tracking_data['role'] = roles\n    \n    #set border colors of points based on grabbed data\n    \n    def set_color(role):\n        role = role[-1]\n        if(role == \"Normal\"):\n            return \"black\"\n        elif(role == \"Gunner\"):\n            return \"red\"\n        elif(role == \"Vise\"):\n            return \"green\"\n        elif(role == \"Kicker\"):\n            return \"blue\"\n        elif(role == \"Returner\"):\n            return \"yellow\"\n        elif(role == \"Tackler\"):\n            return \"gold\"\n        elif(role == \"Missed Tackler\"):\n            return \"DarkSlateGrey\"\n        elif(role == \"Safety\"):\n            return \"cyan\"\n        elif(role == \"Football\"):\n            return \"white\"\n        else:\n            return \"purple\"\n        \n    players = tracking_data.loc[:,[\"nflId\",\"displayName\", \"role\"]].drop_duplicates()\n    game_string = \"{} @ {} - {} Week {}, Play ID {}\".format(visitor_team, home_team, game_data[\"season\"].item(),\n                                               game_data[\"week\"].item(), play_data[\"playId\"].item())\n    fig = px.scatter(tracking_data, x=\"x\", y=\"y\", animation_frame=\"frameId\", animation_group=\"nflId\",\n                     color=\"team\",\n                     hover_name=\"displayName\", range_x=[0,120], range_y=[-2,55.3],\n                     width=1047, height=600,\n                     title=game_string,\n                    hover_data=[\"position\", \"jerseyNumber\", \"role\"])\n    fig.add_shape(type=\"rect\",\n            xref=\"x\", yref=\"y\",\n            x0=10, y0=0,\n                      layer=\"below\",\n            x1=110, y1=53.3,\n            line=dict(\n                color=\"black\",\n                width=2,\n            ),\n            fillcolor=\"white\",\n        )\n    fig.update_xaxes(tick0=0, dtick=10)\n    fig.layout.updatemenus[0].buttons[0].args[1][\"frame\"][\"duration\"] = 100\n    fig.layout.updatemenus[0].buttons[0].args[1]['transition'] = {\"duration\":100, \"easing\":\"quad\"}\n    \n    \n    for i in range(len(fig.data)):\n        fig.data[i]['marker']=dict(size=12,\n                                      line=dict(width=2,\n                                                color=list(map(set_color, fig.data[i]['customdata']))))\n        \n    # This was for dynamic titles to show the event at frame but it was laggy due to redrawing\n    #for button in fig.layout.updatemenus[0].buttons:\n    #    button['args'][1]['frame']['redraw'] = True\n    #for step in fig.layout.sliders[0].steps:\n    #    step[\"args\"][1][\"frame\"][\"redraw\"] = True\n    \n    notable_events = event[(event[\"event\"] != \"None\")]\n    print(play_data[\"playDescription\"].item())\n    print(\"Notable events\")\n    print(notable_events)\n    return fig","metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-11-29T01:17:02.049904Z","iopub.execute_input":"2021-11-29T01:17:02.050467Z","iopub.status.idle":"2021-11-29T01:17:02.085431Z","shell.execute_reply.started":"2021-11-29T01:17:02.050412Z","shell.execute_reply":"2021-11-29T01:17:02.084714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now, let's see some plays!\n\nWhat's the longest punt return of the 2020 season?","metadata":{}},{"cell_type":"code","source":"punt_returns = punts.loc[plays_joined.specialTeamsResult==\"Return\"]\nmax_return_yardage_play = punt_returns[punt_returns[\"kickReturnYardage\"] == punt_returns.loc[:,\"kickReturnYardage\"].max()]\ndraw_play(max_return_yardage_play[\"gameId\"].item(),max_return_yardage_play[\"playId\"].item())","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:02.086789Z","iopub.execute_input":"2021-11-29T01:17:02.087136Z","iopub.status.idle":"2021-11-29T01:17:06.157924Z","shell.execute_reply.started":"2021-11-29T01:17:02.087094Z","shell.execute_reply":"2021-11-29T01:17:06.156741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"On the other hand, what's the saddest punt return out there?","metadata":{}},{"cell_type":"code","source":"min_return_yardage_play = punt_returns[punt_returns[\"kickReturnYardage\"] == punt_returns.loc[:,\"kickReturnYardage\"].min()]\ndraw_play(min_return_yardage_play[\"gameId\"].item(),min_return_yardage_play[\"playId\"].item())","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:06.162122Z","iopub.execute_input":"2021-11-29T01:17:06.162539Z","iopub.status.idle":"2021-11-29T01:17:09.884425Z","shell.execute_reply.started":"2021-11-29T01:17:06.162477Z","shell.execute_reply":"2021-11-29T01:17:09.883573Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Punting EDA","metadata":{}},{"cell_type":"code","source":"punts.iloc[:,:].describe(include=\"all\")","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:09.886246Z","iopub.execute_input":"2021-11-29T01:17:09.886795Z","iopub.status.idle":"2021-11-29T01:17:10.030676Z","shell.execute_reply.started":"2021-11-29T01:17:09.886748Z","shell.execute_reply":"2021-11-29T01:17:10.029844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"punts[\"kickDirectionSameAsIntended\"] = np.where(punts.kickDirectionIntended == punts.kickDirectionActual, True, False)","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:10.031862Z","iopub.execute_input":"2021-11-29T01:17:10.032114Z","iopub.status.idle":"2021-11-29T01:17:10.039159Z","shell.execute_reply.started":"2021-11-29T01:17:10.032087Z","shell.execute_reply":"2021-11-29T01:17:10.038056Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"kick_direction_grouped = punts.groupby(\"kickDirectionSameAsIntended\").size().reset_index(name='count')\npx.bar(kick_direction_grouped, x=\"kickDirectionSameAsIntended\", y=\"count\", title=\"How often do kickers kick in different directions than the setup intended?\").show()\n\nkick_direction_result = punts.groupby([\"kickDirectionSameAsIntended\", \"specialTeamsResult\"]).size().reset_index(name='count')\nkick_direction_result = kick_direction_result.apply(lambda x: x/x.sum() if x.name == \"count\" else x )\n\nkick_dir_result_pct = punts.groupby([\"kickDirectionSameAsIntended\", \"specialTeamsResult\"]).size().groupby(level=0)\\\n.apply(lambda x: x/x.sum()).reset_index(name='pct')\n\npx.bar(kick_dir_result_pct, x=\"kickDirectionSameAsIntended\", y=\"pct\", color= \"specialTeamsResult\",\n      barmode=\"group\", title=\"Kick Direction vs Special Teams Result\").show()\n\nreturned_punts = punts[(punts.specialTeamsResult==\"Return\")]\npx.box(punts, x=\"kickDirectionSameAsIntended\", y=\"kickReturnYardage\", points=\"all\",\n      title=\"Did changing kick direction impact return yardage?\",).show()","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:10.040433Z","iopub.execute_input":"2021-11-29T01:17:10.040783Z","iopub.status.idle":"2021-11-29T01:17:10.293718Z","shell.execute_reply.started":"2021-11-29T01:17:10.040698Z","shell.execute_reply":"2021-11-29T01:17:10.293106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Just to check what's on it, let's look at the plays with non-special teams result, and watch one of them.","metadata":{}},{"cell_type":"code","source":"non_special_teams_result = punts[(punts[\"specialTeamsResult\"] == \"Non-Special Teams Result\")]\n\n\nfrom wordcloud import WordCloud\nwordcloud = WordCloud (\n                    background_color = 'white',\n                    width = 800,\n                    height = 600\n                        ).generate(\" \".join(non_special_teams_result.playDescription))\n\nplt.imshow(wordcloud)","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:10.294824Z","iopub.execute_input":"2021-11-29T01:17:10.295535Z","iopub.status.idle":"2021-11-29T01:17:11.536253Z","shell.execute_reply.started":"2021-11-29T01:17:10.295485Z","shell.execute_reply":"2021-11-29T01:17:11.535309Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's watch one.\ntest_play = non_special_teams_result.iloc[8,:]\ndraw_play(test_play[\"gameId\"].item(),test_play[\"playId\"].item())","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:11.538124Z","iopub.execute_input":"2021-11-29T01:17:11.538792Z","iopub.status.idle":"2021-11-29T01:17:13.189819Z","shell.execute_reply.started":"2021-11-29T01:17:11.538741Z","shell.execute_reply":"2021-11-29T01:17:13.188971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"kick_contact_grouped = punts.groupby(\"kickContactType\").size().reset_index(name='count')\npx.bar(kick_contact_grouped, x=\"kickContactType\", y=\"count\", title=\"Kick contact type\")","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:13.191232Z","iopub.execute_input":"2021-11-29T01:17:13.191638Z","iopub.status.idle":"2021-11-29T01:17:13.259673Z","shell.execute_reply.started":"2021-11-29T01:17:13.191601Z","shell.execute_reply":"2021-11-29T01:17:13.259018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Who has the top 10 punting averages and net punting avg in the 2020 season?","metadata":{}},{"cell_type":"code","source":"kicks_and_yards = punts.loc[:,[\"kickerId\", \"kickLength\", \"playDescription\", \"playResult\"]]","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:29.868517Z","iopub.execute_input":"2021-11-29T01:17:29.868794Z","iopub.status.idle":"2021-11-29T01:17:29.874845Z","shell.execute_reply.started":"2021-11-29T01:17:29.868764Z","shell.execute_reply":"2021-11-29T01:17:29.873601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"kicker_avg = kicks_and_yards.groupby(\"kickerId\").agg({\"playDescription\":\"count\",\n      \"playResult\":\"mean\",\n      \"kickLength\":\"mean\"})\\\n      .rename(columns={'playDescription':'count','playResult':'netPuntingAvg',\n                      'kickLength':'puntingAvg'})\\\n      .sort_values([\"puntingAvg\",\"netPuntingAvg\"]).reset_index()\nkicker_avg = pd.merge(kicker_avg, players, left_on='kickerId', right_on='nflId', how=\"left\")\n\n#Exclude kickers that didn't kick 10 or more punts.\nkicker_avg = kicker_avg[(kicker_avg[\"count\"] >= 10)]\n\npx.bar(kicker_avg.sort_values(\"puntingAvg\",ascending=False).iloc[:10,:],x=\"displayName\",y=\"puntingAvg\",\n       hover_data=[\"count\"], title=\"Top 10 punting average\").show()\npx.bar(kicker_avg.sort_values(\"netPuntingAvg\",ascending=False).iloc[:10,:],x=\"displayName\",y=\"netPuntingAvg\",\n       hover_data=[\"count\"], title=\"Top 10 net punting average\").show()","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:13.267732Z","iopub.execute_input":"2021-11-29T01:17:13.268171Z","iopub.status.idle":"2021-11-29T01:17:13.419383Z","shell.execute_reply.started":"2021-11-29T01:17:13.268120Z","shell.execute_reply":"2021-11-29T01:17:13.418562Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Let's see the other side of the play: returners!\n\nWho are the returners that has the most return yardage?\nNote: Returners are more often ranked based on total yardage instead of their average. You know, with fair catch and stuff.","metadata":{}},{"cell_type":"code","source":"returners_and_yards = punts.loc[:,[\"returnerId\", \"kickReturnYardage\", \"playDescription\"]]\n\n#We don't have to exclude returners based on the number of times they've returned\n#since we're ranking it by total yardage.\n\nplayer_dup = players.loc[:,:]\nplayer_dup[\"nflId\"] = player_dup[\"nflId\"].astype('str')\n\nreturners_yards = returners_and_yards.groupby(\"returnerId\").agg({\"playDescription\":\"count\",\n        \"kickReturnYardage\":\"sum\"})\\\n        .rename(columns={'playDescription':'count'}).sort_values(\"kickReturnYardage\").reset_index()\nreturners_yards = pd.merge(returners_yards, player_dup, left_on='returnerId', right_on='nflId', how=\"left\")\npx.bar(returners_yards.sort_values(\"kickReturnYardage\",ascending=False).iloc[:10,:],x=\"displayName\",y=\"kickReturnYardage\",\n       hover_data=[\"count\"], title=\"Top 10 total return yards\").show()","metadata":{"execution":{"iopub.status.busy":"2021-11-29T01:17:54.347536Z","iopub.execute_input":"2021-11-29T01:17:54.348027Z","iopub.status.idle":"2021-11-29T01:17:54.432459Z","shell.execute_reply.started":"2021-11-29T01:17:54.347977Z","shell.execute_reply":"2021-11-29T01:17:54.431588Z"},"trusted":true},"execution_count":null,"outputs":[]}]}