{"cells":[{"metadata":{"_uuid":"bc60f411-4cb3-4aed-ae77-0ea7c6cafbdd","_cell_guid":"2164f5a7-979b-4baa-852e-78fd0544f843","trusted":true},"cell_type":"markdown","source":"# Feature engineering - 2020 NCAA Womens 🏀"},{"metadata":{"_uuid":"27fff880-7bd6-48e5-bffe-d7a15e736766","_cell_guid":"ce890a72-3bb3-48e0-a2f1-9087a5f3c16b","trusted":true},"cell_type":"markdown","source":"![](https://upload.wikimedia.org/wikipedia/en/thumb/2/28/March_Madness_logo.svg/440px-March_Madness_logo.svg.png)"},{"metadata":{"_uuid":"0ddee8ad-1898-42c9-ac95-4fb5ba673e76","_cell_guid":"19591b8b-470d-428b-94bd-94e24f3f7b22","trusted":true},"cell_type":"code","source":"# ------------------------- #\n# --- Import librairies --- #\n\nimport numpy as np \nimport pandas as pd \nfrom scipy.stats import zscore\nfrom sklearn.ensemble import RandomForestClassifier\n\nfrom plotly.offline import download_plotlyjs, init_notebook_mode, iplot\nfrom plotly.graph_objs import * \n\ninit_notebook_mode()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c51eda49-4e2e-440e-8fef-20f8a964be0c","_cell_guid":"4b4a6049-58b6-4a40-be4e-4c6606f621a8","trusted":true},"cell_type":"markdown","source":"### Gathering data"},{"metadata":{"_uuid":"60cc4a30-3ecc-4b86-b593-ccde123c95e8","_cell_guid":"868467dc-d420-44ca-ac11-22ce85302b6b","trusted":true},"cell_type":"code","source":"# -------------------- #\n# --- Data section --- # \n\n# --- Players data \n\nWPlayers = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WPlayers.csv')\n\n# --- Teams data \n\nWTeams = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WTeams.csv')\nWTeamConferences = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WTeamConferences.csv')\n\n# --- Seasons data (starting 1998)\n\nWSeasons = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WSeasons.csv')\n\n# --- Seasons tourney seeds & slots (starting 1998)\n\nWncaaSeeds = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WNCAATourneySeeds.csv')\nWncaaSlots = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WNCAATourneySlots.csv')\n\n# --- Regular seasons data (compact since 1998, detailed since 2010)\n\nWrsCompactResults = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WRegularSeasonCompactResults.csv')\nWrsDetailedResults = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WRegularSeasonDetailedResults.csv')\n\n# --- NCAA data (compact since 1998, detailed since 2010)\n\nWncaaCompactResults = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WNCAATourneyCompactResults.csv')\nWncaaDetailedResults = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WNCAATourneyDetailedResults.csv')\n\n# --- Cities data (starting 2010)\n\nWGameCities = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/WGameCities.csv')\nCities = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WDataFiles_Stage1/Cities.csv')\n\n# --- Submission files\n\nSubmissionsStage1 = pd.read_csv('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/WSampleSubmissionStage1_2020.csv')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5438d921-03bb-488e-8a1a-d01cf95df585","_cell_guid":"e459b80b-3be8-4cd3-be0e-25c8310f0197","trusted":true},"cell_type":"code","source":"# --- Define usefull functions\n\ndef logloss(true_label, predicted, eps=1e-15):\n    \"\"\"\n        Compute the logloss value of a specific prediction\n    \"\"\"\n    \n    p = np.clip(predicted, eps, 1 - eps)\n    if true_label == 1:\n        return -np.log(p)\n    return -np.log(1 - p)\n\ndef reduce_mem_usage(df, verbose=True):\n    \"\"\"\n        Usefull function to reduce the memory consumed by a dataframe\n    \"\"\"\n    \n    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']\n    start_mem = df.memory_usage().sum() / 1024**2\n    for col in df.columns:\n        col_type = df[col].dtypes\n        if col_type in numerics:\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\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\ndef compute_round(df):\n    \"\"\"\n        Compute the exact value of a round for a specific ncaa game\n    \"\"\"\n    \n    df['Round'] = 0 \n\n    for i in df.index: \n\n        if df['Season'][i]<2003 :\n\n            if df['DayNum'][i] == 137 :\n                df['Round'][i]= 1 \n            elif df['DayNum'][i] == 138: \n                df['Round'][i]= 1 \n\n            elif df['DayNum'][i] == 139 :\n                df['Round'][i]= 2 \n            elif df['DayNum'] [i] == 140 :\n                df['Round'][i]= 2 \n\n            elif df['DayNum'][i] ==145 :\n                df['Round'][i]= 3 \n            elif df['DayNum'][i] ==147 :\n                df['Round'][i]= 4 \n            elif df['DayNum'][i] ==151: \n                df['Round'][i]= 5\n            else: #df['DayNum'][i]==153:\n                df['Round'][i]= 6\n\n\n        else :   \n            df['Round'][i] = 0 \n            if df['Season'][i]<2015 : \n                if df['DayNum'][i] ==138 :\n                    df['Round'][i]= 1 \n                elif df['DayNum'][i] ==139: \n                    df['Round'][i]= 1\n                elif df['DayNum'][i] == 140 :\n                    df['Round'][i]= 2\n                elif df['DayNum'][i] ==141:\n                    df['Round'][i]= 2 \n                elif df['DayNum'][i] ==145 :\n                    df['Round'][i]= 3 \n                elif df['DayNum'][i] ==146:\n                    df['Round'][i]= 3 \n                elif df['DayNum'][i] ==147:\n                    df['Round'][i]= 4\n                elif df['DayNum'][i] ==148:\n                    df['Round'][i]= 4 \n                elif df['DayNum'][i] ==153: \n                    df['Round'][i]= 5\n                else: #df['DayNum'][i]==155:\n                    df['Round'][i]= 6\n\n            else :  \n                if df['Season'][i]<2017 : \n\n                    if df['DayNum'][i] ==137:\n                        df['Round'][i]= 1\n                    elif df['DayNum'][i] ==138:\n                        df['Round'][i]= 1 \n                    elif df['DayNum'][i] ==139 or df['DayNum'][i] ==140:\n                        df['Round'][i]= 2 \n                    elif df['DayNum'][i] ==144 or df['DayNum'][i] ==145:\n                        df['Round'][i]= 3 \n                    elif df['DayNum'][i] ==146 or df['DayNum'][i] ==147:\n                        df['Round'][i]= 4 \n                    elif df['DayNum'][i] ==153: \n                        df['Round'][i]= 5\n                    else: # df['DayNum'][i]==155:\n                        df['Round'][i]= 6\n\n                else : \n                    if df['DayNum'][i] ==137 or df['DayNum'][i] ==138:\n                        df['Round'][i]= 1 \n                    elif df['DayNum'][i] ==139 or df['DayNum'][i] ==140:\n                        df['Round'][i]= 2 \n                    elif df['DayNum'][i] ==144 or df['DayNum'][i] ==145:\n                        df['Round'][i]= 3 \n                    elif df['DayNum'][i] ==146 or df['DayNum'][i] ==147:\n                        df['Round'][i]= 4 \n                    elif df['DayNum'][i] ==151: \n                        df['Round'][i]= 5\n                    else: \n                        df['Round'][i]= 6\n    return df","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b7c08969-0efd-4520-86ad-e2deffac1690","_cell_guid":"ac60a1f5-f2a3-430d-927b-e30dd3572bdc","trusted":true},"cell_type":"markdown","source":"### Processing data"},{"metadata":{"_uuid":"75559847-43fc-41a1-95d8-161e0beefe24","_cell_guid":"0acdebde-3987-4dd9-87cb-70a58e3448f6","trusted":true},"cell_type":"code","source":"# ------------------------ #\n# --- Merging datasets --- #\n\nWrsCompactResults = reduce_mem_usage(WrsCompactResults)\nWGameCities = reduce_mem_usage(WGameCities)\nWTeams = reduce_mem_usage(WTeams)\n\nWGameCities = WGameCities.merge(Cities, how = 'left', on = 'CityID')\nWTeams = WTeams.merge(WPlayers, how = 'inner', on = 'TeamID')\n\nWrsCompactResults = WrsCompactResults\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['WTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'WSeed'})\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['LTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'LSeed'})\n\nWrsCompactResults['SeedDiff'] = WrsCompactResults.apply(lambda row : int(row['WSeed'][-2:]) - int(row['LSeed'][-2:]), axis = 1)\nWrsCompactResults['ScoreDiff'] = WrsCompactResults.apply(lambda row : int(row['WScore']) - int(row['LScore']), axis = 1)\nWrsCompactResults['ExpectedWin'] = WrsCompactResults.SeedDiff.apply(lambda x : x < 0)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"aec511b2-dff0-4764-bfff-3f206ad8a3a2","_cell_guid":"23ca2a88-97fd-4572-87ee-2ebe5fa1080c","trusted":true},"cell_type":"code","source":"WTeams.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"97bf2fbd-7991-4bbf-a171-0e76c87ef0ab","_cell_guid":"c5405d19-280f-44d0-9b11-ecd18cbc5c82","trusted":true},"cell_type":"code","source":"WPlayers.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"95a07cbe-c38c-4762-a540-4b072991d3a9","_cell_guid":"00f601bb-d404-4bb5-9ba7-e188991a2e36","trusted":true},"cell_type":"code","source":"Wrs_res = WrsCompactResults.groupby('Season').ExpectedWin.mean()\n\niplot(\n    Figure(\n        data = [Scatter(x = Wrs_res.index , y = Wrs_res.values)],\n        layout = Layout(\n            title = 'Expected win rates (WSeed > LSeed) evolution [REGULAR SEASON]',\n            yaxis = dict(title = 'Expected win rate', range = [0.4, 0.8]),\n            xaxis = dict(title = 'Season')\n        )\n    )\n)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"009d3eab-9dbf-43a2-b14c-37125fc74e30","_cell_guid":"b9be6b09-ec74-487b-b9cd-8e216f8fd47a","trusted":true},"cell_type":"code","source":"WncaaCompactResults = WncaaCompactResults\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['WTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'WSeed'})\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['LTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'LSeed'})\n\nWncaaCompactResults['SeedDiff'] = WncaaCompactResults.apply(lambda row : int(row['WSeed'][-2:]) - int(row['LSeed'][-2:]), axis = 1)\nWncaaCompactResults['ScoreDiff'] = WncaaCompactResults.apply(lambda row : int(row['WScore']) - int(row['LScore']), axis = 1)\nWncaaCompactResults['ExpectedWin'] = WncaaCompactResults.SeedDiff.apply(lambda x : x < 0)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"719de684-7304-4090-936e-2d3affa5aa02","_cell_guid":"abd7d63f-4e79-4dba-ab07-4b48b06cd514","trusted":true},"cell_type":"code","source":"WncaaCompactResults.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c5d9cded-e971-4c84-8c75-bbea1efb90ec","_cell_guid":"81bb4313-8f87-448d-b2f4-9978021d70e1","trusted":true},"cell_type":"code","source":"Wncaa_res = WncaaCompactResults.groupby('Season').ExpectedWin.mean()\n\niplot(\n    Figure(\n        data = [Scatter(x = Wncaa_res.index , y = Wncaa_res.values)],\n        layout = Layout(\n            title = 'Expected win rates (WSeed > LSeed) evolution [NCAA]',\n            yaxis = dict(title = 'Expected win rate', range = [0.4, 0.9]),\n            xaxis = dict(title = 'Season')\n        )\n    )\n)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"aa4e72fc-065a-4aad-b46e-437f4b0889f4","_cell_guid":"949e0c98-c29c-4484-b391-fd090c93f41e","trusted":true},"cell_type":"markdown","source":"The uncertainty regarding who will win only based on seed data is reducing over time. A base predictors could naively only use the seed difference to predict the winner, however some computation will have to be made to predict the outcome of a game for equaly seeded teams."},{"metadata":{"_uuid":"b7784573-c32f-4cc8-ae22-8ce2a1a58da6","_cell_guid":"66ac463c-cca6-4377-bc2e-9f7b8a3b4781","trusted":true},"cell_type":"markdown","source":"### Adding features"},{"metadata":{"_uuid":"1782b5a6-73c5-4ba5-b6c8-5d5a5303ed1a","_cell_guid":"eb629f7a-7c15-4eef-9ebf-075d78c916e2","trusted":true},"cell_type":"markdown","source":"> Here we will use the work of **Dean Oliver**, published in his book _Basketball on paper, 2002_. The four factors are to score efficiently, protect the basketball on offense, grab as many rebounds as possible, and get to the foul line as often as possible. Each of these elements are closely related to the termination of a possession for a team."},{"metadata":{"_uuid":"2e219d14-3563-443b-8522-f55c77449d88","_cell_guid":"b24d996d-1fa6-4322-94cf-85af1ccb54ee","trusted":true},"cell_type":"code","source":"# --- [Regular Season PROCESSING]\n\nWrsDetailedResults = WrsDetailedResults\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['WTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'WSeed'})\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['LTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'LSeed'})\n\nWrsDetailedResults['SeedDiff'] = WrsDetailedResults.apply(lambda row : int(row['WSeed'][-2:]) - int(row['LSeed'][-2:]), axis = 1)\nWrsDetailedResults['ScoreDiff'] = WrsDetailedResults.apply(lambda row : int(row['WScore']) - int(row['LScore']), axis = 1)\nWrsDetailedResults['ExpectedWin'] = WrsDetailedResults.SeedDiff.apply(lambda x : x < 0)\n\n# --- Computing the four factors for both teams\n\nWrsDetailedResults['WShooting'] = WrsDetailedResults.apply(lambda row : (row['WFGM'] + 0.5 * row['WFGM3'])  / row['WFGA'], axis = 1)\nWrsDetailedResults['WTurnovers'] = WrsDetailedResults.apply(lambda row : row['WTO'] / (row['WFGA'] + 0.44 * row['WFTA'] + row['WTO']), axis = 1)\nWrsDetailedResults['WORebounding'] = WrsDetailedResults.apply(lambda row : row['WOR'] / (row['WOR'] + row['LDR']), axis = 1)\nWrsDetailedResults['WDRebounding'] = WrsDetailedResults.apply(lambda row : row['WDR'] / (row['WDR'] + row['LOR']), axis = 1)\nWrsDetailedResults['WFreeThrows'] = WrsDetailedResults.apply(lambda row : row['WFTA'] / row['WFGA'], axis = 1)\n\nWrsDetailedResults['LShooting'] = WrsDetailedResults.apply(lambda row : (row['LFGM'] + 0.5 * row['LFGM3'])  / row['LFGA'], axis = 1)\nWrsDetailedResults['LTurnovers'] = WrsDetailedResults.apply(lambda row : row['LTO'] / (row['LFGA'] + 0.44 * row['LFTA'] + row['LTO']), axis = 1)\nWrsDetailedResults['LORebounding'] = WrsDetailedResults.apply(lambda row : row['LOR'] / (row['LOR'] + row['WDR']), axis = 1)\nWrsDetailedResults['LDRebounding'] = WrsDetailedResults.apply(lambda row : row['LDR'] / (row['LDR'] + row['WOR']), axis = 1)\nWrsDetailedResults['LFreeThrows'] = WrsDetailedResults.apply(lambda row : row['LFTA'] / row['LFGA'], axis = 1)\n\nWrsDetailedResults['WFourFactorsScore'] = 40 * WrsDetailedResults['WShooting']\\\n                                            - 25 * WrsDetailedResults['WTurnovers']\\\n                                            + 20 * WrsDetailedResults['WORebounding']\\\n                                            + 15 * WrsDetailedResults['WFreeThrows']\\\n                                            - 40 * WrsDetailedResults['LShooting']\\\n                                            + 25 * WrsDetailedResults['LTurnovers']\\\n                                            + 20 * WrsDetailedResults['WDRebounding']\\\n                                            - 10 * WrsDetailedResults['LFreeThrows']\n\nWrsDetailedResults['LFourFactorsScore'] = 40 * WrsDetailedResults['LShooting']\\\n                                            - 25 * WrsDetailedResults['LTurnovers']\\\n                                            + 20 * WrsDetailedResults['LORebounding']\\\n                                            + 15 * WrsDetailedResults['LFreeThrows']\\\n                                            - 40 * WrsDetailedResults['WShooting']\\\n                                            + 25 * WrsDetailedResults['WTurnovers']\\\n                                            + 20 * WrsDetailedResults['LDRebounding']\\\n                                            - 10 * WrsDetailedResults['WFreeThrows']\n\n# --- The idea is to compute a quantity that is related to the a team's state of tiredness \n\n#WrsDetailedResults['ID'] = WrsDetailedResults['Season'].astype(str) + '_' + WrsDetailedResults['WTeamID'].astype(str) + '_' + WrsDetailedResults['LTeamID'].astype(str)\n\n#OvertimesData = pd.concat([WrsDetailedResults[['Season', 'WTeamID', 'ID', 'NumOT']].rename(columns = {\"WTeamID\" : 'TeamID'}), WrsDetailedResults[['Season', 'LTeamID', 'ID', 'NumOT']].rename(columns = {\"LTeamID\" : 'TeamID'})])\n#OvertimesData = OvertimesData[['Season', 'TeamID', 'ID', 'NumOT']].groupby(by=['Season','TeamID','ID']).sum().groupby(level=[1]).cumsum().reset_index().rename(columns = {'NumOT' : 'SumOvertimePlayedBeforeGame'})\n\n#WrsDetailedResults = WrsDetailedResults.merge(OvertimesData[['ID', 'SumOvertimePlayedBeforeGame']], how = 'inner', on = 'ID').drop_duplicates()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"026968d7-fc06-431c-b882-192a7979bf46","_cell_guid":"0d421ebd-4eca-4ee2-998d-1a9a33372553","trusted":true},"cell_type":"code","source":"# We have to split the original dataframe to retrieve data for the wining AND for the loosing team at the same time\n\nWTeamListFeatures = ['WTeamID', 'WScore', 'WFGM', 'WFGA', 'WFGM3', 'WFGA3', 'WFTM', 'WFTA', 'WOR', 'WDR', 'WAst', 'WTO', 'WStl', 'WBlk', 'WPF', 'WShooting', 'WTurnovers', 'WORebounding', 'WDRebounding', 'WFreeThrows', 'WFourFactorsScore']\nWTeamListFeaturesNames = dict(zip(WTeamListFeatures, [feature[1:] if feature[0] == 'W' else feature for feature in WTeamListFeatures]))\nWTeamDataframe = WrsDetailedResults[WrsDetailedResults.Season <= 2015][WTeamListFeatures].rename(columns = WTeamListFeaturesNames)\n\nLTeamListFeatures = ['LTeamID', 'LScore', 'LFGM', 'LFGA', 'LFGM3', 'LFGA3', 'LFTM', 'LFTA', 'LOR', 'LDR', 'LAst', 'LTO', 'LStl', 'LBlk', 'LPF', 'LShooting', 'LTurnovers', 'LORebounding', 'LDRebounding', 'LFreeThrows', 'LFourFactorsScore']\nLTeamListFeaturesNames = dict(zip(LTeamListFeatures, [feature[1:] if feature[0] == 'L' else feature for feature in LTeamListFeatures]))\nLTeamDataframe = WrsDetailedResults[WrsDetailedResults.Season <= 2015][LTeamListFeatures].rename(columns = LTeamListFeaturesNames)\n\nRegularSeasonFeatures = pd.concat([WTeamDataframe, LTeamDataframe])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1355e4a5-8614-4a6d-8cf5-ca731f7ec835","_cell_guid":"81c9b7c4-326a-4eb8-9d5a-72c5ec4d4f38","trusted":true},"cell_type":"code","source":"WrsDetailedResults[['WLoc', 'SeedDiff', 'ScoreDiff', 'ExpectedWin', 'WShooting', 'WTurnovers', 'WORebounding', 'WDRebounding', 'WFreeThrows', 'LShooting', 'LTurnovers', 'LORebounding', 'LDRebounding', 'LFreeThrows', 'WFourFactorsScore', 'LFourFactorsScore']].corr()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f1953f60-7124-4b4c-982a-48672d820e0c","_cell_guid":"039feff5-2412-4968-b9dc-a5442d467d4a","trusted":true},"cell_type":"code","source":"# --- [NCAA PROCESSING]\n\nWncaaDetailedResults = WncaaDetailedResults\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['WTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'WSeed'})\\\n                        .merge(WncaaSeeds, how = 'inner', left_on = ['LTeamID', 'Season'], right_on = ['TeamID', 'Season'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'LSeed'})\n\nWncaaDetailedResults['SeedDiff'] = WncaaDetailedResults.apply(lambda row : int(row['WSeed'][-2:]) - int(row['LSeed'][-2:]), axis = 1)\nWncaaDetailedResults['ScoreDiff'] = WncaaDetailedResults.apply(lambda row : int(row['WScore']) - int(row['LScore']), axis = 1)\nWncaaDetailedResults['ExpectedWin'] = WncaaDetailedResults.SeedDiff.apply(lambda x : x < 0)\n\n# --- Computing the four factors for both teams\n\nWncaaDetailedResults['WShooting'] = WncaaDetailedResults.apply(lambda row : (row['WFGM'] + 0.5 * row['WFGM3'])  / row['WFGA'], axis = 1)\nWncaaDetailedResults['WTurnovers'] = WncaaDetailedResults.apply(lambda row : row['WTO'] / (row['WFGA'] + 0.44 * row['WFTA'] + row['WTO']), axis = 1)\nWncaaDetailedResults['WORebounding'] = WncaaDetailedResults.apply(lambda row : row['WOR'] / (row['WOR'] + row['LDR']), axis = 1)\nWncaaDetailedResults['WDRebounding'] = WncaaDetailedResults.apply(lambda row : row['WDR'] / (row['WDR'] + row['LOR']), axis = 1)\nWncaaDetailedResults['WFreeThrows'] = WncaaDetailedResults.apply(lambda row : row['WFTA'] / row['WFGA'], axis = 1)\n\nWncaaDetailedResults['LShooting'] = WncaaDetailedResults.apply(lambda row : (row['LFGM'] + 0.5 * row['LFGM3'])  / row['LFGA'], axis = 1)\nWncaaDetailedResults['LTurnovers'] = WncaaDetailedResults.apply(lambda row : row['LTO'] / (row['LFGA'] + 0.44 * row['LFTA'] + row['LTO']), axis = 1)\nWncaaDetailedResults['LORebounding'] = WncaaDetailedResults.apply(lambda row : row['LOR'] / (row['LOR'] + row['WDR']), axis = 1)\nWncaaDetailedResults['LDRebounding'] = WncaaDetailedResults.apply(lambda row : row['LDR'] / (row['LDR'] + row['WOR']), axis = 1)\nWncaaDetailedResults['LFreeThrows'] = WncaaDetailedResults.apply(lambda row : row['LFTA'] / row['LFGA'], axis = 1)\n\nWncaaDetailedResults['WFourFactorsScore'] = 40 * WncaaDetailedResults['WShooting']\\\n                                            - 25 * WncaaDetailedResults['WTurnovers']\\\n                                            + 20 * WncaaDetailedResults['WORebounding']\\\n                                            + 15 * WncaaDetailedResults['WFreeThrows']\\\n                                            - 40 * WncaaDetailedResults['LShooting']\\\n                                            + 25 * WncaaDetailedResults['LTurnovers']\\\n                                            + 20 * WncaaDetailedResults['WDRebounding']\\\n                                            - 10 * WncaaDetailedResults['LFreeThrows']\n\nWncaaDetailedResults['LFourFactorsScore'] = 40 * WncaaDetailedResults['LShooting']\\\n                                            - 25 * WncaaDetailedResults['LTurnovers']\\\n                                            + 20 * WncaaDetailedResults['LORebounding']\\\n                                            + 15 * WncaaDetailedResults['LFreeThrows']\\\n                                            - 40 * WncaaDetailedResults['WShooting']\\\n                                            + 25 * WncaaDetailedResults['WTurnovers']\\\n                                            + 20 * WncaaDetailedResults['LDRebounding']\\\n                                            - 10 * WncaaDetailedResults['WFreeThrows']\n\nWncaaDetailedResults = compute_round(WncaaDetailedResults)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8218da0a-c6e2-4fff-9ced-3ffbc68e4990","_cell_guid":"e943f7d7-3308-45d3-84bf-9a378cd04aa5","trusted":true},"cell_type":"code","source":"WncaaDetailedResults.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8e366a72-d314-4e0c-b762-44fb9daaf53d","_cell_guid":"131434e2-a139-42fb-93d0-ff32c9c86036","trusted":true},"cell_type":"code","source":"WncaaDetailedResults[['ExpectedWin', 'WFourFactorsScore', 'LFourFactorsScore']].corr()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"3b4ab011-bf3f-4b3a-99ed-b0ae4d5a2f89","_cell_guid":"84111772-89f3-4d97-bf6e-f07e3635ae09","trusted":true},"cell_type":"markdown","source":"* WFGM - field goals made (by the winning team)\n* WFGA - field goals attempted (by the winning team)\n* WFGM3 - three pointers made (by the winning team)\n* WFGA3 - three pointers attempted (by the winning team)\n* WFTM - free throws made (by the winning team)\n* WFTA - free throws attempted (by the winning team)\n* WOR - offensive rebounds (pulled by the winning team)\n* WDR - defensive rebounds (pulled by the winning team)\n* WAst - assists (by the winning team)\n* WTO - turnovers committed (by the winning team)\n* WStl - steals (accomplished by the winning team)\n* WBlk - blocks (accomplished by the winning team)\n* WPF - personal fouls committed (by the winning team)"},{"metadata":{"_uuid":"3ab11c97-a017-4476-a19b-ec26d07d77ee","_cell_guid":"e7bc24c4-9829-4767-8745-20e3884c1f28","trusted":true},"cell_type":"markdown","source":"### Using simple RandomForest to evaluate Feature Engineering\nWe have to create the same dataframe as the sample submission file, namely, only containing the id of the 2 teams, and their seed, to make predictions. But first, as features, we need to compute all the average values for each features grouped by the Season and the TeamID between 2010 and 2014. We will then use those values as input to predict, as if we were to predict teams that will play \"on their average\". However, the logistic regression model will be train on all real games of regular season between 2010 and 2014."},{"metadata":{"_uuid":"ed6e9655-f7a1-456f-ac9d-e06264d65ee5","_cell_guid":"3d476bf2-f5a1-48b7-8b82-0352a701ffc7","trusted":true},"cell_type":"markdown","source":"#### Compute input features"},{"metadata":{"_uuid":"5aa849f3-2b4c-409f-993d-c3a13187b931","_cell_guid":"b838d6e8-f707-47a0-a748-29003d2641b5","trusted":true},"cell_type":"code","source":"TrainData = WrsDetailedResults[WrsDetailedResults.Season <= 2015][['WTeamID', 'LTeamID', 'WScore', 'LScore', 'WFGM', 'WFGA', 'WFGM3', 'WFGA3', 'WFTM', 'WFTA', 'WOR', 'WDR', 'WAst', 'WTO', 'WStl', 'WBlk', 'WPF', 'LFGM', 'LFGA', 'LFGM3', 'LFGA3', 'LFTM', 'LFTA', 'LOR', 'LDR', 'LAst', 'LTO', 'LStl', 'LBlk', 'LPF', 'ScoreDiff', 'SeedDiff', 'WShooting', 'WTurnovers', 'WORebounding', 'WDRebounding', 'WFreeThrows', 'LShooting', 'LTurnovers', 'LORebounding', 'LDRebounding', 'LFreeThrows', 'WFourFactorsScore', 'LFourFactorsScore']]\n\nTrainData['FirstTeamID'] = TrainData[['WTeamID', 'LTeamID']].apply(lambda row : row['WTeamID'] if row['WTeamID'] < row['LTeamID'] else row['LTeamID'], axis = 1)\nTrainData['SecondTeamID'] = TrainData[['WTeamID', 'LTeamID']].apply(lambda row : row['WTeamID'] if row['WTeamID'] > row['LTeamID'] else row['LTeamID'], axis = 1)\n\nTrainData","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"455a500a-5aac-41e8-84ff-5e00a09816c5","_cell_guid":"c03d3362-02c6-46dd-b28d-404285a45689","trusted":true},"cell_type":"code","source":"# --- Convert current TrainData into the right format for predictions (we don't want to know who is the winner before the game)\n\nfeatures = list(filter(lambda x : x not in ['FirstTeamID', 'SecondTeamID', 'SeedDiff', 'ScoreDiff', 'TeamID'], list(set(list(map(lambda col : col[1:] if col not in ['FirstTeamID', 'SecondTeamID', 'SeedDiff', 'ScoreDiff'] else col, list(TrainData)))))))\n\nfirst_team_features = ['FirstTeam' + col for col in features]\nsecond_team_features = ['SecondTeam' + col for col in features]\n\nprocessed_features = first_team_features + second_team_features + ['SeedDiff', 'ScoreDiff', 'FirstTeamID', 'SecondTeamID', 'Label'] \n\ntrain_data_dict = {key : [] for key in processed_features}\n\nfor line in TrainData.iterrows():\n    if int(line[1].FirstTeamID) == int(line[1].WTeamID) :\n        for col in list(TrainData) :\n            if (col[0] == 'W' and col != 'WTeamID'):\n                train_data_dict['FirstTeam%s'%col[1:]].append(line[1]['%s'%col])\n                \n            elif (col[0] == 'L' and col != 'LTeamID'):\n                train_data_dict['SecondTeam%s'%col[1:]].append(line[1]['%s'%col])\n                \n        train_data_dict['SeedDiff'].append(line[1].SeedDiff)\n        train_data_dict['ScoreDiff'].append(line[1].ScoreDiff)\n        train_data_dict['FirstTeamID'].append(line[1].WTeamID)\n        train_data_dict['SecondTeamID'].append(line[1].LTeamID)\n        train_data_dict['Label'].append(1)\n                   \n    else :\n        for col in list(TrainData) :\n            if (col[0] == 'L' and col != 'LTeamID'):\n                train_data_dict['SecondTeam%s'%col[1:]].append(line[1]['%s'%col])\n                \n            elif (col[0] == 'W' and col != 'WTeamID'):\n                train_data_dict['FirstTeam%s'%col[1:]].append(line[1]['%s'%col])\n                \n        train_data_dict['SeedDiff'].append(line[1].SeedDiff)\n        train_data_dict['ScoreDiff'].append(line[1].ScoreDiff)\n        train_data_dict['FirstTeamID'].append(line[1].LTeamID)\n        train_data_dict['SecondTeamID'].append(line[1].WTeamID)\n        train_data_dict['Label'].append(0)\n\nCorrectTrainData = pd.DataFrame(train_data_dict)\nCorrectTrainData.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c236fb82-486a-41b0-922b-82fccc293f18","_cell_guid":"0f7c0719-96bc-48ce-822a-7ac7714c45a9","trusted":true},"cell_type":"code","source":"print('The two classes are relatively well balanced. The first team win with a frequence of %.2f %%'%CorrectTrainData.Label.mean())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1a541423-d24c-4d4e-b59b-4e6a7ea6e591","_cell_guid":"6de0e492-3bf1-4416-b4ce-c03af1ffde26","trusted":true},"cell_type":"code","source":"basic_features = ['FirstTeamFGM3', 'FirstTeamScore', 'FirstTeamFGA3', 'FirstTeamStl', 'FirstTeamFGM', 'FirstTeamPF', 'FirstTeamFTA', 'FirstTeamAst', 'FirstTeamFGA', 'FirstTeamBlk', 'FirstTeamDR', 'FirstTeamOR', 'FirstTeamTO', 'FirstTeamFTM']\nbasic_features = basic_features + [feature.replace('First', 'Second') for feature in basic_features] + ['SeedDiff', 'ScoreDiff']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"603b0e9e-4904-4686-9159-33f5ca51ccc2","_cell_guid":"f0448e5a-7dc6-41a4-8953-94e26c285848","trusted":true},"cell_type":"code","source":"# --- Let's build a model, using a logistic regression, to predict the probability of win for the FirstTeamID\n\nX_basic = CorrectTrainData[basic_features]\nX_enhanced = CorrectTrainData[list(CorrectTrainData)[:-3]]\n\ny = CorrectTrainData.Label\n\nbasic_RF_model = RandomForestClassifier(n_estimators = 400, random_state = 0).fit(X_basic, y)\nenhanced_RF_model = RandomForestClassifier(n_estimators = 400, random_state = 0).fit(X_enhanced, y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import RandomizedSearchCV\n\n# --- Performing grid search parameter using K-Fold Cross Validation on different set of paramaters \n\nn_estimators = [int(x) for x in np.linspace(start = 200, stop = 2000, num = 10)]\nmax_features = ['auto', 'sqrt']\nmax_depth = [int(x) for x in np.linspace(10, 110, num = 11)]\nmax_depth.append(None)\nmin_samples_split = [2, 5, 10]\nmin_samples_leaf = [1, 2, 4]\nbootstrap = [True, False]\n\nrandom_grid = {'n_estimators': n_estimators,\n               'max_features': max_features,\n               'max_depth': max_depth,\n               'min_samples_split': min_samples_split,\n               'min_samples_leaf': min_samples_leaf,\n               'bootstrap': bootstrap}\n\nrf = RandomForestClassifier()\nrf_random = RandomizedSearchCV(estimator = rf, param_distributions = random_grid, n_iter = 100, cv = 3, verbose=2, random_state=42, n_jobs = -1)\nrf_random.fit(X_basic, y)\n\nrf_random.best_params_","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"basic_RF_params = rf_random.best_params_\nbasic_RF_model = RandomForestClassifier(**basic_RF_params).fit(X_basic, y)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7f569eb4-df30-49b8-b5f3-66d8668e0050","_cell_guid":"88be9bb8-d6ae-4f6a-99a6-ae0da911b2a8","trusted":true},"cell_type":"code","source":"basic_preds = [pred[1] for pred in basic_RF_model.predict_proba(X_basic)]\nenhanced_preds = [pred[1] for pred in enhanced_RF_model.predict_proba(X_enhanced)]\n\nTrainLabelVersusPreds = CorrectTrainData.copy()[['FirstTeamID', 'SecondTeamID', 'Label']]\n\nTrainLabelVersusPreds['BasicPred'] = basic_preds\nTrainLabelVersusPreds['EnhancedPred'] = enhanced_preds\n\nTrainLabelVersusPreds['BasicLogloss'] = TrainLabelVersusPreds.apply(lambda row : logloss(row['Label'], row['BasicPred']), axis = 1)\nTrainLabelVersusPreds['EnhancedLogloss'] = TrainLabelVersusPreds.apply(lambda row : logloss(row['Label'], row['EnhancedPred']), axis = 1)\n\nTrainLabelVersusPreds.head()\n\nprint('Average logloss of the new basic model on training : %.5f'%TrainLabelVersusPreds.BasicLogloss.mean())\nprint('\\nAverage logloss of the new enhanced model on training : %.5f'%TrainLabelVersusPreds.EnhancedLogloss.mean())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"deb871d6-87b7-44e2-b00d-f33c7328ce74","_cell_guid":"c94264e3-7671-4513-8be4-c6b90078d5a6","trusted":true},"cell_type":"markdown","source":"#### Now we need to create the test dataframe"},{"metadata":{"_uuid":"773734b4-dd2c-428c-afb5-1154e532af34","_cell_guid":"25605dbe-07e8-4f63-80bd-53f6d7b0e9e8","trusted":true},"cell_type":"markdown","source":"#### Sample submission file"},{"metadata":{"_uuid":"ad274ac6-b80a-4937-bf3b-66558488cfde","_cell_guid":"4c7a5b60-2f45-49e6-8ed3-9e419cd420ab","trusted":true},"cell_type":"code","source":"SubmissionsStage1.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"37f1f945-05be-461d-a27d-af386ef9d54f","_cell_guid":"8834b29c-c70e-4e76-a0b9-1f5757da2aa1","trusted":true},"cell_type":"markdown","source":"#### Retrieve the ground truth, or labels, for what we have to predict [2015-2019]"},{"metadata":{"_uuid":"3d3eff7d-a29e-498c-bf2b-1f47098c802e","_cell_guid":"230767c3-c026-4919-919a-79ad602828f3","trusted":true},"cell_type":"code","source":"GroundTruthStage1 = WncaaCompactResults.loc[:, ['Season', 'WTeamID', 'LTeamID']]\n\nGroundTruthStage1['FirstTeamID'] = GroundTruthStage1[['WTeamID', 'LTeamID']].apply(lambda row : row['WTeamID'] if row['WTeamID'] < row['LTeamID'] else row['LTeamID'], axis = 1)\nGroundTruthStage1['SecondTeamID'] = GroundTruthStage1[['WTeamID', 'LTeamID']].apply(lambda row : row['WTeamID'] if row['WTeamID'] > row['LTeamID'] else row['LTeamID'], axis = 1)\nGroundTruthStage1['ID'] = GroundTruthStage1.apply(lambda row : str(row['Season']) + '_' + str(row['FirstTeamID']) + '_' + str(row['SecondTeamID']), axis = 1)\n\nGroundTruthStage1['Label'] = GroundTruthStage1[['WTeamID', 'FirstTeamID']].apply(lambda row : 1 if row['FirstTeamID'] == row['WTeamID'] else 0, axis = 1)\n\nGroundTruthStage1 = GroundTruthStage1[GroundTruthStage1.Season >= 2010]\n\nGroundTruthStage1.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"62a0630a-9adb-47bc-952a-2e39aabd4f88","_cell_guid":"846dcd0c-886e-46ae-a629-cb3dddafff36","trusted":true},"cell_type":"code","source":"Stage1Seeds = WncaaSeeds[WncaaSeeds.Season > 2014]\n\nSubmissionsStage1 = SubmissionsStage1.drop('Pred', axis = 1)\n\nSubmissionsStage1['Season'] = SubmissionsStage1.ID.apply(lambda x : int(x[:4]))\nSubmissionsStage1['FirstTeamID'] = SubmissionsStage1.ID.apply(lambda x : int(x[5:9]))\nSubmissionsStage1['SecondTeamID'] = SubmissionsStage1.ID.apply(lambda x : int(x[-4:]))\n\nSubmissionsStage1 = SubmissionsStage1\\\n                        .merge(Stage1Seeds, how = 'inner', left_on = ['Season', 'FirstTeamID'], right_on = ['Season', 'TeamID'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'FirstTeamSeed'})\\\n                        .merge(Stage1Seeds, how = 'inner', left_on = ['Season', 'SecondTeamID'], right_on = ['Season', 'TeamID'])\\\n                        .drop('TeamID', axis = 1)\\\n                        .rename(columns = {'Seed' : 'SecondTeamSeed'})\n\nSubmissionsStage1['SeedDiff'] = SubmissionsStage1.apply(lambda row : int(row['FirstTeamSeed'][-2:]) - int(row['SecondTeamSeed'][-2:]), axis = 1)\nSubmissionsStage1 = SubmissionsStage1.merge(GroundTruthStage1[['ID', 'Label']], how = 'inner', on = ['ID'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TestPresence = SubmissionsStage1.copy()\n\n# --- We will remove all teams from which we don't have any data between 2010 and 2015. This is OF COURSE NOT A THING TO DO as its cheating. We just want to evaluate feature engineering here.\n\nTeamList = TestFeatures.TeamID.tolist()\n\nTestPresence['FirstTeamPresent'] = SubmissionsStage1.FirstTeamID.apply(lambda x : x in TeamList)\nTestPresence['SecondTeamPresent'] = SubmissionsStage1.SecondTeamID.apply(lambda x : x in TeamList)\n\nFTeamToRemove = TestPresence[TestPresence.FirstTeamPresent == False].FirstTeamID.unique()\nSTeamToRemove = TestPresence[TestPresence.SecondTeamPresent == False].SecondTeamID.unique()\n\nTeamToRemove = list(FTeamToRemove) + list(STeamToRemove)\n\nSubmissionsStage1 = SubmissionsStage1[(~SubmissionsStage1.FirstTeamID.isin(TeamToRemove)) & (~SubmissionsStage1.SecondTeamID.isin(TeamToRemove))]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ed8b6e0a-d828-4d8e-b8e2-f3ec6ef6f0c6","_cell_guid":"f98359a8-c719-4421-9c55-56b162b13549","trusted":true},"cell_type":"markdown","source":"#### Let's build a naive model, which only take the seed difference to predict the probability of win"},{"metadata":{"_uuid":"d12a91dc-72ec-4325-a296-7c4aea4bde3f","_cell_guid":"024214bb-43bf-4f2d-9828-19f49db9a1a8","trusted":true},"cell_type":"code","source":"NaiveModelSubmissions = SubmissionsStage1.copy()\nNaiveModelSubmissions['Pred'] = NaiveModelSubmissions.SeedDiff.apply(lambda x : 0.5 if x == 0 else 1 if x < -9 else 0.8 if -9 <= x < 0 else 0.2 if 0 < x <= 9 else 0)\nNaiveModelSubmissions['LogLoss'] = NaiveModelSubmissions[['Pred', 'Label']].apply(lambda row : logloss(row['Label'], row['Pred']), axis = 1)\n\nNaiveModelSubmissions.head()\nprint('Average logloss for the naïve model : {}'.format(NaiveModelSubmissions.LogLoss.mean()))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b8adc90c-d5b7-4c33-bc89-32ab7e84c4ac","_cell_guid":"ad4a549f-ffdb-4844-990f-5cbf3962bd5c","trusted":true},"cell_type":"markdown","source":"#### Let's evaluate our (hopefully) better model"},{"metadata":{"_uuid":"6b236499-4de1-4724-82fb-03c18b7a9243","_cell_guid":"72c4b678-f1cb-40d4-8b99-a7b91a74e5a3","trusted":true},"cell_type":"code","source":"TestFeatures = RegularSeasonFeatures.groupby(['TeamID']).mean().reset_index()\nTestFeatures","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BetterModelSubmission = SubmissionsStage1.copy()\n\ntest_data_dict = {key : [] for key in processed_features[:-1] + ['ID']}\n\nfor line in BetterModelSubmission.iterrows():\n    \n    first_team = line[1].FirstTeamID\n    second_team = line[1].SecondTeamID\n    \n    test_data_dict['FirstTeamID'].append(first_team)\n    test_data_dict['SecondTeamID'].append(second_team)\n    test_data_dict['ID'].append(line[1].ID)\n    test_data_dict['SeedDiff'].append(line[1].SeedDiff)\n    \n    first_team_data = TestFeatures[TestFeatures.TeamID == first_team]\n    second_team_data = TestFeatures[TestFeatures.TeamID == second_team]\n    \n    for feature in list(TestFeatures)[1:] :\n        try :\n            test_data_dict['FirstTeam'+feature].append(first_team_data[feature].tolist()[0])\n            test_data_dict['SecondTeam'+feature].append(second_team_data[feature].tolist()[0])\n        except:\n            print(line)\n\n    test_data_dict['ScoreDiff'].append(first_team_data.Score.tolist()[0] - second_team_data.Score.tolist()[0])\n    \n# Need to compute the ScoreDiff as FirstTeamScore - SecondTeamScore\n\n#for key in test_data_dict.keys():\n#    print(key, len(test_data_dict[key]))\n\nTestData = pd.DataFrame(test_data_dict)\nTestData = TestData[list(TestData)[:-3]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"basic_test_preds = [pred[1] for pred in basic_RF_model.predict_proba(TestData[basic_features])]\nenhanced_test_preds = [pred[1] for pred in enhanced_RF_model.predict_proba(TestData)]\n\nBetterModelSubmission['BasicPred'] = basic_test_preds\nBetterModelSubmission['EnhancedPred'] = enhanced_test_preds\n\nBetterModelSubmission['BasicLogLoss'] = BetterModelSubmission.apply(lambda row : logloss(row['Label'], row['BasicPred']), axis = 1)\nBetterModelSubmission['EnhancedLogLoss'] = BetterModelSubmission.apply(lambda row : logloss(row['Label'], row['EnhancedPred']), axis = 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BetterModelSubmission.BasicLogLoss.mean()\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4111a7e5-a565-40d7-b98a-27894138eda1","_cell_guid":"8933ef09-28b1-4a83-b5e9-c32021221069","trusted":true},"cell_type":"markdown","source":"---\n## To do \n\n- ✅ Créer un identifiant unique par match\n- [ ] Enrichir le dataset Regular Seasons et NCAA avec les `cities`\n- ✅ Merge les `cities` datasets\n- ✅ Calcul des rounds (NCAA)\n- [ ] Statistiques corrélation \n- ✅ Première soumission avec calcul de perf\n\n❗️Il ne faut pas utiliser les informations de victoire ou défaite, il faut utiliser des valeurs absolues et non relatives. On doit prédire la probabilité de victoire de la `FirstTeam` donc on calculera la différence de signe entre les deux teams. Et le signe de cette différence aura un fort impact, en plus de l'amplitude de la différence.\n\n💡Faire un focus sur les matchs dont l'issue était inatendue, discerner des patterns spécifiques (clustering des matchs)  \n💡Variables à prendre en compte car on aura pas grand chose mis à part l'historique : \n - Statistiques globales d'une équipe\n \n    1. Indicateurs de performances divers, disponibles dans les `detailed` results \n    2. Résultats de calculs de metrics `Four Factors`, `Pytagorean Expectation`\n    3. Présence des joueuses star (et leur état de forme)  \n    \n    \n - Statistiques temporelles d'une équipe\n \n    1. État de forme usuel de l'équipe à la date donnée, idem pour équipe adverse\n    2. A l'inverse, l'état de fatigue est intéressant, par exemple le nombre de prolongations jouées (à court terme).\n    \n \n - Statistiques concernant la rencontre spécifique des 2 équipes (à court terme, ou pondération spécifique)\n \n    1. Rivalité entre les deux équipes\n    \n    \n - Statistiques diverses\n \n    1. Emplacement du match (si disponible, ou du moins la region)\n    2. Récence du coach (données externes)\n    3. Blessées (si disponible)"}],"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":4}