{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# 0. 导入必要的库\nimport pandas as pd\nimport numpy as np\nimport os\nfrom tqdm import tqdm\nfrom collections import Counter\nimport itertools\nfrom matplotlib import pyplot as plt\nimport matplotlib.cm as cm\nimport seaborn as sns\n\n# 特征工程\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.metrics import f1_score\nfrom sklearn.preprocessing import OneHotEncoder\nfrom sklearn.model_selection import KFold, GroupKFold\nfrom sklearn.model_selection import train_test_split\nimport xgboost as xgb\n\n# 成功性预测\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.model_selection import KFold\nfrom sklearn.model_selection import cross_val_score, cross_validate\nfrom sklearn.ensemble import RandomForestClassifier\nfrom lightgbm import LGBMClassifier\nfrom lightgbm.sklearn import LGBMRegressor\nfrom xgboost import XGBClassifier\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-06-04T17:51:02.093539Z","iopub.execute_input":"2023-06-04T17:51:02.093919Z","iopub.status.idle":"2023-06-04T17:51:06.214367Z","shell.execute_reply.started":"2023-06-04T17:51:02.093895Z","shell.execute_reply":"2023-06-04T17:51:06.213079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 1.导入数据\ndf1 = pd.read_csv('/kaggle/input/predict-student-performance-from-game-play/train.csv')  # 训练集\ndf_labels = pd.read_csv('/kaggle/input/predict-student-performance-from-game-play/train_labels.csv') \ndf2 = pd.read_csv('/kaggle/input/predict-student-performance-from-game-play/test.csv')   # 测试集\n\ndisplay(df1.shape, df_labels.shape, df2.shape)\ndisplay(df1.head())","metadata":{"execution":{"iopub.status.busy":"2023-06-04T17:51:06.220269Z","iopub.execute_input":"2023-06-04T17:51:06.223683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 4.特征工程\n# 4.1 变量分类\ncats = ['event_name','fqid','room_fqid','text']\nnums = ['elapsed_time','level','page','room_coor_x','room_coor_y'\n       ,'screen_coor_x','screen_coor_y','hover_duration']\nevents = ['navigate_click','person_click','cutscene_click','object_click','map_hover','notification_click'\n         ,'map_click','observation_click','checkpoint']\n\ndef feature_engineer(train): \n    dfs = [] \n    for c in cats: \n        tmp = train.groupby(['session_id','level_group'])[c].agg('nunique')\n        tmp.name = tmp.name +'_nunique'\n        dfs.append(tmp)\n\n    for c in nums:\n        tmp = train.groupby(['session_id','level_group'])[c].agg('mean')\n        tmp.name = tmp.name +'_mean'\n        dfs.append(tmp)\n\n    for c in nums:\n        tmp = train.groupby(['session_id','level_group'])[c].agg('std')\n        tmp.name = tmp.name +'_std'\n        dfs.append(tmp)\n\n    for c in events:\n        train[c] = (train.event_name == c).astype('int8')\n\n    for c in events +['elapsed_time']:\n        tmp = train.groupby(['session_id','level_group'])[c].agg('sum')\n        tmp.name = tmp.name +'_sum'\n        dfs.append(tmp)\n        \n    train = train.drop(events,axis=1)\n    \n    tmp = train.groupby(['session_id','level_group'])['elapsed_time'].agg(['max','min','count'])\n    tmp['time'] = tmp['max'] - tmp['min']\n    tmp['avg_time'] = tmp['time']/tmp['count']\n    dfs.append(tmp)\n\n    df = pd.concat(dfs,axis=1)\n    df = df.fillna(-1)\n    \n    df = df.reset_index()\n    df = df.set_index('session_id')\n    \n    return df\n\ndf_ = feature_engineer(df1)\nprint(df_.shape)\ndf_.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tmp = df1.groupby('session_id').session_id.agg('count')\n\n# COMPUTE READS AND SKIPS\nPIECES = 10\nCHUNK = int( np.ceil(len(tmp)/PIECES) )\n\nreads = []\nskips = [0]\nfor k in range(PIECES):\n    a = k*CHUNK\n    b = (k+1)*CHUNK\n    if b>len(tmp): b=len(tmp)\n    r = tmp.iloc[a:b].sum()\n    reads.append(r)\n    skips.append(skips[-1]+r)\n    \nprint(f'To avoid memory error, we will read train in {PIECES} pieces of sizes:')\nprint(reads)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nimport gc\n# PROCESS TRAIN DATA IN PIECES\nall_pieces = []\nprint(f'Processing train as {PIECES} pieces to avoid memory error... ')\nfor k in range(PIECES):\n    print(k,', ',end='')\n    SKIPS = 0\n    if k>0: SKIPS = range(1,skips[k]+1)\n    train = pd.read_csv('./Predict Performance of Students/train.csv',\n                        nrows=reads[k], skiprows=SKIPS)\n    dft = feature_engineer(train)\n    all_pieces.append(dft)\n    \n# CONCATENATE ALL PIECES\nprint('\\n')\ndel train; gc.collect()\ndf_ = pd.concat(all_pieces, axis=0)\nprint('Shape of all train data after feature engineering:', df_.shape )\ndf_.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# XGBoost\nALL_USERS = df_.index.unique()\nFEATURES = [c for c in df_.columns if c != 'level_group']\ngkf = GroupKFold(n_splits=5)\noof = pd.DataFrame(data=np.zeros((len(ALL_USERS),18)), index=ALL_USERS)\nmodels = {}\n\n# COMPUTE CV SCORE WITH 5 GROUP K FOLD\nfor i, (train_index, test_index) in enumerate(gkf.split(X=df_, groups=df_.index)):\n    print('\\n', '-'*25)\n    print('Fold',i+1)\n    \n    xgb_params = {\n    'objective' : 'binary:logistic',\n    'eval_metric':'logloss',\n    'learning_rate': 0.05,\n    'max_depth': 4,\n    'n_estimators': 1000,\n    'early_stopping_rounds': 50,\n    'tree_method':'hist',\n    'subsample':0.8,\n    'colsample_bytree': 0.4}\n    \n    # ITERATE THRU QUESTIONS 1 THRU 18\n    for t in range(1,19):\n        \n        # USE THIS TRAIN DATA WITH THESE QUESTIONS\n        if t<=3: grp = '0-4'\n        elif t<=13: grp = '5-12'\n        elif t<=22: grp = '13-22'\n            \n        # TRAIN DATA\n        train_x = df_.iloc[train_index]\n        train_x = train_x.loc[train_x.level_group == grp]\n        train_users = train_x.index.values\n        train_y = df_labels.loc[df_labels.q==t].set_index('session').loc[train_users]\n        \n        # VALID DATA\n        valid_x = df_.iloc[test_index]\n        valid_x = valid_x.loc[valid_x.level_group == grp]\n        valid_users = valid_x.index.values\n        valid_y = df_labels.loc[df_labels.q==t].set_index('session').loc[valid_users]\n        \n        # TRAIN MODEL        \n        clf =  XGBClassifier(**xgb_params)\n        clf.fit(train_x[FEATURES].astype('float32'), train_y['correct'],\n                eval_set=[ (valid_x[FEATURES].astype('float32'), valid_y['correct']) ],\n                verbose=0)\n        print(f'{t}({clf.best_ntree_limit}), ',end='')\n\n# SAVE MODEL, PREDICT VALID OOF\n        models[f'{grp}_{t}'] = clf\n        oof.loc[valid_users, t-1] = clf.predict_proba(valid_x[FEATURES].astype('float32'))[:,1]\n        \n    print()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# XGBoost调参\n# PUT TRUE LABELS INTO DATAFRAME WITH 18 COLUMNS\ntrue = oof.copy()\nfor k in range(18):\n    # GET TRUE LABELS\n    tmp = df_labels.loc[df_labels.q == k+1].set_index('session').loc[ALL_USERS]\n    true[k] = tmp.correct.values\n\n# FIND BEST THRESHOLD TO CONVERT PROBS INTO 1s AND 0s\nscores, thresholds = [],[]\nbest_score, best_threshold = 0, 0\n\nfor threshold in np.arange(0.4,0.81,0.01):\n    print(f'{threshold:.02f}, ',end='')\n    preds = (oof.values.reshape((-1))>threshold).astype('int')\n    m = f1_score(true.values.reshape((-1)), preds, average='macro')   \n    scores.append(m)\n    thresholds.append(threshold)\n    if m>best_score:\n        best_score = m\n        best_threshold = threshold\n\n# PLOT THRESHOLD VS. F1_SCORE\nplt.figure(figsize=(20,5))\nplt.plot(thresholds,scores,'-o',color='blue')\nplt.scatter([best_threshold], [best_score], s=300, alpha=1)\nplt.xlabel('Threshold',size=14)\nplt.ylabel('Validation F1 Score',size=14)\nplt.title(f'Threshold vs. F1_Score with Best F1_Score = {best_score:.3f} at Best Threshold = {best_threshold:.3}',size=18)\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# IMPORT KAGGLE API\nimport jo_wilder\nenv = jo_wilder.make_env()\niter_test = env.iter_test()\n\n# CLEAR MEMORY\nimport gc\ndel targets, df, oof, true\n_ = gc.collect()\nlimits = {'0-4':(1,4), '5-12':(4,14), '13-22':(14,19)}\n\nfor (test, sample_submission) in iter_test:\n    \n    # FEATURE ENGINEER TEST DATA\n    df_final = feature_engineer(test)\n    \n    # INFER TEST DATA\n    grp = test.level_group.values[0]\n    a,b = limits[grp]\n    for t in range(a,b):\n        clf = models[f'{grp}_{t}']\n        p = clf.predict_proba(df_final[FEATURES].astype('float32'))[0,1]\n        mask = sample_submission.session_id.str.contains(f'q{t}')\n        sample_submission.loc[mask,'correct'] = int( p > best_threshold )\n    \n    env.predict(sample_submission)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}