{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\nfrom typing import Union, Sequence\nfrom numbers import Number\nfrom datetime import datetime, timedelta\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom matplotlib import cm\nimport plotly.express as px\nimport plotly.figure_factory as ff\nimport plotly.graph_objects as go\nfrom plotly.subplots import make_subplots","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-11-11T09:59:56.643851Z","iopub.execute_input":"2021-11-11T09:59:56.644661Z","iopub.status.idle":"2021-11-11T10:00:00.002067Z","shell.execute_reply.started":"2021-11-11T09:59:56.644610Z","shell.execute_reply":"2021-11-11T10:00:00.000817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load the data","metadata":{}},{"cell_type":"code","source":"players = pd.read_csv(\"/kaggle/input/nfl-big-data-bowl-2022/players.csv\")\nplays = pd.read_csv(\"/kaggle/input/nfl-big-data-bowl-2022/plays.csv\")\ngames = pd.read_csv(\"/kaggle/input/nfl-big-data-bowl-2022/games.csv\")\nscouting = pd.read_csv(\"/kaggle/input/nfl-big-data-bowl-2022/PFFScoutingData.csv\")\ntracking2018 = pd.read_csv(\"/kaggle/input/nfl-big-data-bowl-2022/tracking2018.csv\")\n#tracking2019 = pd.read_csv(\"/kaggle/input/nfl-big-data-bowl-2022/tracking2019.csv\")\n#tracking2020 = pd.read_csv(\"/kaggle/input/nfl-big-data-bowl-2022/tracking2020.csv\")","metadata":{"execution":{"iopub.status.busy":"2021-11-11T10:00:00.004087Z","iopub.execute_input":"2021-11-11T10:00:00.004411Z","iopub.status.idle":"2021-11-11T10:00:51.936978Z","shell.execute_reply.started":"2021-11-11T10:00:00.004370Z","shell.execute_reply":"2021-11-11T10:00:51.936046Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Format Datetime","metadata":{}},{"cell_type":"code","source":"games['gameDateTime'] = pd.to_datetime(games['gameDate'] + \"-\" + games['gameTimeEastern'], format=\"%m/%d/%Y-%H:%M:%S\")\nplayers['birthDate'] = pd.to_datetime(players['birthDate'])\nplays['gameClock']  = pd.to_datetime(plays['gameClock']).apply(lambda i: timedelta(hours=i.hour, minutes=i.minute, seconds=i.second))\ntracking2018['time'] = pd.to_datetime(tracking2018['time'])","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:54:01.812765Z","iopub.execute_input":"2021-11-11T09:54:01.813011Z","iopub.status.idle":"2021-11-11T09:54:05.866293Z","shell.execute_reply.started":"2021-11-11T09:54:01.812984Z","shell.execute_reply":"2021-11-11T09:54:05.865640Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### games data\nplayers.csv consist of the following fields:\n* **gameID**: `int`\n* **season**: `int`\n* **week**: `int`\n* **gameDate**: `str`\n* **gameTimeEastern**: `str`\n* **homeTeamAbbr**: `str`\n* **visitorTeamAbbr**: `str`","metadata":{}},{"cell_type":"code","source":"print(len(games))\ngames.head(10)","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:39:57.099862Z","iopub.execute_input":"2021-11-11T09:39:57.100121Z","iopub.status.idle":"2021-11-11T09:39:57.123321Z","shell.execute_reply.started":"2021-11-11T09:39:57.100091Z","shell.execute_reply":"2021-11-11T09:39:57.122500Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### [WIP] calculate game score","metadata":{}},{"cell_type":"markdown","source":"### Players data\nplayers.csv consist of the following fields:\n* **nflID**: `int`\n* **Height**: `str`. Some are X(feet)-Y(inch), some are XX(inch) format. I will convert them to cm.\n* **Weight**: `int`\n* **birthdate**: `str`. This will be converted to `datetime` type.\n* **collegeName**: `str`\n* **Position**: `str`\n* **displayName**: `str`","metadata":{}},{"cell_type":"code","source":"print(len(players))\nplayers.head(10)","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:39:57.124857Z","iopub.execute_input":"2021-11-11T09:39:57.125345Z","iopub.status.idle":"2021-11-11T09:39:57.141947Z","shell.execute_reply.started":"2021-11-11T09:39:57.125284Z","shell.execute_reply":"2021-11-11T09:39:57.141183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Format height & birthDate","metadata":{}},{"cell_type":"code","source":"def format_height(height: Union[str, Number]):\n    \"\"\" Convert height to cm \"\"\"\n    # if height is number, do nothing (assuming it's already converted)\n    if isinstance(height, Number):\n        return height\n    # in 'X-X' format\n    elif '-' in height:\n        feet, inch = map(int, height.split('-'))\n        return (feet * 12 + inch) * 2.54\n    # in 'XX' format\n    elif isinstance(height, str):\n        return int(height) * 2.54\n    else:\n        ValueError(\"invalid heigth format\")","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:39:57.142947Z","iopub.execute_input":"2021-11-11T09:39:57.143491Z","iopub.status.idle":"2021-11-11T09:39:57.153270Z","shell.execute_reply.started":"2021-11-11T09:39:57.143454Z","shell.execute_reply":"2021-11-11T09:39:57.152658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"players['height'] = players['height'].apply(format_height)\nplayers['age'] = (datetime.today() - players['birthDate']).apply(lambda i: i.days)  # age in terms of days","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:39:57.154316Z","iopub.execute_input":"2021-11-11T09:39:57.155057Z","iopub.status.idle":"2021-11-11T09:39:57.210758Z","shell.execute_reply.started":"2021-11-11T09:39:57.155025Z","shell.execute_reply":"2021-11-11T09:39:57.209838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ff.create_scatterplotmatrix(players[[\"Position\", \"height\", \"weight\", \"age\"]],\n                            height=800, width=1000,\n                            index='Position',\n                            diag='histogram')","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:39:57.211975Z","iopub.execute_input":"2021-11-11T09:39:57.212215Z","iopub.status.idle":"2021-11-11T09:39:58.248368Z","shell.execute_reply.started":"2021-11-11T09:39:57.212184Z","shell.execute_reply":"2021-11-11T09:39:58.247743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Tracking\n* **time**: `str`. Time stamp of play (time, yyyy-mm-dd, hh:mm:ss)\n* **x**: `float`. Player position along the long axis of the field, 0 - 120 yards.\n* **y**: `float`. Player position along the short axis of the field, 0 - 53.3 yards.\n* **s**: `float`. Speed in yards/second (numeric)\n* **a**: `float`. Speed in yards/second^2 (numeric)\n* **dis**: `float`. Distance traveled from prior time point, in yards (numeric)\n* **o**: `float`. Player orientation (deg), 0 - 360 degrees (numeric)\n* **dir**: `float`. Angle of player motion (deg), 0 - 360 degrees (numeric)\n* **event**: `str`. Tagged play details, including moment of ball snap, pass release, pass catch, tackle, etc (text)\n* **nflId**: `int`. Player identification number, unique across players (numeric)\n* **displayName**: `str`. Player name (text)\n* **jerseyNumber**: `float`. Jersey number of player (numeric)\n* **position**: `str`. Player position group (text)\n* **team**: `str`. Team (away or home) of corresponding player (text)\n* **frameId**: `int`. Frame identifier for each play, starting at 1 (numeric)\n* **gameId**: `int`. Game identifier, unique (numeric)\n* **playId**: `int`. Play identifier, not unique across games (numeric)\n* **playDirection**: `str`. Direction that the offense is moving (left or right)","metadata":{}},{"cell_type":"code","source":"tracking2018.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:39:58.249556Z","iopub.execute_input":"2021-11-11T09:39:58.249882Z","iopub.status.idle":"2021-11-11T09:39:58.273835Z","shell.execute_reply.started":"2021-11-11T09:39:58.249852Z","shell.execute_reply":"2021-11-11T09:39:58.273274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gameid = gameid = 2018123000\nplayid = 36\nfig = plt.figure(figsize=(15, 10))\nfor team, cmap in zip(('home', 'away', 'football'), ('Reds', 'Blues', 'Greens')):\n    tmp = tracking2018.query(f\"(gameId=={gameid}) & (playId == {playid}) & (team == '{team}')\")\n    cmap = plt.cm.get_cmap(cmap)\n    x = tmp['x'].values\n    y = tmp['y'].values\n    v_x = tmp['s'].values * np.sin(np.deg2rad(tmp['o'].values))\n    v_y = tmp['s'].values * np.cos(np.deg2rad(tmp['o'].values))\n    if team != 'football':\n        plt.quiver(x, y, v_x, v_y, angles=\"xy\", zorder=5, alpha=0.7,\n                   color=[cmap(i/tmp['frameId'].max()) for i in tmp['frameId']])\n    else:\n        plt.scatter(x, y, color=[cmap(i/tmp['frameId'].max()) for i in tmp['frameId']])\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2021-11-11T10:01:08.737758Z","iopub.execute_input":"2021-11-11T10:01:08.738101Z","iopub.status.idle":"2021-11-11T10:01:16.197002Z","shell.execute_reply.started":"2021-11-11T10:01:08.738069Z","shell.execute_reply":"2021-11-11T10:01:16.196025Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Plays\n* **gameId**: `str`. Game identifier, unique (numeric)\n* **playId**: `int`.  Play identifier, not unique across games (numeric)\n* **playDescription**: `str`. Description of play (text)\n* **quarter**: `int`. Game quarter (numeric)\n* **down**: `int`. Down (numeric)\n* **yardsToGo**: `int`. Distance needed for a first down (numeric)\n* **possessionTeam**: `str`. Team punting, placekicking or kicking off the ball (text)\n* **specialTeamsPlayType**: `str`. Formation of play: Extra Point, Field Goal, Kickoff or Punt (text)\n* **specialTeamsPlayResult**: `str`. Special Teams outcome of play dependent on play type: Blocked Kick Attempt, Blocked Punt, Downed, Fair Catch, Kick Attempt Good, Kick Attempt No Good, Kickoff Team Recovery, Muffed, Non-Special Teams Result, Out of Bounds, Return or Touchback (text)\n* **kickerId**: `float`. nflId of placekicker, punter or kickoff specialist on play (numeric)\n* **returnerId**: `None` or `int`. nflId(s) of returner(s) on play if there was a special teams return. Multiple returners on a play are separated by a ; (text)\n* **kickBlockerId**: `float`. nflId of blocker of kick on play if there was a blocked field goal or blocked punt (numeric)\n* **yardlineSide**: `str`. 3-letter team code corresponding to line-of-scrimmage (text)\n* **yardlineNumber**: `int`. Yard line at line-of-scrimmage (numeric)\n* **gameClock**: `str`. Time on clock of play (MM:SS)\n* **penaltyCodes**: `str` or `None`. NFL categorization of the penalties that occurred on the play. A standard penalty code followed by a d means the penalty was on the defense. Multiple penalties on a play are separated by a ; (text)\n* **penaltyJerseyNumber**: `str` or `None`. Jersey number and team code of the player committing each penalty. Multiple penalties on a play are separated by a ; (text)\n* **penaltyYards**: `float`. yards gained by possessionTeam by penalty (numeric)\n* **preSnapHomeScore**: `int`. Home score prior to the play (numeric)\n* **preSnapVisitorScore**: `int`. Visiting team score prior to the play (numeric)\n* **passResult**: `str` or `None`. Scrimmage outcome of the play if specialTeamsPlayResult is \"Non-Special Teams Result\" (C: Complete pass, I: Incomplete pass, S: Quarterback sack, IN: Intercepted pass, R: Scramble, ' ': Designed Rush, text)\n* **kickLength**: `float`. Kick length in air of kickoff, field goal or punt (numeric)\n* **kickReturnYardage**: `float`. Yards gained by return team if there was a return on a kickoff or punt (numeric)\n* **playResult**: `int`. Net yards gained by the kicking team, including penalty yardage (numeric)\n* **absoluteYardlineNumber**: `int`. Location of ball downfield in tracking data coordinates (numeric)","metadata":{}},{"cell_type":"code","source":"play = plays.query(f\"gameId == {gameid}\")\nplay.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:40:01.267207Z","iopub.execute_input":"2021-11-11T09:40:01.268074Z","iopub.status.idle":"2021-11-11T09:40:01.300452Z","shell.execute_reply.started":"2021-11-11T09:40:01.268031Z","shell.execute_reply":"2021-11-11T09:40:01.299659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Merge tracking data with plays & games according to playId","metadata":{}},{"cell_type":"code","source":"tracking2018 = tracking2018.merge(plays[['gameId', 'playId', 'quarter', 'possessionTeam']], on=['gameId', 'playId'])","metadata":{"execution":{"iopub.status.busy":"2021-11-11T10:00:51.938596Z","iopub.execute_input":"2021-11-11T10:00:51.938859Z","iopub.status.idle":"2021-11-11T10:01:02.551442Z","shell.execute_reply.started":"2021-11-11T10:00:51.938829Z","shell.execute_reply":"2021-11-11T10:01:02.550552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"game = tracking2018.query(f\"gameId == {gameid}\")\ngame.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-11-11T10:01:16.198938Z","iopub.execute_input":"2021-11-11T10:01:16.199657Z","iopub.status.idle":"2021-11-11T10:01:16.287188Z","shell.execute_reply.started":"2021-11-11T10:01:16.199608Z","shell.execute_reply":"2021-11-11T10:01:16.286167Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Visualize ball heatmap using kernel density estimation\nNote that play side (direction) changes every quarter","metadata":{"execution":{"iopub.status.busy":"2021-11-06T15:52:33.438285Z","iopub.execute_input":"2021-11-06T15:52:33.438972Z","iopub.status.idle":"2021-11-06T15:52:33.444164Z","shell.execute_reply.started":"2021-11-06T15:52:33.438928Z","shell.execute_reply":"2021-11-06T15:52:33.443171Z"}}},{"cell_type":"code","source":"ball = game.query(\"displayName == 'football'\")\nball['possession'] = np.where(ball['possessionTeam'] == games.query(f\"gameId == {gameid}\")['homeTeamAbbr'].iloc[0], 'home', 'away')\nball['x'] = ball['x'].where((ball['quarter']%2!=0) & (ball['possession']=='home'), 120 - ball['x'])\nball['x'] = ball['x'].where((ball['quarter']%2==0) & (ball['possession']=='away'), 120 - ball['x'])\nfig, ax = plt.subplots(1,2, figsize=(15, 10))\nsns.kdeplot(ax=ax[0], data=ball.query(\"possession == 'home'\"), x='y', y='x', levels=100, fill=True, cmap=\"rocket_r\")\nsns.kdeplot(ax=ax[1], data=ball.query(\"possession == 'away'\"), x='y', y='x', levels=100, fill=True, cmap=\"rocket_r\")\nfor i in range(2):\n    ax[i].set_xlim([-20, 80])\n    ax[i].set_ylim([-20, 140])\n    for h in range(0, 140, 20):\n        ax[i].hlines(y=h, xmin=-20, xmax=80, linewidth=1, linestyles='--', color='gray')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-11-11T11:14:55.500879Z","iopub.execute_input":"2021-11-11T11:14:55.501233Z","iopub.status.idle":"2021-11-11T11:14:58.661850Z","shell.execute_reply.started":"2021-11-11T11:14:55.501196Z","shell.execute_reply":"2021-11-11T11:14:58.660905Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Posession in each quarter","metadata":{}},{"cell_type":"code","source":"fig = make_subplots(rows=1, cols=5, specs=[[{'type':'domain'} for _ in range(5)]],\n                    subplot_titles=['Total Possession',]+[f\"Possession in quarter:{q}\" for q in range(1,5)])\n_teams = pd.unique(ball['possessionTeam'])\nfig.add_trace(go.Pie(labels=_teams, values=[len(ball.query(f\"possessionTeam == '{t}'\")) for t in _teams], sort=False), 1, 1)\nfor quarter in range(1, 5):\n    fig.add_trace(go.Pie(labels=_teams, \n                         values=[len(ball.query(f\"possessionTeam == '{t}' & quarter == {quarter}\")) for t in _teams],\n                         sort=False),\n                  1, quarter+1)\nfig.update_traces(hole=.4, hoverinfo=\"label+percent\")\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:40:15.495234Z","iopub.execute_input":"2021-11-11T09:40:15.495463Z","iopub.status.idle":"2021-11-11T09:40:15.594230Z","shell.execute_reply.started":"2021-11-11T09:40:15.495436Z","shell.execute_reply":"2021-11-11T09:40:15.593371Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Scouting [WIP]","metadata":{}},{"cell_type":"code","source":"print(len(scouting))\nscouting.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-11-11T09:40:15.595773Z","iopub.execute_input":"2021-11-11T09:40:15.596565Z","iopub.status.idle":"2021-11-11T09:40:15.621627Z","shell.execute_reply.started":"2021-11-11T09:40:15.596507Z","shell.execute_reply":"2021-11-11T09:40:15.620740Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}