{"cells":[{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\n# from tqdm import tqdm_notebook\nimport gc\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nnp.random.seed(1)\n\nsmall_subset = True\nkernel = True\nfilesdir = 'data/'\nif kernel:\n    os.listdir(\"../input\")\n    filesdir = '../input/'\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"be029eadec7a3fd8742d2ab577d2871ecba2329a","trusted":true},"cell_type":"code","source":"dtypes = {\n        'Id'                : 'object',\n        'groupId'           : 'object',\n        'matchId'           : 'object',\n        'assists'           : 'uint8',\n        'boosts'            : 'uint8',\n        'damageDealt'       : 'float16',\n        'DBNOs'             : 'uint8',\n        'headshotKills'     : 'uint8', \n        'heals'             : 'uint8',    \n        'killPlace'         : 'uint8',    \n        'killPoints'        : 'uint8',    \n        'kills'             : 'uint8',    \n        'killStreaks'       : 'uint8',    \n        'longestKill'       : 'float16',    \n        'maxPlace'          : 'uint8',    \n        'numGroups'         : 'uint8',    \n        'revives'           : 'uint8',    \n        'rideDistance'      : 'float16',    \n        'roadKills'         : 'uint8',    \n        'swimDistance'      : 'float16',    \n        'teamKills'         : 'uint8',    \n        'vehicleDestroys'   : 'uint8',    \n        'walkDistance'      : 'float16',    \n        'weaponsAcquired'   : 'uint8',    \n        'winPoints'         : 'uint8', \n        'winPlacePerc'      : 'float16' \n}\n\n# Memory saving function credit to https://www.kaggle.com/gemartin/load-data-reduce-memory-usage\ndef reduce_mem_usage(df):\n    \"\"\" iterate through all the columns of a dataframe and modify the data type\n        to reduce memory usage.\n    \"\"\"\n    start_mem = df.memory_usage().sum() / 1024**2\n    \n    for col in df.columns:\n        col_type = df[col].dtype\n        \n        if col_type != object:\n            c_min = df[col].min()\n            c_max = df[col].max()\n            if str(col_type)[:3] == 'int':\n                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:\n                    df[col] = df[col].astype(np.int8)\n                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:\n                    df[col] = df[col].astype(np.int16)\n                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:\n                    df[col] = df[col].astype(np.int32)\n                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:\n                    df[col] = df[col].astype(np.int64)  \n            else:\n                #if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:\n                #    df[col] = df[col].astype(np.float16)\n                #el\n                if c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:\n                    df[col] = df[col].astype(np.float32)\n                else:\n                    df[col] = df[col].astype(np.float64)\n        #else:\n            #df[col] = df[col].astype('category')\n\n    end_mem = df.memory_usage().sum() / 1024**2\n    print('Memory usage of dataframe is {:.2f} MB --> {:.2f} MB (Decreased by {:.1f}%)'.format(\n        start_mem, end_mem, 100 * (start_mem - end_mem) / start_mem))\n    return df","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train = pd.read_csv(filesdir + 'train_V2.csv', dtype=dtypes)\ntest = pd.read_csv(filesdir + 'test_V2.csv', dtype=dtypes)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"48aaa5eb3770bd32b572546df009d98864831009","trusted":true},"cell_type":"code","source":"train.info()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b1518c906c9cdf1ca9eed6595fc039188dee186e","trusted":true},"cell_type":"code","source":"def reload(subset=1):\n    train = pd.read_csv(filesdir + 'train_V2.csv', dtype=dtypes)\n    test = pd.read_csv(filesdir + 'test_V2.csv', dtype=dtypes)\n    gc.collect()\n    train = train.dropna()\n    return train, test","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"211261843dfe6328c52686a4d241d8737ce0a682"},"cell_type":"markdown","source":"### Util functions"},{"metadata":{"_uuid":"92c72eda51c5c12d605e9a57348a294558205cee","trusted":true},"cell_type":"code","source":"# plot whole DataFrame\ndef hist_df(df):\n    df.hist(figsize=(15,30), layout=(10, 3))\n    plt.show()\n    \ndef plot_players_joined(df):\n    data = df[df['playersJoined']>49]\n    plt.figure(figsize=(15,10))\n    sns.countplot(data['playersJoined'])\n    plt.title(\"Players Joined\",fontsize=15)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ff7f86415ba0d5069352255516838ec3b5e04a3a"},"cell_type":"markdown","source":"### Preprocessing & data cleaning"},{"metadata":{"_uuid":"177a4ef0238e86e242b38274ebed01607e0f74dd","trusted":true},"cell_type":"code","source":"# drop nan rows\ntrain = train.dropna()\n\n# sample amount of matches to get smaller dataFrame that is suitable for feature selection\ndef get_subset_from_indexes(df, amount=500):\n    uniques = pd.Series.unique(df['matchId'])\n    assert amount <= uniques.shape[0], \"amount of matches should be less than total amount of matches\"\n    matchIds = np.random.choice(uniques, size=amount, replace=False)\n    return df[df['matchId'].isin(matchIds)]\n\n# remove outliers by clipping values by 1% - 99% value (bounding)\ndef clip(df):\n    cols_to_drop = ['Id', 'groupId', 'matchId','matchType', 'winPlacePerc']\n    features_to_fit = [col for col in df.columns if col not in cols_to_drop]\n    df[features_to_fit] = df[features_to_fit].clip(df[features_to_fit].quantile(0.01), df[features_to_fit].quantile(0.99), axis=1)    \n    return df\n\ndef normalize_df(df):\n    df = df.dropna()\n    df = clip(df)\n\ntrain, test = reload()\n    \n    ","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bfd4a18a15c35029bbdf4081ac3633f793ac8ba3","trusted":true},"cell_type":"code","source":"df = clip(train)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"77b48a053f4638ac48e06fe9371b3d07aaed703f","trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b03aa76b11921f4146c2b74bc5c06f4c649546cd"},"cell_type":"markdown","source":"### Feature engineering functions"},{"metadata":{"_uuid":"e75732ad12398c0ef189d81a669b92d00a72e1aa","trusted":true},"cell_type":"code","source":"def oneHotEncodeGameType(df, colName='matchType', prefix='match_'):\n    oneHotEncoded = pd.get_dummies(df[colName],prefix=prefix, drop_first=True)\n    returnDf = pd.concat([df, oneHotEncoded], axis=1)\n    return returnDf\n\ndef add_walk_distance_sqrt(df):\n    df['walkDistance_sqrt'] = df['walkDistance'].apply(\n        lambda x: np.sqrt(x) \n    )\n    return df\n\ndef add_walk_distance_log(df):\n    df['walkDistance_log'] = df['walkDistance'].apply(\n        lambda x: np.log(x + 1) \n    )\n    return df\n\ndef add_damage_dealt_log(df):\n    df['damageDealt_log'] = df['damageDealt'].apply(\n        lambda x: np.log(x + 1)\n    )\n    return df\n\ndef players_in_team(df):\n    agg = df.groupby(['groupId']).size().to_frame('players_in_team')\n    return df.merge(agg, how='left', on=['groupId'])\n\ndef total_distance(df):\n    df['total_distance'] = df['rideDistance'] + df['swimDistance'] + df['walkDistance']\n    return df\n\ndef items(df):\n    df['items'] = df['heals'] + df['boosts']\n    return df\n\ndef headshotKills_over_kills(df):\n    df['headshotKills_over_kills'] = df['headshotKills'] / df['kills']\n    df['headshotKills_over_kills'].fillna(0, inplace=True)\n    df['headshotKills_over_kills'].replace(np.inf, 0, inplace=True)\n    return df\n\ndef killPlace_over_maxPlace(df):\n    df['killPlace_over_maxPlace'] = df['killPlace'] / df['maxPlace']\n    df['killPlace_over_maxPlace'].fillna(0, inplace=True)\n    df['killPlace_over_maxPlace'].replace(np.inf, 0, inplace=True)\n    return df\n\ndef walkDistance_over_heals(df):\n    df['walkDistance_over_heals'] = df['walkDistance'] / df['heals']\n    df['walkDistance_over_heals'].fillna(0, inplace=True)\n    df['walkDistance_over_heals'].replace(np.inf, 0, inplace=True)\n    return df\n\ndef walkDistance_over_kills(df):\n    df['walkDistance_over_kills'] = df['walkDistance'] / df['kills']\n    df['walkDistance_over_kills'].fillna(0, inplace=True)\n    df['walkDistance_over_kills'].replace(np.inf, 0, inplace=True)\n    return df\n\ndef teamwork(df):\n    df['teamwork'] = df['assists'] + df['revives']\n    return df\n\ndef add_players_joined(df):\n    df['playersJoined'] = df.groupby('matchId')['matchId'].transform('count')\n    return df\n\ndef add_kills_norm(df):\n    df['killsPercentage'] = df['kills']*(df['playersJoined']/100)\n    return df\n\ndef add_damagedone_norm(df):\n    try: \n        df['damageDonePercentage'] = df['damageDealt'] / (100 * df['playersJoined'])\n    except:\n        pass\n    return df\n\ndef min_by_team(df, agg_cols):\n    agg = df.groupby(['matchId','groupId'])[agg_cols].min()\n    returndf = df.merge(agg, suffixes=['', '_min'], how='left', on=['matchId', 'groupId'])\n    return returndf\n\ndef max_by_team(df, agg_cols):\n    agg = df.groupby(['matchId', 'groupId'])[agg_cols].max()\n    return df.merge(agg, suffixes=['', '_max'], how='left', on=['matchId', 'groupId'])\n\ndef sum_by_team(df, agg_cols):\n    agg = df.groupby(['matchId', 'groupId'])[agg_cols].sum()\n    return df.merge(agg, suffixes=['', '_sum'], how='left', on=['matchId', 'groupId'])\n\ndef median_by_team(df, agg_cols):\n    agg = df.groupby(['matchId', 'groupId'])[agg_cols].median()\n    return df.merge(agg, suffixes=['', '_median'], how='left', on=['matchId', 'groupId'])\n\ndef mean_by_team(df, agg_cols):\n    agg = df.groupby(['matchId', 'groupId'])[agg_cols].mean()\n    return df.merge(agg, suffixes=['', '_mean'], how='left', on=['matchId', 'groupId'])\n\ndef rank_by_team(df, agg_cols):\n    agg = df.groupby(['matchId', 'groupId'])[agg_cols].mean()\n    print('aggregation cols', agg_cols)\n#     print(agg.columns)\n    agg = agg.groupby('matchId')[agg_cols].rank(pct=True)\n    return df.merge(agg, suffixes=['', '_mean_rank'], how='left', on=['matchId', 'groupId'])\n\ndef get_X_Y_fromdf(df):\n    cols_to_drop = ['Id', 'groupId', 'matchId', 'winPlacePerc']\n    fitcol = ['winPlacePerc']\n    features_to_fit = [col for col in df.columns if col not in cols_to_drop]\n    X = df[features_to_fit]\n    y = None\n    if fitcol[0] in df.columns:\n        y = df[fitcol]\n    return X, y\n\nfe_functions = [\n    clip,\n    add_players_joined,\n    add_kills_norm,\n    add_damagedone_norm,\n#     oneHotEncodeGameType,\n    add_walk_distance_sqrt,\n    add_walk_distance_log,\n    add_damage_dealt_log,\n    players_in_team,\n    total_distance,\n    items,\n    headshotKills_over_kills,\n    killPlace_over_maxPlace,\n    walkDistance_over_heals,\n    walkDistance_over_kills,\n    teamwork\n]\n\nfe_agg_functions = [\n    rank_by_team\n#     ,\n#     min_by_team,\n#     max_by_team,\n#     sum_by_team,\n#     median_by_team,\n#     mean_by_team\n]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"acc58026949cfbd553f34b9ad4902f1f0e086f0f","trusted":true},"cell_type":"code","source":"df_subset = get_subset_from_indexes(train).drop(columns='matchType')\npd.options.mode.chained_assignment = None  # disable the copy warning assignment\n# df_subset.info()\ndef add_basic_fe(df):\n#     cols_to_drop = ['Id', 'groupId', 'matchId', 'winPlacePerc']\n#     features_to_fit = [col for col in df.columns if col not in cols_to_drop]\n    start_c = len(df.columns)\n    for func in fe_functions:\n        start = len(df.columns)\n        df = func(df)\n        end = len(df.columns)\n        print(f\"Added {end - start} feature(s) with {func.__name__} function\")\n        gc.collect()\n    end_c = len(df.columns)\n    print(f'in total added {end_c - start_c} columns.')\n    return df\n\ndef add_agg_fe(df):\n    cols_to_drop = ['Id', 'groupId', 'matchId', 'winPlacePerc']\n    features_to_fit = [col for col in df.columns if col not in cols_to_drop]\n    start_c = len(df.columns)\n    print('start', df.columns)\n    for func in fe_agg_functions:\n        start = len(df.columns)\n        df = func(df, features_to_fit)\n        end = len(df.columns)\n        print(f\"Added {end - start} feature(s) with {func.__name__} function\")\n        gc.collect()\n    end_c = len(df.columns)\n    print(f'in total added {end_c - start_c} columns.')\n    return df\n\ndef add_df_features(df):\n    df_subset_with_basic = add_basic_fe(df)    \n    df_subset_with_basic_agg = add_agg_fe(df_subset_with_basic)\n    return df_subset_with_basic_agg\n\ndf_subset_with_basic_agg = add_df_features(df_subset)\ndf_subset_with_basic_agg.info()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c4896573d24dfa6d2e87566142fa0ce002daf1b3","trusted":true},"cell_type":"code","source":"# def cross_val_score_MSE(model):","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d600490f79a916b85ffbb08a2d58900526616ba0"},"cell_type":"markdown","source":"### Feature selection"},{"metadata":{"_uuid":"124ef43f75f48067824805c4cb6a9181ea45e7a6","trusted":true},"cell_type":"code","source":"from sklearn.feature_selection import SelectFromModel\nfrom lightgbm import LGBMRegressor\n\ncols_to_drop = ['Id', 'groupId', 'matchId', 'winPlacePerc']\nfitcol = ['winPlacePerc']\nfeatures_to_fit = [col for col in df_subset_with_basic_agg.columns if col not in cols_to_drop]\n# define X and y\nX_train = df_subset_with_basic_agg[features_to_fit]\ny_train = df_subset_with_basic_agg[fitcol]\n\nlgbr=LGBMRegressor(n_estimators=500, learning_rate=0.05, num_leaves=32, colsample_bytree=0.2,\n            reg_alpha=3, reg_lambda=1, min_split_gain=0.01, min_child_weight=40)\n\nembeded_lgb_selector = SelectFromModel(lgbr, threshold='1.25*median')\nembeded_lgb_selector.fit(X_train, y_train)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7fd4a94801f313a9a4c7e101ec185bf97528b2e1","trusted":true},"cell_type":"code","source":"print('Start cols len: ', len(X_train.columns))\nembeded_lgb_support = embeded_lgb_selector.get_support()\n# print(embeded_lgb_support)\nembeded_lgb_feature = X_train.loc[:,embeded_lgb_support].columns.tolist()\nprint('end cols len after feature selection: ', len(embeded_lgb_feature))\nprint(str(len(embeded_lgb_feature)), 'selected features')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"01ffc4b844f8713c38872307dad29a917a567c3d"},"cell_type":"markdown","source":"### Training and submission:"},{"metadata":{"_uuid":"295f2bac3e633f0b5fcbfe76844fb6d642aa2979","trusted":true},"cell_type":"code","source":"train, test = reload()\nprint(f'len train: {len(train)}, len_test: {len(test)}')\ntrain, test = reduce_mem_usage(train), reduce_mem_usage(test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4540a0bf67a2997bf184355c0e4336de3afbee29"},"cell_type":"code","source":"train = reduce_mem_usage(clip(train))\ntrain = add_df_features(train.drop(columns='matchType'))\ntest = reduce_mem_usage(clip(test))\ntest = add_df_features(test.drop(columns='matchType'))\nprint(f'len train: {len(train)}, len_test: {len(test)}')\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d3378270ce3c35dd6b158f18b2af75411422ddac"},"cell_type":"code","source":"# Select only most important features (115 in this case)\ntrain = train[embeded_lgb_feature]\ntest = test[embeded_lgb_feature]\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2c0bdc876801ac454d2790257960068e1044b792"},"cell_type":"code","source":"# Fit LightGbm predictor\nimport lightgbm as lgb\nparams={'learning_rate': 0.1,\n        'objective':'mae',\n        'metric':'mae',\n        'num_leaves': 31,\n        'verbose': 1,\n        'random_state':42,\n        'bagging_fraction': 0.7,\n        'feature_fraction': 0.7\n       }\n\nreg = lgb.LGBMRegressor(**params, n_estimators=200)\nX_train, y_train = get_X_Y_fromdf(train)\nreg.fit(X_train, y_train)\nX_test, _ = get_X_Y_fromdf(test)\npred = reg.predict(X_test, num_iteration=reg.best_iteration_)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"699fa0c08a53d5721e023c2e63c647b2932731f6"},"cell_type":"markdown","source":"### Output prediction"},{"metadata":{"trusted":true,"_uuid":"f03db9da123b6ec289353e4ddbb872b4907ee0f6"},"cell_type":"code","source":"# print(len(df_sub))\nprint(len(test))\nprint(len(X_test))\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1d1856a0f878fb61cfafee51d55ed164c753d271","trusted":true},"cell_type":"code","source":"df_sub = pd.read_csv(filesdir + \"sample_submission_V2.csv\")\ndf_test = pd.read_csv(filesdir + \"test_V2.csv\")\ndf_sub['winPlacePerc'] = pred\n# Restore some columns\ndf_sub = df_sub.merge(df_test[[\"Id\", \"matchId\", \"groupId\", \"maxPlace\", \"numGroups\"]], on=\"Id\", how=\"left\")\n\n# Sort, rank, and assign adjusted ratio\ndf_sub_group = df_sub.groupby([\"matchId\", \"groupId\"]).first().reset_index()\ndf_sub_group[\"rank\"] = df_sub_group.groupby([\"matchId\"])[\"winPlacePerc\"].rank()\ndf_sub_group = df_sub_group.merge(\n    df_sub_group.groupby(\"matchId\")[\"rank\"].max().to_frame(\"max_rank\").reset_index(), \n    on=\"matchId\", how=\"left\")\ndf_sub_group[\"adjusted_perc\"] = (df_sub_group[\"rank\"] - 1) / (df_sub_group[\"numGroups\"] - 1)\n\ndf_sub = df_sub.merge(df_sub_group[[\"adjusted_perc\", \"matchId\", \"groupId\"]], on=[\"matchId\", \"groupId\"], how=\"left\")\ndf_sub[\"winPlacePerc\"] = df_sub[\"adjusted_perc\"]\n\n# Deal with edge cases\ndf_sub.loc[df_sub.maxPlace == 0, \"winPlacePerc\"] = 0\ndf_sub.loc[df_sub.maxPlace == 1, \"winPlacePerc\"] = 1\n\n# Align with maxPlace\n# Credit: https://www.kaggle.com/anycode/simple-nn-baseline-4\nsubset = df_sub.loc[df_sub.maxPlace > 1]\ngap = 1.0 / (subset.maxPlace.values - 1)\nnew_perc = np.around(subset.winPlacePerc.values / gap) * gap\ndf_sub.loc[df_sub.maxPlace > 1, \"winPlacePerc\"] = new_perc\n\n# Edge case\ndf_sub.loc[(df_sub.maxPlace > 1) & (df_sub.numGroups == 1), \"winPlacePerc\"] = 0\nassert df_sub[\"winPlacePerc\"].isnull().sum() == 0\n\ndf_sub[[\"Id\", \"winPlacePerc\"]].to_csv(\"submission_adjusted.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9a738cc00b23573dd16c5140d38e4a0f98a7f253"},"cell_type":"markdown","source":"#### Credits\nSome of the parts of the code such as submission was used from "}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}