{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import pandas as pd\nimport csv\nimport seaborn as sns\nfrom matplotlib import pyplot as plt\nimport numpy as np\nimport joblib\nimport time\nimport re\nimport math\n\nfrom xgboost import XGBRegressor\nfrom sklearn.linear_model import LinearRegression, Lasso, Ridge, LogisticRegression, SGDRegressor, BayesianRidge, PassiveAggressiveRegressor\nfrom sklearn.ensemble import GradientBoostingRegressor, AdaBoostRegressor, BaggingRegressor\nfrom sklearn.preprocessing import PolynomialFeatures \nfrom sklearn.metrics import log_loss","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading Data"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"teams = pd.read_csv(\"../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MDataFiles_Stage1/MTeams.csv\")\nseasons = pd.read_csv(\"../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MDataFiles_Stage1/MSeasons.csv\")\nseeds = pd.read_csv(\"../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MDataFiles_Stage1/MNCAATourneySeeds.csv\")\nrankings = pd.read_csv(\"../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MDataFiles_Stage1/MMasseyOrdinals.csv\")\nregular_results = pd.read_csv(\"../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MDataFiles_Stage1/MRegularSeasonCompactResults.csv\")\ntourney_results = pd.read_csv(\"../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MDataFiles_Stage1/MNCAATourneyCompactResults.csv\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Exploration"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(15,5))\n\nregular_results.groupby(['Season']).mean().drop([\"DayNum\", \"WTeamID\", \"LTeamID\", \"NumOT\"], axis=1)\\\n.plot(title=\"Average Point Scored (Regular Season)\", ax = axes[0]).grid()\ntourney_results.groupby(['Season']).mean().drop([\"DayNum\", \"WTeamID\", \"LTeamID\", \"NumOT\"], axis=1)\\\n.plot(title=\"Average Point Scored (NCAA tourney)\", ax = axes[1]).grid()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = regular_results.drop([\"DayNum\", \"WTeamID\", \"WScore\", \"LTeamID\", \"LScore\", \"NumOT\"], axis=1)\\\n.groupby(['Season', \"WLoc\"]).size().unstack(fill_value=0).plot.area(title=\"Location of winning team\")\n\nax.legend([\"AWAY\", \"HOME\", \"NEUTRAL\"]);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tourney_results[tourney_results[\"DayNum\"]==154]\\\n.drop([\"Season\", \"DayNum\", \"WScore\", \"LTeamID\", \"LScore\", \"NumOT\", \"WLoc\"], axis=1)\\\n.merge(teams, left_on=\"WTeamID\", right_on=\"TeamID\", how=\"left\")\\\n.groupby([\"TeamName\"]).size().sort_values()[-10:]\\\n.plot(kind='barh', title=\"Top 10 NCAAM winners form 1985\");","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"\\nTop 10 NCAAM participants according to average ranking (from 2003)\")\nrankings.drop([\"SystemName\", \"RankingDayNum\", \"Season\"], axis=1).merge(teams[[\"TeamID\", \"TeamName\"]], on=\"TeamID\")\\\n.groupby([\"TeamName\"]).mean().sort_values(by=\"OrdinalRank\").drop([\"TeamID\"], axis=1)[:10]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Feature engineering"},{"metadata":{"trusted":true},"cell_type":"code","source":"final_rankings = rankings[(rankings[\"RankingDayNum\"]==133)]\nfinal_rankings = final_rankings.pivot_table(index=[\"TeamID\", \"Season\"], columns=\"SystemName\", values=\"OrdinalRank\", aggfunc=\"first\")\n\nmean_ranks = rankings.groupby([\"TeamID\", \"Season\", \"RankingDayNum\"]).mean()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_final_ranking(team_id, season, mean_ranks):\n    \n    if team_id not in mean_ranks.index.get_level_values(0):\n        return 1000\n    \n    years = mean_ranks.index.get_level_values(1)\n    \n    if season in years:\n        return mean_ranks.loc[team_id, season].unstack().tail(1)[0]\n    else:\n        years = years[years<season]\n        if len(years)>0:\n            return mean_ranks.loc[team_id, max(years)].unstack().tail(1)[0]\n        else:\n            return 1000","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"rounds = [[134, 135], [136, 137], [138, 139], [143, 144], [145, 146], [152], [154]]\n\ndef get_tourney_head_to_heads(results, teamA, teamB):\n    team_A_wins = []\n    team_B_wins = []\n\n    for i in range(len(rounds)):\n        team_A_wins.append(results[(results.WTeamID==teamA)\\\n                                   & (results.LTeamID==teamB) & (results.DayNum.isin(rounds[i]))].count()[0])\n        team_B_wins.append(results[(results.WTeamID==teamB)\\\n                                   & (results.LTeamID==teamA) & (results.DayNum.isin(rounds[i]))].count()[0])\n    \n    return team_A_wins, team_B_wins\n\ndef get_round(day):\n    for i in range(len(rounds)):\n        if day in rounds[i]:\n            return i\n    return -1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_matches(seeds, season):\n    matches = []\n    teams = list(seeds[seeds.Season==season].TeamID.sort_values())\n    for i in range(len(teams)):\n        for j in range(i+1, len(teams)):\n            matches.append((season, teams[i], teams[j]))\n    return matches","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_mean(values):\n    numerical = []\n    for value in values:\n        numerical.append(int(re.sub(\"[A-Za-z]\", \"\", value)))\n    return np.mean(numerical)\n\noverall_seeds = seeds.groupby(\"TeamID\").agg({'Seed': get_mean})\noverall_seeds = overall_seeds.astype('float64')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Features building\n\nBefore building the feature matrix we need to remove from data everything that is related to 2015 or later.<br>\nFurthermore we need to compute the features considering only the training set to avoid feature leakage.<br>\nI consider  all the matches before season 2015 as training set and all the matches of season 2015 and later as test set <br>\nFor the regular season matches I can consider also season 2015 matches given that are played before Selection Sunday<br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_tourney_results = tourney_results[(tourney_results.Season>2002)&(tourney_results.Season<2015)]\ntrain_regular_results = regular_results[(regular_results.Season>2002)&(regular_results.Season<=2015)]\n\ntest_tourney_results = tourney_results[(tourney_results.Season>=2015)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"regular_wins = train_regular_results.groupby([\"WTeamID\", \"WLoc\"]).count()\\\n        .unstack().drop([\"DayNum\", \"WScore\", \"LTeamID\", \"LScore\", \"NumOT\"], axis=1)\nregular_wins.columns=['A', 'H', \"N\"]\n\nregular_losses = train_regular_results.groupby([\"LTeamID\", \"WLoc\"]).count()\\\n        .unstack().drop([\"DayNum\", \"WScore\", \"WTeamID\", \"LScore\", \"NumOT\"], axis=1)\nregular_losses.columns=['H', 'A', \"N\"]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"winners = regular_results[['Season', \"WTeamID\", \"WScore\"]]\nlosers = regular_results[['Season', \"LTeamID\", \"LScore\"]]\nwinners.rename(columns={'WTeamID':'TeamID', 'WScore':'Score'}, inplace=True)\nlosers.rename(columns={'LTeamID':'TeamID', 'LScore':'Score'}, inplace=True)\n\noverall_scores = pd.concat((winners, losers))\noverall_scores = overall_scores.groupby([\"Season\", \"TeamID\"]).sum()\noverall_scores","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"columns = ['Ranking A', 'Ranking B', 'Ranking diff', \"H2H Tourney A0\", \"H2H Tourney A1\", \"H2H Tourney A2\", \"H2H Tourney A3\", \"H2H Tourney A4\", \"H2H Tourney A5\", \"H2H Tourney A6\", \"H2H Tourney B0\", \"H2H Tourney B1\", \"H2H Tourney B2\", \"H2H Tourney B3\", \"H2H Tourney B4\", \"H2H Tourney B5\", \"H2H Tourney B6\", \"Seed A\", \"Seed B\", \"Seed diff\", \"ScoreA\", \"ScoreB\", \"Score diff\"]\ndef extract_match_features(teamA, teamB, tourney_results, regular_results, regular_wins, regular_losses, mean_ranks, seeds, season):\n        features = []        \n        \n        #The overall final ranking for that year (final means the last ranking before NCAA)\n        features.append(get_final_ranking(teamA, (season), mean_ranks))\n        features.append(get_final_ranking(teamB, (season), mean_ranks))\n        features.append(features[-2]-features[-1])\n\n\n        #The number of wins for the previous head to heads of the two teams during the different phases of the tournament\n        tourney_h2h = get_tourney_head_to_heads(tourney_results, teamA, teamB)\n        features.extend(tourney_h2h[0])\n        features.extend(tourney_h2h[1])        \n        \n        #The seed of the two teams\n        seedA = int(seeds[(seeds.TeamID==teamA) & (seeds.Season==season)].Seed.values[0][1:3])\n        seedB = int(seeds[(seeds.TeamID==teamB) & (seeds.Season==season)].Seed.values[0][1:3])\n\n        features.append(seedA)\n        features.append(seedB)\n        features.append(seedA-seedB)\n        \n        #Overall scores during regular season\n        scoreA = (overall_scores.loc[season, teamA].values[0])\n        scoreB = (overall_scores.loc[season, teamB].values[0])\n        \n        features.append(scoreA)\n        features.append(scoreB)\n        features.append(scoreA-scoreB)\n        \n        return features\n\n        \ndef extract_features(results, tourney_results, regular_results, final_rankings, seeds):\n    labels = []\n    train_features = []\n    \n    regular_wins = train_regular_results.groupby([\"WTeamID\", \"WLoc\"]).count()\\\n        .unstack().drop([\"DayNum\", \"WScore\", \"LTeamID\", \"LScore\", \"NumOT\"], axis=1)\n    regular_wins.columns=['A', 'H', \"N\"]\n\n    regular_losses = train_regular_results.groupby([\"LTeamID\", \"WLoc\"]).count()\\\n        .unstack().drop([\"DayNum\", \"WScore\", \"WTeamID\", \"LScore\", \"NumOT\"], axis=1)\n    regular_losses.columns=['H', 'A', \"N\"]\n    \n    for index, match in (results).iterrows():\n                \n        teams = [match.WTeamID, match.LTeamID]\n        teamA = min(teams)\n        teamB = max(teams)\n        \n        # print(f\"{index}: {teamA}, {teamB}\")\n        \n        if teamA == match.WTeamID: \n            labels.append(1.0) \n        else: \n            labels.append(0.0)\n        features = extract_match_features(teamA, teamB, tourney_results, regular_results, regular_wins, regular_losses, mean_ranks, seeds, match.Season)\n        \n        train_features.append(features)\n        if index%1000 == 0:\n            print(f\"{index}\")\n        \n    train_features_df = pd.DataFrame(train_features)\n    train_features_df.columns = columns\n    return train_features_df, labels","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#features extraction\nX_train, y_train = extract_features(train_tourney_results, train_tourney_results, train_regular_results, mean_ranks, seeds)\n    \njoblib.dump(X_train, \"features.joblib\")\njoblib.dump(y_train, \"labels.joblib\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X_test, y_test = extract_features(test_tourney_results, train_tourney_results, train_regular_results, mean_ranks, seeds)\n\njoblib.dump(X_test, \"test_features.joblib\")\njoblib.dump(y_test, \"test_labels.joblib\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pd.set_option('display.max_columns', 100)\nX_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Features correlation matrix\")\nfig = plt.figure(figsize =(15, 15))\n\nax = fig.add_subplot(111)\ncax = ax.matshow(X_train.tail(100).corr())\n\nax.set_xticks(np.arange(len(columns)))\nax.set_yticks(np.arange(len(columns)))\n\nax.set_xticklabels(columns, rotation=90, fontsize=12)\nax.set_yticklabels(columns, fontsize=12)\n\nfig.colorbar(cax);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import Dense, Dropout, BatchNormalization\nfrom tensorflow.keras.optimizers import Adam, Adadelta, SGD, Adagrad, Nadam, Adamax, RMSprop","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# build model\n\nmodel = Sequential()\nmodel.add(Dense(128, input_shape=X_train.loc[0].shape, activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.5))\nmodel.add(Dense(128, activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.5))\nmodel.add(Dense(128, activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.5))\nmodel.add(Dense(128, activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.5))\nmodel.add(Dense(128, activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(0.5))\nmodel.add(Dense(1, activation='sigmoid'))\nmodel.compile(optimizer=Adam(0.0001), loss='binary_crossentropy', metrics=['accuracy'])\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=300)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_results(matches, model):\n    X = []\n    for i, match in enumerate(matches):\n        if i%1000 == 0:\n            print(i)\n            \n        X.append(extract_match_features(match[1], match[2], train_tourney_results, train_regular_results, regular_wins, regular_losses, mean_ranks, seeds, match[0]))\n    \n    X = pd.DataFrame(X)\n    labels = model.predict(X)\n    return X, labels","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def dump_results(matches, labels, filename):\n     with open(filename, mode='w', newline='') as file:\n        writer = csv.writer(file, delimiter=',')\n        writer.writerow(['ID', 'Pred'])\n\n        for i, match in enumerate(matches):\n            writer.writerow([f\"{match[0]}_{match[1]}_{match[2]}\", labels[i]])\n    \n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"matches = []\nfor season in range(2015, 2020):\n    matches.extend(get_matches(seeds, season))\n    \nX, predictions = get_results(matches, model)\npredictions = [a[0] for a in predictions]\npredictions -= np.min(predictions)\npredictions /= np.max(predictions) #normalization\n\ndump_results(matches, predictions, \"predictions.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dump_results(matches, predictions, \"predictions.csv\")","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":4}