{"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":"# 1st and Future - Player Contact Detection","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport cv2\nimport torch\n\nfrom sklearn.metrics import matthews_corrcoef","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:08:51.186173Z","iopub.execute_input":"2022-12-23T00:08:51.186614Z","iopub.status.idle":"2022-12-23T00:08:51.192545Z","shell.execute_reply.started":"2022-12-23T00:08:51.186572Z","shell.execute_reply":"2022-12-23T00:08:51.191661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load data\n> The description of the data can be found at the kaggle competition: https://www.kaggle.com/competitions/nfl-player-contact-detection/data","metadata":{}},{"cell_type":"code","source":"DATASET_DIR = \"/kaggle/input/nfl-player-contact-detection\"\n\n# player tracking\ntrain_player_tracking_df = pd.read_csv(os.path.join(DATASET_DIR, \"train_player_tracking.csv\"), \n                                       parse_dates=[\"datetime\"])\ntest_player_tracking_df = pd.read_csv(os.path.join(DATASET_DIR, \"test_player_tracking.csv\"), \n                                      parse_dates=[\"datetime\"])\n\n# helmet detection\ntrain_helmet_detection_df = pd.read_csv(os.path.join(DATASET_DIR, \"train_baseline_helmets.csv\"))\ntest_helmet_detection_df = pd.read_csv(os.path.join(DATASET_DIR, \"test_baseline_helmets.csv\"))\n\n# video metadata\ntrain_video_metadata_df = pd.read_csv(os.path.join(DATASET_DIR, \"train_video_metadata.csv\"), \n                                      parse_dates=[\"start_time\", \"end_time\", \"snap_time\"])\ntest_video_metadata_df = pd.read_csv(os.path.join(DATASET_DIR, \"test_video_metadata.csv\"), \n                                      parse_dates=[\"start_time\", \"end_time\", \"snap_time\"])\n\n# train labels\ntrain_labels_df =  pd.read_csv(os.path.join(DATASET_DIR, \"train_labels.csv\"), \n                               parse_dates=[\"datetime\"])","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:08:51.194108Z","iopub.execute_input":"2022-12-23T00:08:51.194993Z","iopub.status.idle":"2022-12-23T00:09:27.856279Z","shell.execute_reply.started":"2022-12-23T00:08:51.194957Z","shell.execute_reply":"2022-12-23T00:09:27.854990Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Check the player tracking dataframe","metadata":{}},{"cell_type":"code","source":"train_player_tracking_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:27.858258Z","iopub.execute_input":"2022-12-23T00:09:27.858698Z","iopub.status.idle":"2022-12-23T00:09:27.881925Z","shell.execute_reply.started":"2022-12-23T00:09:27.858655Z","shell.execute_reply":"2022-12-23T00:09:27.880930Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_player_tracking_df.describe()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:27.884407Z","iopub.execute_input":"2022-12-23T00:09:27.885122Z","iopub.status.idle":"2022-12-23T00:09:28.637069Z","shell.execute_reply.started":"2022-12-23T00:09:27.885066Z","shell.execute_reply":"2022-12-23T00:09:28.635962Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_player_tracking_df.info()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:28.638874Z","iopub.execute_input":"2022-12-23T00:09:28.639278Z","iopub.status.idle":"2022-12-23T00:09:28.861221Z","shell.execute_reply.started":"2022-12-23T00:09:28.639244Z","shell.execute_reply":"2022-12-23T00:09:28.860050Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Check the helmet detection dataframe","metadata":{}},{"cell_type":"code","source":"train_helmet_detection_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:28.862894Z","iopub.execute_input":"2022-12-23T00:09:28.863325Z","iopub.status.idle":"2022-12-23T00:09:28.880555Z","shell.execute_reply.started":"2022-12-23T00:09:28.863289Z","shell.execute_reply":"2022-12-23T00:09:28.879036Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_helmet_detection_df.describe()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:28.882500Z","iopub.execute_input":"2022-12-23T00:09:28.882981Z","iopub.status.idle":"2022-12-23T00:09:29.838442Z","shell.execute_reply.started":"2022-12-23T00:09:28.882936Z","shell.execute_reply":"2022-12-23T00:09:29.837240Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_helmet_detection_df.info()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:29.840345Z","iopub.execute_input":"2022-12-23T00:09:29.840817Z","iopub.status.idle":"2022-12-23T00:09:29.853712Z","shell.execute_reply.started":"2022-12-23T00:09:29.840772Z","shell.execute_reply":"2022-12-23T00:09:29.852519Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Check the video metadata dataframe","metadata":{}},{"cell_type":"code","source":"train_video_metadata_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:29.857624Z","iopub.execute_input":"2022-12-23T00:09:29.857969Z","iopub.status.idle":"2022-12-23T00:09:29.875230Z","shell.execute_reply.started":"2022-12-23T00:09:29.857939Z","shell.execute_reply":"2022-12-23T00:09:29.874000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_video_metadata_df.describe(include=\"all\", datetime_is_numeric=True)","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:29.876689Z","iopub.execute_input":"2022-12-23T00:09:29.877038Z","iopub.status.idle":"2022-12-23T00:09:29.914003Z","shell.execute_reply.started":"2022-12-23T00:09:29.877008Z","shell.execute_reply":"2022-12-23T00:09:29.912817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_video_metadata_df.info()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:29.915713Z","iopub.execute_input":"2022-12-23T00:09:29.916224Z","iopub.status.idle":"2022-12-23T00:09:29.934341Z","shell.execute_reply.started":"2022-12-23T00:09:29.916155Z","shell.execute_reply":"2022-12-23T00:09:29.933239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Check the train labels dataframe","metadata":{}},{"cell_type":"code","source":"train_labels_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:29.935881Z","iopub.execute_input":"2022-12-23T00:09:29.936285Z","iopub.status.idle":"2022-12-23T00:09:29.954946Z","shell.execute_reply.started":"2022-12-23T00:09:29.936250Z","shell.execute_reply":"2022-12-23T00:09:29.953257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels_df.describe(include=\"all\", datetime_is_numeric=True)","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:29.956627Z","iopub.execute_input":"2022-12-23T00:09:29.958340Z","iopub.status.idle":"2022-12-23T00:09:37.356101Z","shell.execute_reply.started":"2022-12-23T00:09:29.958288Z","shell.execute_reply":"2022-12-23T00:09:37.355265Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels_df.info()","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.357336Z","iopub.execute_input":"2022-12-23T00:09:37.357633Z","iopub.status.idle":"2022-12-23T00:09:37.369665Z","shell.execute_reply.started":"2022-12-23T00:09:37.357606Z","shell.execute_reply":"2022-12-23T00:09:37.368376Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* We can see that nfl_player_id_2\tis an object and not an integer, as it can have the value 'G' to indicate a contact with the ground","metadata":{}},{"cell_type":"markdown","source":"## Data Preprocessing","metadata":{}},{"cell_type":"markdown","source":"### Extract the contact info","metadata":{}},{"cell_type":"code","source":"def extract_contact_id_info(input_df):\n    # game_key, play_id, player_1_id, player_2_id, step\n    contact_df = pd.DataFrame()\n    split = input_df['contact_id'].str.split(\"_\")\n    contact_df['contact_id'] = input_df['contact_id']\n    contact_df['game_play'] = split.str[0]+'_'+split.str[1]\n    contact_df['step'] = split.str[2].astype(\"int\")\n    contact_df['nfl_player_id_1'] = split.str[3]\n    contact_df['nfl_player_id_2'] = split.str[4]\n    # replace ground 'G' players id with '-1'\n    contact_df.loc[contact_df[\"nfl_player_id_2\"] == 'G', 'nfl_player_id_2'] = '-1'\n    contact_df['nfl_player_id_1'] = contact_df['nfl_player_id_1'].astype('int')\n    contact_df['nfl_player_id_2'] = contact_df['nfl_player_id_2'].astype('int')\n    \n    return contact_df","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.372011Z","iopub.execute_input":"2022-12-23T00:09:37.372390Z","iopub.status.idle":"2022-12-23T00:09:37.381617Z","shell.execute_reply.started":"2022-12-23T00:09:37.372355Z","shell.execute_reply":"2022-12-23T00:09:37.380570Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def add_contact_tracking_info(contact_df, tracking_df):\n    \"\"\"\n    Joins a contact dataframe with player tracking dataframe on both players\n    \"\"\"\n    player_specific_columns=[\"team\", \"position\", \"jersey_number\",\n                              \"x_position\", \"y_position\", \"speed\", \n                              \"distance\", \"direction\", \"orientation\",\n                              \"acceleration\", \"sa\"]\n    # join on player 1\n    contact_df = contact_df.merge(tracking_df, \n                                 left_on=['game_play', 'nfl_player_id_1', 'step'], \n                                 right_on=['game_play', 'nfl_player_id', 'step'], \n                                 how='left').drop_duplicates()\n    # remove the repeated column\n    contact_df.drop([\"nfl_player_id\"], axis=1, inplace=True)\n    # rename player 1 specific tracking data\n    contact_df.rename(columns=lambda x: x+'_1' if x in player_specific_columns else x, inplace=True)\n\n    # join on player 2\n    # avoid repeating the \"game_key\", \"play_id\", 'datetime' columns\n    contact_df = contact_df.merge(tracking_df.drop(['game_key', 'play_id', 'datetime'], axis=1), \n                                 left_on=['game_play', 'nfl_player_id_2', 'step'], \n                                 right_on=['game_play', 'nfl_player_id', 'step'], \n                                 how='left').drop_duplicates()\n    # remove the repeated column\n    contact_df.drop(\"nfl_player_id\", axis=1, inplace=True)\n    # rename player 2 specific tracking data\n    contact_df.rename(columns=lambda x: x+'_2' if x in player_specific_columns else x, inplace=True)\n    contact_df.replace(np.nan, 0, inplace=True)\n    \n    return contact_df","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.383230Z","iopub.execute_input":"2022-12-23T00:09:37.383703Z","iopub.status.idle":"2022-12-23T00:09:37.397568Z","shell.execute_reply.started":"2022-12-23T00:09:37.383660Z","shell.execute_reply":"2022-12-23T00:09:37.396545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def compute_distance(contact_df):\n    # compute the distance between 2 players\n    contact_df[\"p2p_distance\"] = np.sqrt(\n        np.square(contact_df[\"x_position_1\"] - contact_df[\"x_position_2\"])\n        + np.square(contact_df[\"y_position_1\"] - contact_df[\"y_position_2\"])\n    )\n    # set the distance between a player and the ground to 999\n    contact_df.loc[contact_df[\"nfl_player_id_2\"]==-1, 'p2p_distance'] = 999\n    contact_df['p2p_distance'] = contact_df['p2p_distance'].fillna(999)\n    return contact_df","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.399190Z","iopub.execute_input":"2022-12-23T00:09:37.399907Z","iopub.status.idle":"2022-12-23T00:09:37.416245Z","shell.execute_reply.started":"2022-12-23T00:09:37.399863Z","shell.execute_reply":"2022-12-23T00:09:37.414778Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Final preprocessing function","metadata":{}},{"cell_type":"code","source":"def preprocess_input(input_df, tracking_df):\n    \"\"\"Returns the preprocessed dataframe with all the expanded contact_id info, tracking data, distance\n    and ground truth\"\"\"\n    # train tracking df contains all data from the test tracking df\n    contact_df = extract_contact_id_info(input_df)\n    # add tracking info to the contact df\n    contact_df = add_contact_tracking_info(contact_df, tracking_df)\n    contact_df = contact_df.fillna(0)\n    # compute player distance\n    contact_df = compute_distance(contact_df)\n\n    return contact_df","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.417705Z","iopub.execute_input":"2022-12-23T00:09:37.418635Z","iopub.status.idle":"2022-12-23T00:09:37.435230Z","shell.execute_reply.started":"2022-12-23T00:09:37.418599Z","shell.execute_reply":"2022-12-23T00:09:37.434216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# contact_df = preprocess_input(train_labels_df)\n# # add ground truth data\n# contact_df = add_ground_truth_data(contact_df, train_labels_df)\n\n# print(contact_df.columns)\n# contact_df","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.436502Z","iopub.execute_input":"2022-12-23T00:09:37.436820Z","iopub.status.idle":"2022-12-23T00:09:37.448526Z","shell.execute_reply.started":"2022-12-23T00:09:37.436791Z","shell.execute_reply":"2022-12-23T00:09:37.447217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# contacts = contact_df['contact_truth'].values.sum()\n# print(f\"There are {contacts} positive contacts out of {len(contact_df)} input contact_ids\")","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.450177Z","iopub.execute_input":"2022-12-23T00:09:37.450625Z","iopub.status.idle":"2022-12-23T00:09:37.459064Z","shell.execute_reply.started":"2022-12-23T00:09:37.450578Z","shell.execute_reply":"2022-12-23T00:09:37.458171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Naive Prediction","metadata":{}},{"cell_type":"code","source":"# best_score = 0\n# best_thrs = 0\n# # find the best distance threshold with steps of 0.1\n# for thrs in np.arange(0.0, 5.0, 0.1):\n#     predictions = (contact_df['p2p_distance'] < thrs).astype(int)\n#     score = matthews_corrcoef(contact_df['contact_truth'], predictions)\n#     if score > best_score:\n#         best_score = score\n#         best_thrs = thrs\n        \n# contact_df['contact'] = (contact_df['p2p_distance'] < best_thrs).astype(int)\n# print(f'Matthew’s correlation coefficient = {best_score} for distance threshold: {best_thrs:.2f}')","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.460942Z","iopub.execute_input":"2022-12-23T00:09:37.461394Z","iopub.status.idle":"2022-12-23T00:09:37.472463Z","shell.execute_reply.started":"2022-12-23T00:09:37.461352Z","shell.execute_reply":"2022-12-23T00:09:37.471217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# contact_df['contact'] = (contact_df['p2p_distance'] < 1.2).astype(int)\n# score = matthews_corrcoef(contact_df['contact_truth'], contact_df['contact'])\n# print(score)","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:37.474124Z","iopub.execute_input":"2022-12-23T00:09:37.475361Z","iopub.status.idle":"2022-12-23T00:09:37.483933Z","shell.execute_reply.started":"2022-12-23T00:09:37.475315Z","shell.execute_reply":"2022-12-23T00:09:37.483062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create submission","metadata":{}},{"cell_type":"code","source":"# load the submission dataframe\ninput_df = pd.read_csv(os.path.join(DATASET_DIR, \"sample_submission.csv\"))\n\nTHRES = 1.1\n\ncontact_df = preprocess_input(input_df, test_player_tracking_df)\n# contact_df = add_ground_truth_data(contact_df, train_labels_df)\ncontact_df['contact'] = (contact_df['p2p_distance'] < THRES).astype(int)\n\n\nsubmission = contact_df[[\"contact_id\", \"contact\"]].copy()\nsubmission['contact'] = submission['contact'].fillna(0).astype('int')\nsubmission[[\"contact_id\", \"contact\"]].to_csv(\"submission.csv\", index=False)\n\nsubmission","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:10:54.363604Z","iopub.execute_input":"2022-12-23T00:10:54.364137Z","iopub.status.idle":"2022-12-23T00:10:55.239069Z","shell.execute_reply.started":"2022-12-23T00:10:54.364091Z","shell.execute_reply":"2022-12-23T00:10:55.237800Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# matthews_corrcoef(contact_df['contact_truth'], contact_df['contact'])","metadata":{"execution":{"iopub.status.busy":"2022-12-23T00:09:38.379865Z","iopub.execute_input":"2022-12-23T00:09:38.380274Z","iopub.status.idle":"2022-12-23T00:09:38.385489Z","shell.execute_reply.started":"2022-12-23T00:09:38.380239Z","shell.execute_reply":"2022-12-23T00:09:38.384119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}