{"cells":[{"metadata":{},"cell_type":"markdown","source":"The purpose of this notebook is to provide a method for visualizing player movement, focusing on injury plays. The thought is that this may help generate ideas of how to analyze the data.\n\nAn animated plot function using Plotly is provided that shows player movement on the field, with accompanying speed and acceleration subplots."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport plotly.graph_objects as go\nfrom plotly.offline import download_plotlyjs, init_notebook_mode,  iplot\ninit_notebook_mode(connected=True)\n\npd.options.mode.chained_assignment = None","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# Read the input files\nplaylist = pd.read_csv('../input/nfl-playing-surface-analytics/PlayList.csv')\ninj = pd.read_csv('../input/nfl-playing-surface-analytics/InjuryRecord.csv')\ntrk = pd.read_csv('../input/nfl-playing-surface-analytics/PlayerTrackData.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Define a function to filter the data for a single play, and preprocess.\n\nNote that player movement prior to 'ball_snap' or 'kickoff' is removed."},{"metadata":{"trusted":true},"cell_type":"code","source":"def preprocess_samp_play(samp_play):\n\n    play_dt = trk[trk.PlayKey==samp_play]\n    dataset = play_dt.iloc[np.flatnonzero((play_dt.event == 'ball_snap') | (play_dt.event == 'kickoff'))[0]:]\n    dataset['event'].ffill(inplace=True)\n\n    # Calculate instantaneous acceleration\n    dataset['a'] = (dataset.s - dataset.s.shift(1)) / (dataset.time - dataset.time.shift(1))\n    dataset.a.iloc[0] = 0 # At the moment of ball_snap or kickoff, acceleration is likely 0\n    \n    return dataset","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Define the animated plot function."},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_injury_play(samp_play, dataset):\n\n    # Define the basic figure consisting in 6 traces: two in the field subplot,\n    # 2 in the speed subplot, and 2 in the accel subplot. These will be updated\n    # by frames:\n\n    x = dataset.x.values\n    y = dataset.y.values\n    N = dataset.shape[0] - 1\n    title_string = (samp_play+', '+\n                   inj[inj.PlayKey==samp_play].BodyPart.values[0]+', '+\n                   'M1: '+str(inj[inj.PlayKey==samp_play].DM_M1.values[0])+', '+\n                   'M7: '+str(inj[inj.PlayKey==samp_play].DM_M7.values[0])+', '+\n                   'M28: '+str(inj[inj.PlayKey==samp_play].DM_M28.values[0])+', '+\n                   'M42: '+str(inj[inj.PlayKey==samp_play].DM_M42.values[0])+', '+\n                   playlist[playlist.PlayKey==samp_play].RosterPosition.values[0]+', '+\n                   str(playlist[playlist.PlayKey==samp_play].StadiumType.values[0])+', '+\n                   playlist[playlist.PlayKey==samp_play].FieldType.values[0])\n\n    fig = dict(\n        layout = dict(height=400,\n            xaxis1 = {'domain': [0.0, 0.75], 'anchor': 'y1', 'range': [0, 120], 'tickmode': 'array',\n                      'tickvals': [0, 10, 35, 60, 85, 110, 120],\n                      'ticktext': ['End', 'G', '25', '50', '25', 'G', 'End']},\n            yaxis1 = {'domain': [0.0, 1], 'anchor': 'x1', 'range': [0, 160/3], \n                      'scaleanchor': 'x1', 'scaleratio': 1, 'tickmode': 'array',\n                      'tickvals': [0, 23.583, 29.75, 160/3],\n                      'ticktext': ['Side', 'Hash', 'Hash', 'Side']},\n            xaxis2 = {'domain': [0.8, 1], 'anchor': 'y2', 'range': [0, N]},\n            yaxis2 = {'domain': [0.0, 0.475], 'anchor': 'x2', 'range': [0, 10]},\n            xaxis3 = {'domain': [0.8, 1], 'anchor': 'y3', 'range': [0, N],\n                      'showticklabels': False},\n            yaxis3 = {'domain': [0.525, 1], 'anchor': 'x3', 'range': [-10, 10]},\n            title = {'text': title_string, 'y':0.92, 'x':0, 'xanchor': 'left', 'yanchor': 'top',\n                     'font': dict(size=12)},\n            annotations= [{\"x\": 0.9, \"y\": 0.425, \"font\": {\"size\": 12}, \"text\": \"Speed\",\n                           \"xref\": \"paper\", \"yref\": \"paper\", \"xanchor\": \"center\",\n                           \"yanchor\": \"bottom\", \"showarrow\": False},\n                          {\"x\": 0.9, \"y\": 0.95, \"font\": {\"size\": 12}, \"text\": \"Accel\",\n                           \"xref\": \"paper\", \"yref\": \"paper\", \"xanchor\": \"center\",\n                           \"yanchor\": \"bottom\", \"showarrow\": False}],\n            plot_bgcolor = 'rgba(181, 226, 141, 1)', # https://www.hexcolortool.com/#b5e28d\n            margin = {'t': 50, 'b': 50, 'l': 50, 'r': 50},\n        ),\n\n        data = [\n            {'type': 'scatter', # This trace is identified inside frames as trace 0\n             'name': 'f1', \n             'x': x, \n             'y': y, \n             'hoverinfo': 'name+text', \n             'marker': {'opacity': 1.0, 'symbol': 'circle', 'line': {'width': 0, 'color': 'rgba(50,50,50,0.8)'}},\n             'line': {'color': 'rgba(255,79,38,1.000000)'}, \n             'mode': 'lines', \n             'fillcolor': 'rgba(255,79,38,0.600000)', \n             'legendgroup': 'f1',\n             'showlegend': False, \n             'xaxis': 'x1', 'yaxis': 'y1'},\n            {'type': 'scatter', # This trace is identified inside frames as trace 1\n             'name': 'f12', \n             'x': [x[0]],\n             'y': [y[0]],\n             'mode': 'markers+text',\n             'text': dataset.event.iloc[0],\n             'textposition': 'middle left' if x[0] >= 60 else 'middle right', #'middle right',\n             'showlegend': False,\n             'marker': {'size': 10, 'color':'black'},\n             'xaxis': 'x1', 'yaxis': 'y1'},\n            {'type': 'scatter', # # This trace is identified inside frames as trace 2\n             'name': 'f2', \n             'x': list(range(N)), \n             'y': dataset.s, \n             'hoverinfo': 'name+text', \n             'marker': {'opacity': 1.0, 'symbol': 'circle', 'line': {'width': 0, 'color': 'rgba(50,50,50,0.8)'}},\n             'line': {'color': 'rgba(255,79,38,1.000000)'}, \n             'mode': 'lines', \n             'fillcolor': 'rgba(255,79,38,0.600000)', \n             'legendgroup': 'f2',\n             'showlegend': False, \n             'xaxis': 'x2', 'yaxis': 'y2'},\n            {'type': 'scatter', # This trace is identified inside frames as trace 3\n             'name': 'f22', \n             'x': [0],\n             'y': [dataset.s.iloc[0]],\n             'mode': 'markers',\n             'showlegend': False,\n             'marker': {'size': 7, 'color':'black'},\n             'xaxis': 'x2', 'yaxis': 'y2'},\n            {'type': 'scatter', # # This trace is identified inside frames as trace 4\n             'name': 'f3', \n             'x': list(range(N)), \n             'y': dataset.a, \n             'hoverinfo': 'name+text', \n             'marker': {'opacity': 1.0, 'symbol': 'circle', 'line': {'width': 0, 'color': 'rgba(50,50,50,0.8)'}},\n             'line': {'color': 'rgba(255,79,38,1.000000)'}, \n             'mode': 'lines', \n             'fillcolor': 'rgba(255,79,38,0.600000)', \n             'legendgroup': 'f2',\n             'showlegend': False, \n             'xaxis': 'x3', 'yaxis': 'y3'},\n            {'type': 'scatter', # This trace is identified inside frames as trace 5\n             'name': 'f33', \n             'x': [0],\n             'y': [dataset.a.iloc[0]],\n             'mode': 'markers',\n             'showlegend': False,\n             'marker': {'size': 7, 'color':'black'},\n             'xaxis': 'x3', 'yaxis': 'y3'},\n        ]\n\n\n    )\n\n\n    frames = [dict(name=k,\n                   data=[dict(x=x, y=y),\n                         dict(x=[x[k]], y=[y[k]], text=dataset.event.iloc[k]),\n                         dict(x=list(range(N)), y=dataset.s),\n                         dict(x=[k], y=[dataset.s.iloc[k]]),\n                         dict(x=list(range(N)), y=dataset.a),\n                         dict(x=[k], y=[dataset.a.iloc[k]]),\n                       ],\n                   traces=[0,1,2,3,4,5]) for k in range(N)]\n\n\n\n    updatemenus = [dict(type='buttons',\n                        buttons=[dict(label='Play',\n                                      method='animate',\n                                      args=[[f'{k}' for k in range(N)], \n                                             dict(frame=dict(duration=25, redraw=False), \n                                                  transition=dict(duration=0),\n                                                  easing='linear',\n                                                  fromcurrent=True,\n                                                  mode='immediate'\n                                                                     )]),\n                                 dict(label='Pause',\n                                      method='animate',\n                                      args=[[None],\n                                            dict(frame=dict(duration=0, redraw=False), \n                                                 transition=dict(duration=0),\n                                                 mode='immediate' )])],\n                        direction= 'left', \n                        pad=dict(r= 10, t=85), \n                        showactive =True, x= 0.1, y= 0, xanchor= 'right', yanchor= 'top')\n                ]\n\n\n\n    sliders = [{'yanchor': 'top',\n                'xanchor': 'left', \n                'currentvalue': {'font': {'size': 16}, 'prefix': 'Frame: ', 'visible': True, 'xanchor': 'right'},\n                'transition': {'duration': 25.0, 'easing': 'linear'},\n                'pad': {'b': 10, 't': 50}, \n                'len': 0.9, 'x': 0.1, 'y': 0, \n                'steps': [{'args': [[k], {'frame': {'duration': 25.0, 'easing': 'linear', 'redraw': False},\n                                          'transition': {'duration': 0, 'easing': 'linear'}}], \n                           'label': k, 'method': 'animate'} for k in range(N)       \n                        ]}]\n\n\n\n    fig.update(frames=frames),\n    fig['layout'].update(updatemenus=updatemenus,\n              sliders=sliders)\n\n\n\n    iplot(fig)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Select a random injury play, and plot.  Note that injury plays with PlayKey=nan are ignored.\n\nSome notes on the plot:\n* The text that follows the player's marker is the most recent event\n* The default animation speed is very slow.  I found it more useful to press Play, and/or to manually move the slider back and forth to investigate relationships of direction, speed, and acceleration.\n* The plot begins with 'ball_snap' or 'kickoff', thinking no injury will happen before those events...surely!\n* Each 'frame' of the plot is 0.1 seconds.\n* The plotting process takes more time than a typical plot because it is basically a separate plot for each of the many frames (sometimes 100+) that the animation cycles through.\n* The scaling looks great on my laptop, but looks stretched out in the width direction on my desktop.  Not sure how this can be fixed."},{"metadata":{"trusted":true},"cell_type":"code","source":"samp_play = np.random.choice(inj.PlayKey[~inj.PlayKey.isna()])\ndataset = preprocess_samp_play(samp_play)\nplot_injury_play(samp_play, dataset)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"Hope this is a useful tool. Please let me know of any questions, comments, or suggestions!"},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":1}