{"cells":[{"metadata":{},"cell_type":"markdown","source":"**NFL 1st and Future - Analytics**\nBy Ben Jenkins and Steve Jenkins\n\n**Key Findings \n**\n\nWe have verified that synthetic surfaces have a greater probability of injury.  From the data provided, synthetic surfaces have approximately 1.8 times greater injury rate compared to natural surfaces \nInjuries tend to be more severe on synthetic surfaces in terms of days missed, and injuries on synthetic turf tend to more dispersed across the field. This suggests that non-contact injuries may play a larger role in synthetic compared to natural turf. \n\nTemperature plays a role in injuries. Low temperatures (10 degrees to 40 degrees) is a predictor of low injury rates. \n\nThere are differences in speed and acceleration between the injured and non-injured population. The injured population has a higher average max speed and acceleration. For a given weight of player, greater acceleration would create more force (Force=Mass  times  Acceleration) and likelihood of injury. \nPlayers tend to be injured early in the game. This tendency is more pronounced for natural playing surfaces compared to synthetic. Early in the game players tend to run faster and may generate more force leading to increased injury rate. There is a weak negative correlation between the number of plays a player participates in during a game, and their speed. \n\nThe player location plays a role in injury incidence. A model was built to predict injuries based on the variables provided in the competition. The model's performance and findings are discussed in the presentation provided. The injury model suggests that there is a higher chance for injuries at low yardage (x axis is the long axis of field).  At high yardage, the model suggests that there is less chance for injury \n \n**Recommendations for NFL \n**\n\nSubstitute natural turf for synthetic turf. This analysis shows that this could significantly reduce the lower-limb injury incidence. Play in indoor stadiums. This allows the ability to control weather and temperature, which are shown to contribute to injuries. A variety of other factors considered in this analysis cannot be realistically addressed by the NFL, without drastic changes to the rules of the game.  We note that player speed, acceleration, number of plays, orientation and location on the field impact the probability of injury. Other factors not considered in this analysis such as type of footwear, padding, etc. may be useful to reduce injury \n\n**Recommendations for Future Analysis \n**\n\nMore data from lower-limb injuries is needed to allow the model to more accurately predict injury occurrence.  For example, collecting data on additional seasons would provide more information on injury trends. Higher quality data, such as helmet mounted accelerometers would likely improve injury prediction. Another key improvement to the model would be a detection of the exact time at which the injury occurred. This would allow analysis of the pre and post injury movements by the player. In some cases, the time of the injury may not be available.  "},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import matplotlib.pylab as plt\nimport seaborn as sns\nimport numpy as np\nimport pandas as pd\nimport pandas_profiling\n\nimport plotly.graph_objects as go\nfrom plotly.offline import download_plotlyjs, init_notebook_mode,  iplot\ninit_notebook_mode(connected=True)\nfrom PIL import Image\nfrom scipy import stats\n\npd.options.mode.chained_assignment = None","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport seaborn as sns\n\nfrom functools import reduce\n\n\nimport os \nimport gc\nimport psutil\n\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"print(os.listdir(\"../input/nfl-playing-surface-analytics/\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def reduce_mem_usage(df):\n    \"\"\" iterate through all the columns of a dataframe and modify the data type\n        to reduce memory usage.        \n    \"\"\"\n    start_mem = df.memory_usage().sum() / 1024**2\n    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))\n    \n    for col in df.columns:\n        col_type = df[col].dtype\n        \n        if col_type != object:\n            c_min = df[col].min()\n            c_max = df[col].max()\n            if str(col_type)[:3] == 'int':\n                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:\n                    df[col] = df[col].astype(np.int8)\n                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:\n                    df[col] = df[col].astype(np.int16)\n                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:\n                    df[col] = df[col].astype(np.int32)\n                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:\n                    df[col] = df[col].astype(np.int64)  \n            else:\n                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:\n                    df[col] = df[col].astype(np.float16)\n                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:\n                    df[col] = df[col].astype(np.float32)\n                else:\n                    df[col] = df[col].astype(np.float64)\n        else:\n            df[col] = df[col].astype('category')\n\n    end_mem = df.memory_usage().sum() / 1024**2\n    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))\n    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))\n    \n    return df\n\n\ndef import_data(file):\n    \"\"\"create a dataframe and optimize its memory usage\"\"\"\n    df = pd.read_csv(file, parse_dates=True, keep_date_col=True)\n    df = reduce_mem_usage(df)\n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"InjuryRecord = import_data(\"../input/nfl-playing-surface-analytics/InjuryRecord.csv\")\nPlayList = import_data(\"../input/nfl-playing-surface-analytics/PlayList.csv\")\nPlayerTrackData = import_data(\"../input/nfl-playing-surface-analytics/PlayerTrackData.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"print(PlayerTrackData.shape)\nprint(PlayList.shape)\nprint(InjuryRecord.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"PlayList.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"PlayList['StadiumType'].unique()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"array_outdoors = ['Outdoor', 'Oudoor', 'Outdoors',\n       'Ourdoor', 'Outddors', 'Heinz Field', 'Outdor', 'Outside', 'Cloudy']\narray_indoors = ['Indoors', 'Indoor', 'Indoor', 'Retractable Roof']\narray_open = ['Open','Outdoor Retr Roof-Open', 'Retr. Roof-Open', 'Indoor, Open Roof',\n       'Domed, Open', 'Domed, open', 'Retr. Roof - Open']\narray_closed = ['Closed Dome', 'Domed, closed', 'Dome', 'Domed',\n       'Retr. Roof-Closed', 'Outdoor Retr Roof-Open', 'Retractable Roof', 'Indoor, Roof Closed', 'Retr. Roof - Closed', 'Bowl', 'Dome, closed',\n       'Retr. Roof Closed']\n\nPlayList['StadiumType'] = PlayList['StadiumType'].replace(array_outdoors, 'Outdoors')\nPlayList['StadiumType'] = PlayList['StadiumType'].replace(array_indoors, 'Indoors')\nPlayList['StadiumType'] = PlayList['StadiumType'].replace(array_open, 'Open')\nPlayList['StadiumType'] = PlayList['StadiumType'].replace(array_closed, 'Closed')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"PlayList['Weather'].unique()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"array_clear = ['Clear and warm', 'Sunny', 'Clear',\n       'Sunny and warm', 'Clear and Cool',\n       'Clear and cold', 'Sunny and cold', 'Partly Sunny',\n       'Mostly Sunny', 'Clear Skies', 'Partly sunny', \n       'Sunny and clear', 'Clear skies',\n       'Sunny Skies', 'Fair', 'Partly clear', \n       'Heat Index 95', 'Sunny, highs to upper 80s', \n       'Mostly sunny', 'Sunny, Windy', 'Mostly Sunny Skies', \n       'Clear and Sunny', 'Clear and sunny',\n       'Clear to Partly Cloudy', 'Cold']\n\narray_cloudy = ['Mostly Cloudy', 'Cloudy',\n       'Cloudy, fog started developing in 2nd quarter',\n       'Partly Cloudy', 'Mostly cloudy', 'Cloudy and cold',\n       'Cloudy and Cool', 'Partly cloudy', \n       'Party Cloudy', 'Hazy', 'Partly Clouidy',\n       'Overcast', 'Cloudy, 50% change of rain',\n       'Mostly Coudy', 'Cloudy, chance of rain',\n       'Sun & clouds', 'Cloudy, Rain',\n       'cloudy', 'Coudy']\n\narray_indoors = ['Controlled Climate','Indoor',\n       'N/A (Indoors)', 'Indoors', 'N/A Indoor']\n\narray_precip = ['Rain',\n       'Snow',\n       'Scattered Showers',\n       'Light Rain',\n       'Heavy lake effect snow', 'Cloudy, Rain',\n       'Rainy',\n       'Cloudy, light snow accumulating 1-3\"',\n       'Cloudy with periods of rain, thunder possible. Winds shifting to WNW, 10-20 mph.',\n       'Rain shower', 'Rain likely, temps in low 40s.', 'Rain Chance 40%', 'Rain likely, temps in low 40s.',\n       'Cloudy, 50% change of rain', '10% Chance of Rain', 'Showers', '30% Chance of Rain']\n\nPlayList['Weather'] = PlayList['Weather'].replace(array_clear, 'Clear')\nPlayList['Weather'] = PlayList['Weather'].replace(array_cloudy, 'Cloudy')\nPlayList['Weather'] = PlayList['Weather'].replace(array_indoors, 'Indoors')\nPlayList['Weather'] = PlayList['Weather'].replace(array_precip, 'Precipitation')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"total = pd.merge(PlayList, InjuryRecord, on='PlayKey',how='left')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"final = pd.merge(total, PlayerTrackData, on='PlayKey',how='left')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"final['DM_M1'] = final['DM_M1'].fillna(0).astype(int)\nfinal['DM_M7'] = final['DM_M7'].fillna(0).astype(int)\nfinal['DM_M28'] = final['DM_M28'].fillna(0).astype(int)\nfinal['DM_M42'] = final['DM_M42'].fillna(0).astype(int)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"injury = pd.merge(InjuryRecord, PlayList, on='PlayKey',how='inner')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"injured = pd.merge(injury, PlayerTrackData, on='PlayKey',how='inner')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"injury_rate = injured['FieldType'].value_counts()/final['FieldType'].value_counts() ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = injury_rate.plot(title='Injuries Occur Over 1.8x More Often On Synthetic Fields', \n                      kind='barh', figsize=(12,8), color='#2678B2', fontsize=12)\nvals = ax.get_xticks()\nax.xaxis.label.set_size(14)\nax = plt.xlabel('Injury Rate (%)')\nax = plt.ylabel('Field Type')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"injured_syn = injured[injured['Surface']=='Synthetic']\ninjured_nat = injured[injured['Surface']=='Natural']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"inj_s_syn = injured_syn.groupby('PlayKey', as_index=False)['s'].max()\ninj_s_nat = injured_nat.groupby('PlayKey', as_index=False)['s'].max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = sns.kdeplot(data=inj_s_syn ['s'], label='Synthetic', shade=True)\nax = sns.kdeplot(data=inj_s_nat['s'], label='Natural', shade=True)\nax = plt.title(\"Distribution of Max Speed of Synthetic and Natural Turf\")\nax = plt.xlabel('Yards Per Second')\nax = plt.ylabel('Density')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"stats.ttest_ind(inj_s_syn['s'], inj_s_nat['s'], equal_var = False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"inj_dis_syn = injured_syn.groupby('PlayKey', as_index=False)['dis'].max()\ninj_dis_nat = injured_nat.groupby('PlayKey', as_index=False)['dis'].max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = sns.kdeplot(data=inj_dis_syn ['dis'], label='Synthetic', shade=True)\nax = sns.kdeplot(data=inj_dis_nat['dis'], label='Natural', shade=True)\nax = plt.title(\"Distribution of Max Distance of Synthetic and Natural Turf\")\nax = plt.xlabel('Yards Per Second')\nax = plt.ylabel('Density')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"injured_syn['a'] = (injured_syn.s - injured_syn.s.shift(1))/ (injured_syn.time - injured_syn.time.shift(1))\ninjured_syn.a.iloc[0] = 0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"injured_nat['a'] = (injured_nat.s - injured_nat.s.shift(1))/ (injured_nat.time - injured_nat.time.shift(1))\ninjured_nat.a.iloc[0] = 0 ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"inj_a_syn = injured_syn.groupby('PlayKey', as_index=False)['a'].max()\ninj_a_nat = injured_nat.groupby('PlayKey', as_index=False)['a'].max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = sns.kdeplot(data=inj_a_syn ['a'], label='Synthetic', shade=True)\nax = sns.kdeplot(data=inj_a_nat['a'], label='Natural', shade=True)\nax = plt.title(\"Distribution of Max Acceleration on Synthetic and Natural Turf\")\nax = plt.xlabel('Yards Per Second Per Second')\nax = plt.ylabel('Density')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"stats.ttest_ind(inj_a_nat['a'], inj_a_syn['a'], equal_var = False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"inj_a_syn = injured_syn.groupby('PlayKey', as_index=False)['a'].min()\ninj_a_nat = injured_nat.groupby('PlayKey', as_index=False)['a'].min()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = sns.kdeplot(data=inj_a_syn ['a'], label='Synthetic', shade=True)\nax = sns.kdeplot(data=inj_a_nat['a'], label='Natural', shade=True)\nax = plt.title(\"Distribution of Max Deceleration of Synthetic and Natural Turf\")\nax = plt.xlabel('Yards Per Second Per Second')\nax = plt.ylabel('Density')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Subset the injured dataset by surface\nsynthetic = injured.query(\"Surface == 'Synthetic'\")\nnatural = injured.query(\"Surface == 'Natural'\")\n\n# Set up the figure\nf, ax = plt.subplots(figsize=(12, 10))\nax.set_aspect(\"equal\")\n\n# Draw the two density plots\nax = sns.kdeplot(synthetic.x, synthetic.y,\n                 cmap=\"Reds\", shade=True, shade_lowest=False)\nax = sns.kdeplot(natural.x, natural.y,\n                 cmap=\"Blues\", shade=True, shade_lowest=False)\n\n# Add labels to the plot\nred = sns.color_palette(\"Reds\")[-2]\nblue = sns.color_palette(\"Blues\")[-2]\nax.text(2.5, 8.2, \"natural\", size=16, color=blue)\nax.text(2.5, 37, \"synthetic\", size=16, color=red)\nax = plt.title(\"Location of Injuries on Synthetic and Natural Turf\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = sns.kdeplot(data=injured_syn ['PlayerGamePlay'], label='Synthetic', shade=True)\nax = sns.kdeplot(data=injured_nat['PlayerGamePlay'], label='Natural', shade=True)\nax = plt.title(\"Distribution of the Number of Plays Until Injury on Synthetic and Natural Turf\")\nax = plt.xlabel('Number of Plays')\nax = plt.ylabel('Density')\nttest = stats.ttest_ind(injured_syn['PlayerGamePlay'], injured_nat['PlayerGamePlay'], equal_var = False)\nif ttest.pvalue < .05:\n    print('The difference in the number of plays until injury on synthetic and natural turf is statistically significant.')\nelse:\n    print('The difference in the number of plays until injury on synthetic and natural turf is not statistically significant.')\nprint('p-value:', '%f' % ttest.pvalue)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"syn = injured[injured['Surface']=='Synthetic']\nnat = injured[injured['Surface']=='Natural']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"playkey = np.random.choice(injured.PlayKey[~injured.PlayKey.isna()])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import plotly.express as px","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"injuries = pd.get_dummies(injured, columns = ['FieldType'], dummy_na = True, drop_first = True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"injuries.columns","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.parallel_coordinates(injuries, color=\"FieldType_Synthetic\",\n                              dimensions=['x', 'y', 's',\n                                          'o', 'dis', 'dir'],\n                              title=\"Parallel Coordinates of Tracking Data Amongst Injured Population Across Playing Surfaces\",\n                              color_continuous_scale=px.colors.diverging.Tealrose)\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.density_contour(injured, x=\"x\", y=\"y\", color=\"BodyPart\", marginal_x=\"box\", marginal_y=\"box\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.density_heatmap(injured_syn, x=\"x\", y=\"y\", marginal_x=\"histogram\", marginal_y=\"histogram\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.density_heatmap(injured_nat, x=\"x\", y=\"y\", marginal_x=\"histogram\", marginal_y=\"histogram\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.line_3d(injured, x=\"x\", y=\"y\", z=\"s\", color=\"BodyPart\", line_dash=\"BodyPart\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.line(injured, x=\"x\", y=\"y\", color='PlayKey')\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"specific_player = injured[injured['PlayKey']=='43505-2-49']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.scatter_polar(specific_player, r=\"o\", theta=\"dir\", color='time')\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"","_uuid":"","trusted":true},"cell_type":"code","source":"import pandas as pd\nstraight = pd.read_csv(\"../input/straight/straight.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.scatter_polar(straight, r=\"o\", theta=\"dir\", color='time',\n                      title=\"Example of Player Running and Facing the Same Direction\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"","_uuid":"","trusted":true},"cell_type":"code","source":"import pandas as pd\ndirections = pd.read_csv(\"../input/dummies/directions.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.scatter_polar(directions, r=\"o\", theta=\"dir\", color='time',\n                      title='Comparison of Player Running and Looking Straight, Running and Looking Left and Right')\nfig.show()","execution_count":null,"outputs":[]},{"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}