{"cells":[{"metadata":{},"cell_type":"markdown","source":"In this kernel I'm working with data from Google Cloud & NCAA® ML Competition 2019-Men's Challenge. We'll try to predict winners of NCAA based on previous tournaments! We have a lot of data, so let's start with EDA and then build a baseline model.\n\n![](https://i.imgur.com/jEDyzuA.png)"},{"metadata":{},"cell_type":"markdown","source":"## Import libraries"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# Libraries\nimport numpy as np\nimport pandas as pd\npd.set_option('max_columns', None)\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nplt.style.use('seaborn')\n%matplotlib inline\nimport copy\nimport datetime\nimport lightgbm as lgb\nfrom scipy import stats\nfrom sklearn.model_selection import train_test_split, StratifiedKFold, KFold, cross_val_score, GridSearchCV, RepeatedStratifiedKFold\nfrom sklearn.preprocessing import StandardScaler\nimport os\nimport plotly.offline as py\npy.init_notebook_mode(connected=True)\nimport plotly.graph_objs as go\nimport plotly.tools as tls\nimport xgboost as xgb\nimport lightgbm as lgb\nfrom sklearn import model_selection\nfrom sklearn.metrics import accuracy_score, roc_auc_score, log_loss, classification_report, confusion_matrix\nimport json\nimport ast\nimport time\nfrom sklearn import linear_model\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\nimport os\nimport glob\n\nfrom sklearn.base import BaseEstimator, TransformerMixin\nfrom sklearn.preprocessing import LabelEncoder","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Helper functions and classes"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class LGBWrapper(object):\n    \"\"\"\n    A wrapper for lightgbm model so that we will have a single api for various models.\n    \"\"\"\n\n    def __init__(self):\n        self.model = lgb.LGBMClassifier()\n\n    def fit(self, X_train, y_train, X_valid=None, y_valid=None, X_holdout=None, y_holdout=None, params=None):\n\n        eval_set = [(X_train, y_train)]\n        eval_names = ['train']\n        self.model = self.model.set_params(**params)\n\n        if X_valid is not None:\n            eval_set.append((X_valid, y_valid))\n            eval_names.append('valid')\n\n        if X_holdout is not None:\n            eval_set.append((X_holdout, y_holdout))\n            eval_names.append('holdout')\n\n        if 'cat_cols' in params.keys():\n            cat_cols = [col for col in params['cat_cols'] if col in X_train.columns]\n            if len(cat_cols) > 0:\n                categorical_columns = params['cat_cols']\n            else:\n                categorical_columns = 'auto'\n        else:\n            categorical_columns = 'auto'\n\n        self.model.fit(X=X_train, y=y_train,\n                       eval_set=eval_set, eval_names=eval_names,\n                       verbose=params['verbose'], early_stopping_rounds=params['early_stopping_rounds'])\n\n        self.best_score_ = self.model.best_score_\n        self.feature_importances_ = self.model.feature_importances_\n\n    def predict_proba(self, X_test):\n        if self.model.objective == 'binary':\n            return self.model.predict_proba(X_test, num_iteration=self.model.best_iteration_)[:, 1]\n        else:\n            return self.model.predict_proba(X_test, num_iteration=self.model.best_iteration_)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class MainTransformer(BaseEstimator, TransformerMixin):\n\n    def __init__(self, convert_cyclical: bool = False, create_interactions: bool = False, n_interactions: int = 20):\n        \"\"\"\n        Main transformer for the data. Can be used for processing on the whole data.\n\n        :param convert_cyclical: convert cyclical features into continuous\n        :param create_interactions: create interactions between features\n        \"\"\"\n\n        self.convert_cyclical = convert_cyclical\n        self.create_interactions = create_interactions\n        self.feats_for_interaction = None\n        self.n_interactions = n_interactions\n\n    def fit(self, X, y=None):\n\n        if self.create_interactions:\n            pass\n        return self\n\n    def transform(self, X, y=None):\n        data = copy.deepcopy(X)\n\n        # data['installation_event_code_count_mean'] = data.groupby(['installation_id'])['sum_event_code_count'].transform('mean')\n\n        return data\n\n    def fit_transform(self, X, y=None, **fit_params):\n        data = copy.deepcopy(X)\n        self.fit(data)\n        return self.transform(data)\n\n\nclass FeatureTransformer(BaseEstimator, TransformerMixin):\n\n    def __init__(self, main_cat_features: list = None, num_cols: list = None):\n        \"\"\"\n\n        :param main_cat_features:\n        :param num_cols:\n        \"\"\"\n        self.main_cat_features = main_cat_features\n        self.num_cols = num_cols\n\n    def fit(self, X, y=None):\n\n        self.num_cols = [col for col in X.columns if 'sum' in col or 'mean' in col or 'max' in col or 'std' in col\n                         or 'attempt' in col]\n\n        return self\n\n    def transform(self, X, y=None):\n        data = copy.deepcopy(X)\n#         for col in self.num_cols:\n#             data[f'{col}_to_mean'] = data[col] / data.groupby('installation_id')[col].transform('mean')\n#             data[f'{col}_to_std'] = data[col] / data.groupby('installation_id')[col].transform('std')\n\n        return data\n\n    def fit_transform(self, X, y=None, **fit_params):\n        data = copy.deepcopy(X)\n        self.fit(data)\n        return self.transform(data)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class ClassifierModel(object):\n    \"\"\"\n    A wrapper class for classification models.\n    It can be used for training and prediction.\n    Can plot feature importance and training progress (if relevant for model).\n\n    \"\"\"\n\n    def __init__(self, columns: list = None, model_wrapper=None):\n        \"\"\"\n\n        :param original_columns:\n        :param model_wrapper:\n        \"\"\"\n        self.columns = columns\n        self.model_wrapper = model_wrapper\n        self.result_dict = {}\n        self.train_one_fold = False\n        self.preprocesser = None\n\n    def fit(self, X: pd.DataFrame, y,\n            X_holdout: pd.DataFrame = None, y_holdout=None,\n            folds=None,\n            params: dict = None,\n            eval_metric='auc',\n            cols_to_drop: list = None,\n            preprocesser=None,\n            transformers: dict = None,\n            adversarial: bool = False,\n            plot: bool = True):\n        \"\"\"\n        Training the model.\n\n        :param X: training data\n        :param y: training target\n        :param X_holdout: holdout data\n        :param y_holdout: holdout target\n        :param folds: folds to split the data. If not defined, then model will be trained on the whole X\n        :param params: training parameters\n        :param eval_metric: metric for validataion\n        :param cols_to_drop: list of columns to drop (for example ID)\n        :param preprocesser: preprocesser class\n        :param transformers: transformer to use on folds\n        :param adversarial\n        :return:\n        \"\"\"\n        self.cols_to_drop = cols_to_drop\n\n        if folds is None:\n            folds = KFold(n_splits=3, random_state=42)\n            self.train_one_fold = True\n\n        self.columns = X.columns if self.columns is None else self.columns\n        self.feature_importances = pd.DataFrame(columns=['feature', 'importance'])\n        self.trained_transformers = {k: [] for k in transformers}\n        self.transformers = transformers\n        self.models = []\n        self.folds_dict = {}\n        self.eval_metric = eval_metric\n        n_target = 1 if len(set(y.values)) == 2 else len(set(y.values))\n        self.oof = np.zeros((len(X), n_target))\n        self.n_target = n_target\n\n        X = X[self.columns]\n        if X_holdout is not None:\n            X_holdout = X_holdout[self.columns]\n\n        if preprocesser is not None:\n            self.preprocesser = preprocesser\n            self.preprocesser.fit(X, y)\n            X = self.preprocesser.transform(X, y)\n            self.columns = X.columns.tolist()\n            if X_holdout is not None:\n                X_holdout = self.preprocesser.transform(X_holdout)\n            # y = X['accuracy_group']\n\n        for fold_n, (train_index, valid_index) in enumerate(folds.split(X, y)):\n            if X_holdout is not None:\n                X_hold = X_holdout.copy()\n            else:\n                X_hold = None\n            self.folds_dict[fold_n] = {}\n            if params['verbose']:\n                print(f'Fold {fold_n + 1} started at {time.ctime()}')\n            self.folds_dict[fold_n] = {}\n\n            X_train, X_valid = X.iloc[train_index], X.iloc[valid_index]\n            y_train, y_valid = y.iloc[train_index], y.iloc[valid_index]\n            if self.train_one_fold:\n                X_train = X[self.original_columns]\n                y_train = y\n                X_valid = None\n                y_valid = None\n\n            datasets = {'X_train': X_train, 'X_valid': X_valid, 'X_holdout': X_hold, 'y_train': y_train}\n            X_train, X_valid, X_hold = self.transform_(datasets, cols_to_drop)\n\n            self.folds_dict[fold_n]['columns'] = X_train.columns.tolist()\n\n            model = copy.deepcopy(self.model_wrapper)\n\n            if adversarial:\n                X_new1 = X_train.copy()\n                if X_valid is not None:\n                    X_new2 = X_valid.copy()\n                elif X_holdout is not None:\n                    X_new2 = X_holdout.copy()\n                X_new = pd.concat([X_new1, X_new2], axis=0)\n                y_new = np.hstack((np.zeros((X_new1.shape[0])), np.ones((X_new2.shape[0]))))\n                X_train, X_valid, y_train, y_valid = train_test_split(X_new, y_new)\n\n            model.fit(X_train, y_train, X_valid, y_valid, X_hold, y_holdout, params=params)\n\n            self.folds_dict[fold_n]['scores'] = model.best_score_\n            if self.oof.shape[0] != len(X):\n                self.oof = np.zeros((X.shape[0], self.oof.shape[1]))\n            if not adversarial:\n                self.oof[valid_index] = model.predict_proba(X_valid).reshape(-1, n_target)\n\n            fold_importance = pd.DataFrame(list(zip(X_train.columns, model.feature_importances_)),\n                                           columns=['feature', 'importance'])\n            self.feature_importances = self.feature_importances.append(fold_importance)\n            self.models.append(model)\n\n        self.feature_importances['importance'] = self.feature_importances['importance'].astype(float)\n\n        # if params['verbose']:\n        self.calc_scores_()\n\n        if plot:\n#             print(classification_report(y, self.oof.argmax(1)))\n            print(classification_report(y, (self.oof > 0.5) * 1))\n            fig, ax = plt.subplots(figsize=(16, 12))\n            plt.subplot(2, 2, 1)\n            self.plot_feature_importance(top_n=25)\n            plt.subplot(2, 2, 2)\n            self.plot_metric()\n            plt.subplot(2, 2, 3)\n            g = sns.heatmap(confusion_matrix(y, (self.oof > 0.5) * 1), annot=True, cmap=plt.cm.Blues,fmt=\"d\")\n            g.set(ylim=(-0.5, 4), xlim=(-0.5, 4), title='Confusion matrix')\n\n            plt.subplot(2, 2, 4)\n            plt.hist(self.oof)\n            plt.xticks(range(self.n_target), range(self.n_target))\n            plt.title('Distribution of oof predictions');\n\n    def transform_(self, datasets, cols_to_drop):\n        for name, transformer in self.transformers.items():\n            transformer.fit(datasets['X_train'], datasets['y_train'])\n            datasets['X_train'] = transformer.transform(datasets['X_train'])\n            if datasets['X_valid'] is not None:\n                datasets['X_valid'] = transformer.transform(datasets['X_valid'])\n            if datasets['X_holdout'] is not None:\n                datasets['X_holdout'] = transformer.transform(datasets['X_holdout'])\n            self.trained_transformers[name].append(transformer)\n        if cols_to_drop is not None:\n            cols_to_drop = [col for col in cols_to_drop if col in datasets['X_train'].columns]\n            self.cols_to_drop = cols_to_drop\n            datasets['X_train'] = datasets['X_train'].drop(cols_to_drop, axis=1)\n            if datasets['X_valid'] is not None:\n                datasets['X_valid'] = datasets['X_valid'].drop(cols_to_drop, axis=1)\n            if datasets['X_holdout'] is not None:\n                datasets['X_holdout'] = datasets['X_holdout'].drop(cols_to_drop, axis=1)\n\n        return datasets['X_train'], datasets['X_valid'], datasets['X_holdout']\n\n    def calc_scores_(self):\n        print()\n        datasets = [k for k, v in [v['scores'] for k, v in self.folds_dict.items()][0].items() if len(v) > 0]\n        self.scores = {}\n        for d in datasets:\n            scores = [v['scores'][d][self.eval_metric] for k, v in self.folds_dict.items()]\n            print(f\"CV mean score on {d}: {np.mean(scores):.4f} +/- {np.std(scores):.4f} std.\")\n            self.scores[d] = np.mean(scores)\n\n    def predict(self, X_test, averaging: str = 'usual'):\n        \"\"\"\n        Make prediction\n\n        :param X_test:\n        :param averaging: method of averaging\n        :return:\n        \"\"\"\n        full_prediction = np.zeros((X_test.shape[0], self.oof.shape[1]))\n        if self.preprocesser is not None:\n            X_test = self.preprocesser.transform(X_test)\n        for i in range(len(self.models)):\n            X_t = X_test.copy()\n            for name, transformers in self.trained_transformers.items():\n                X_t = transformers[i].transform(X_t)\n            if self.cols_to_drop:\n                cols_to_drop = [col for col in self.cols_to_drop if col in X_t.columns]\n                X_t = X_t.drop(cols_to_drop, axis=1)\n            y_pred = self.models[i].predict_proba(X_t[self.folds_dict[i]['columns']]).reshape(-1, full_prediction.shape[1])\n\n            # if case transformation changes the number of the rows\n            if full_prediction.shape[0] != len(y_pred):\n                full_prediction = np.zeros((y_pred.shape[0], self.oof.shape[1]))\n\n            if averaging == 'usual':\n                full_prediction += y_pred\n            elif averaging == 'rank':\n                full_prediction += pd.Series(y_pred).rank().values\n\n        return full_prediction / len(self.models)\n\n    def plot_feature_importance(self, drop_null_importance: bool = True, top_n: int = 10):\n        \"\"\"\n        Plot default feature importance.\n\n        :param drop_null_importance: drop columns with null feature importance\n        :param top_n: show top n columns\n        :return:\n        \"\"\"\n\n        top_feats = self.get_top_features(drop_null_importance, top_n)\n        feature_importances = self.feature_importances.loc[self.feature_importances['feature'].isin(top_feats)]\n        feature_importances['feature'] = feature_importances['feature'].astype(str)\n        top_feats = [str(i) for i in top_feats]\n        sns.barplot(data=feature_importances, x='importance', y='feature', orient='h', order=top_feats)\n        plt.title('Feature importances')\n\n    def get_top_features(self, drop_null_importance: bool = True, top_n: int = 10):\n        \"\"\"\n        Get top features by importance.\n\n        :param drop_null_importance:\n        :param top_n:\n        :return:\n        \"\"\"\n        grouped_feats = self.feature_importances.groupby(['feature'])['importance'].mean()\n        if drop_null_importance:\n            grouped_feats = grouped_feats[grouped_feats != 0]\n        return list(grouped_feats.sort_values(ascending=False).index)[:top_n]\n\n    def plot_metric(self):\n        \"\"\"\n        Plot training progress.\n        Inspired by `plot_metric` from https://lightgbm.readthedocs.io/en/latest/_modules/lightgbm/plotting.html\n\n        :return:\n        \"\"\"\n        full_evals_results = pd.DataFrame()\n        for model in self.models:\n            evals_result = pd.DataFrame()\n            for k in model.model.evals_result_.keys():\n                evals_result[k] = model.model.evals_result_[k][self.eval_metric]\n            evals_result = evals_result.reset_index().rename(columns={'index': 'iteration'})\n            full_evals_results = full_evals_results.append(evals_result)\n\n        full_evals_results = full_evals_results.melt(id_vars=['iteration']).rename(columns={'value': self.eval_metric,\n                                                                                            'variable': 'dataset'})\n        full_evals_results[self.eval_metric] = np.abs(full_evals_results[self.eval_metric])\n        sns.lineplot(data=full_evals_results, x='iteration', y=self.eval_metric, hue='dataset')\n        plt.title('Training progress')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Loading the data\n\nLet's load all useful data into a single dictionary!"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"data_dict = {}\nfor i in glob.glob('/kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MDataFiles_Stage1/*'):\n    name = i.split('/')[-1].split('.')[0]\n    if name != 'MTeamSpellings':\n        data_dict[name] = pd.read_csv(i)\n    else:\n        data_dict[name] = pd.read_csv(i, encoding='cp1252')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data overview"},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dict.keys()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We have a lot of data, but I'll use only some of it in my baseline."},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dict['MNCAATourneySeeds'].head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here we can see seeds of all the teams in all seasons."},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dict['MNCAATourneyCompactResults'].head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here we can see pairs of teams which played in tournaments."},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dict['MNCAATourneyCompactResults'].groupby(['Season'])['WScore'].mean().plot(kind='line');\nplt.title('Mean scores of winning teams by season in tourneys');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can see that mean scores peaked in ~1900 and steadily decreased since that time, but in recent years mean scores rise again. Sadly, I don't know enough info about history of matches in NCAA, but maybe teams prefer to pay more attention to defence now?"},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dict['MRegularSeasonCompactResults']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"And here we can see the results of regular seasons."},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dict['MRegularSeasonCompactResults'].groupby(['Season'])['WScore'].mean().plot();\nplt.title('Mean scores of winning teams by season in regular plays');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data processing and feature engineering.\n\nThe main idea is to extract features, which could be useful to understand how much one team is better than another one.\nAlso we take only the data before 2015, so that there are no leaks."},{"metadata":{"trusted":true},"cell_type":"code","source":"# process seed\ndata_dict['MNCAATourneySeeds'] = data_dict['MNCAATourneySeeds'].loc[data_dict['MNCAATourneySeeds']['Season'] <= 2014]\ndata_dict['MNCAATourneySeeds']['Seed'] = data_dict['MNCAATourneySeeds']['Seed'].apply(lambda x: int(x[1:3]))\n# take only useful columns\ndata_dict['MNCAATourneySeeds'] = data_dict['MNCAATourneySeeds'][['Season', 'TeamID', 'Seed']]\ndata_dict['MNCAATourneyCompactResults'] = data_dict['MNCAATourneyCompactResults'][['Season','WTeamID', 'LTeamID']]\ndata_dict['MNCAATourneyCompactResults'] = data_dict['MNCAATourneyCompactResults'].loc[data_dict['MNCAATourneyCompactResults']['Season'] <= 2014]\n# merge the data and rename the columns\ndf = pd.merge(data_dict['MNCAATourneyCompactResults'], data_dict['MNCAATourneySeeds'],\n              how='left', left_on=['Season', 'WTeamID'], right_on=['Season', 'TeamID'])\ndf = pd.merge(df, data_dict['MNCAATourneySeeds'], how='left', left_on=['Season', 'LTeamID'], right_on=['Season', 'TeamID'])\ndf = df.drop(['TeamID_x', 'TeamID_y'], axis=1)\ndf.columns = ['Season', 'WTeamID', 'LTeamID', 'WSeed', 'LSeed']\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"team_win_score = data_dict['MRegularSeasonCompactResults'].groupby(['Season', 'WTeamID']).agg({'WScore':['sum', 'count']}).reset_index()\nteam_win_score.columns = ['Season', 'WTeamID', 'WScore_sum', 'WScore_count']\nteam_loss_score = data_dict['MRegularSeasonCompactResults'].groupby(['Season', 'LTeamID']).agg({'LScore':['sum', 'count']}).reset_index()\nteam_loss_score.columns = ['Season', 'LTeamID', 'LScore_sum', 'LScore_count']\ndf = pd.merge(df, team_win_score, how='left', left_on=['Season', 'WTeamID'], right_on=['Season', 'WTeamID'])\ndf = pd.merge(df, team_loss_score, how='left', left_on=['Season', 'LTeamID'], right_on=['Season', 'LTeamID'])\ndf = pd.merge(df, team_loss_score, how='left', left_on=['Season', 'WTeamID'], right_on=['Season', 'LTeamID'])\ndf = pd.merge(df, team_win_score, how='left', left_on=['Season', 'LTeamID_x'], right_on=['Season', 'WTeamID'])\ndf.drop(['LTeamID_y', 'WTeamID_y'], axis=1, inplace=True)\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df['x_score'] = df['WScore_sum_x'] + df['LScore_sum_y']\ndf['y_score'] = df['WScore_sum_y'] + df['LScore_sum_x']\ndf['x_count'] = df['WScore_count_x'] + df['LScore_count_y']\ndf['y_count'] = df['WScore_count_y'] + df['LScore_count_x']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_win = df.copy()\ndf_los = df.copy()\ndf_win = df_win[['WSeed', 'LSeed', 'x_score', 'y_score', 'x_count', 'y_count']]\ndf_los = df_los[['LSeed', 'WSeed', 'y_score', 'x_score', 'y_count', 'x_count']]\ndf_win.columns = ['Seed_1', 'Seed_2', 'Score_1', 'Score_2', 'Count_1', 'Count_2']\ndf_los.columns = ['Seed_1', 'Seed_2', 'Score_1', 'Score_2', 'Count_1', 'Count_2']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_win['Seed_diff'] = df_win['Seed_1'] - df_win['Seed_2']\ndf_win['Score_diff'] = df_win['Score_1'] - df_win['Score_2']\ndf_los['Seed_diff'] = df_los['Seed_1'] - df_los['Seed_2']\ndf_los['Score_diff'] = df_los['Score_1'] - df_los['Score_2']\n\ndf_win['Count_diff'] = df_win['Count_1'] - df_win['Count_2']\ndf_win['Mean_score1'] = df_win['Score_1'] / df_win['Count_1']\ndf_win['Mean_score2'] = df_win['Score_2'] / df_win['Count_2']\ndf_win['Mean_score_diff'] = df_win['Mean_score1'] - df_win['Mean_score2']\ndf_los['Count_diff'] = df_los['Count_1'] - df_los['Count_2']\ndf_los['Mean_score1'] = df_los['Score_1'] / df_los['Count_1']\ndf_los['Mean_score2'] = df_los['Score_2'] / df_los['Count_2']\ndf_los['Mean_score_diff'] = df_los['Mean_score1'] - df_los['Mean_score2']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_win['result'] = 1\ndf_los['result'] = 0\ndata = pd.concat((df_win, df_los)).reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for col in ['Score_1', 'Score_2', 'Count_1', 'Count_2', 'Score_diff', 'Count_diff']:\n    print(col)\n    data[col] = data[col].fillna(0).astype(int)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Training model"},{"metadata":{"trusted":true},"cell_type":"code","source":"n_fold = 5\nfolds = RepeatedStratifiedKFold(n_splits=n_fold)\n# folds = StratifiedKFold(n_splits=n_fold)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X = data.drop(['result'], axis=1)\ny = data['result']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# some of params are from this kernel: https://www.kaggle.com/ratan123/march-madness-2020-ncaam-simple-lightgbm-on-kfold\nparam = {'n_estimators':10000,\n          'num_leaves': 400,\n          'min_child_weight': 0.034,\n          'feature_fraction': 0.379,\n          'bagging_fraction': 0.418,\n          'min_data_in_leaf': 106,\n          'objective': 'binary',\n          'max_depth': -1,\n          'learning_rate': 0.007,\n          \"boosting_type\": \"gbdt\",\n          #\"bagging_seed\": 11,\n          \"metric\": 'binary_logloss',\n          \"verbosity\": 10,\n          'reg_alpha': 0.3899,\n          'reg_lambda': 0.648,\n          'random_state': 47,\n          'task':'train', 'nthread':-1, \n         'verbose': 100,\n         'early_stopping_rounds': 30,\n         'eval_metric': 'binary_logloss'\n         }\ncat_cols = []\nmt = MainTransformer(create_interactions=False)\n# ct = CategoricalTransformer(drop_original=True, cat_cols=cat_cols)\nft = FeatureTransformer()\ntransformers = {'ft': ft}\nlgb_model = ClassifierModel(model_wrapper=LGBWrapper())\nlgb_model.fit(X=X, y=y, folds=folds, params=param, preprocesser=mt, transformers=transformers,\n                    eval_metric='binary_logloss', cols_to_drop=None, plot=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Preparing test data"},{"metadata":{"trusted":true},"cell_type":"code","source":"test = pd.read_csv('../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MSampleSubmissionStage1_2020.csv')\ntest = test.drop(['Pred'], axis=1)\ntest['Season'] = test['ID'].apply(lambda x: int(x.split('_')[0]))\ntest['Team1'] = test['ID'].apply(lambda x: int(x.split('_')[1]))\ntest['Team2'] = test['ID'].apply(lambda x: int(x.split('_')[2]))\ntest = pd.merge(test, data_dict['MNCAATourneySeeds'], how='left', left_on=['Season', 'Team1'], right_on=['Season', 'TeamID'])\ntest = pd.merge(test, data_dict['MNCAATourneySeeds'], how='left', left_on=['Season', 'Team2'], right_on=['Season', 'TeamID'])\ntest = pd.merge(test, team_win_score, how='left', left_on=['Season', 'Team1'], right_on=['Season', 'WTeamID'])\ntest = pd.merge(test, team_loss_score, how='left', left_on=['Season', 'Team2'], right_on=['Season', 'LTeamID'])\ntest = pd.merge(test, team_loss_score, how='left', left_on=['Season', 'Team1'], right_on=['Season', 'LTeamID'])\ntest = pd.merge(test, team_win_score, how='left', left_on=['Season', 'Team2'], right_on=['Season', 'WTeamID'])\ntest['seed_diff'] = test['Seed_x'] - test['Seed_y']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test['x_score'] = test['WScore_sum_x'] + test['LScore_sum_y']\ntest['y_score'] = test['WScore_sum_y'] + test['LScore_sum_x']\ntest['x_count'] = test['WScore_count_x'] + test['LScore_count_y']\ntest['y_count'] = test['WScore_count_y'] + test['WScore_count_x']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test = test[['Seed_x', 'Seed_y', 'x_score', 'y_score', 'x_count', 'y_count']]\ntest.columns = ['Seed_1', 'Seed_2', 'Score_1', 'Score_2', 'Count_1', 'Count_2']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test['Seed_diff'] = test['Seed_1'] - test['Seed_2']\ntest['Score_diff'] = test['Score_1'] - test['Score_2']\ntest['Seed_diff'] = test['Seed_1'] - test['Seed_2']\ntest['Score_diff'] = test['Score_1'] - test['Score_2']\n\ntest['Count_diff'] = test['Count_1'] - test['Count_2']\ntest['Mean_score1'] = test['Score_1'] / test['Count_1']\ntest['Mean_score2'] = test['Score_2'] / test['Count_2']\ntest['Mean_score_diff'] = test['Mean_score1'] - test['Mean_score2']\ntest['Count_diff'] = test['Count_1'] - test['Count_2']\ntest['Mean_score1'] = test['Score_1'] / test['Count_1']\ntest['Mean_score2'] = test['Score_2'] / test['Count_2']\ntest['Mean_score_diff'] = test['Mean_score1'] - test['Mean_score2']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Making predictions"},{"metadata":{"trusted":true},"cell_type":"code","source":"test_preds = lgb_model.predict(test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.hist(test_preds);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = pd.read_csv('../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/MSampleSubmissionStage1_2020.csv')\nsubmission_df['Pred'] = test_preds\nsubmission_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.to_csv('submission.csv', index=False)","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}