{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":45533,"databundleVersionId":5748852,"sourceType":"competition"}],"dockerImageVersionId":30587,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"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\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-11-20T22:19:25.928827Z","iopub.execute_input":"2023-11-20T22:19:25.929218Z","iopub.status.idle":"2023-11-20T22:19:26.463671Z","shell.execute_reply.started":"2023-11-20T22:19:25.929180Z","shell.execute_reply":"2023-11-20T22:19:26.462771Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# declare the dtypes of each feature to save memory\n# courtesy of Dennis Sakva: https://www.kaggle.com/competitions/predict-student-performance-from-game-play/discussion/384359\n\ntrain_dtypes={'session_id':'category', \n'elapsed_time':np.int32,\n    'event_name':'category',\n    'name':'category',\n    'level':np.uint8,\n    'page':'category',\n    'room_coor_x':np.float32,\n    'room_coor_y':np.float32,\n    'screen_coor_x':np.float32,\n    'screen_coor_y':np.float32,\n    'hover_duration':np.float32,\n     'text':'category',\n     'fqid':'category',\n     'room_fqid':'category',\n     'text_fqid':'category',\n     'fullscreen':'category',\n     'hq':'category',\n     'music':'category',\n     'level_group':'category'}\n\ntest_dtypes={'session_id':'category', \n'elapsed_time':np.int32,\n    'event_name':'category',\n    'name':'category',\n    'level':np.uint8,\n    'page':'category',\n    'room_coor_x':np.float32,\n    'room_coor_y':np.float32,\n    'screen_coor_x':np.float32,\n    'screen_coor_y':np.float32,\n    'hover_duration':np.float32,\n     'text':'category',\n     'fqid':'category',\n     'room_fqid':'category',\n     'text_fqid':'category',\n     'fullscreen':'category',\n     'hq':'category',\n     'music':'category',\n     'level_group':'category',\n     'session_level':np.uint8}\n","metadata":{"execution":{"iopub.status.busy":"2023-11-20T22:19:35.931110Z","iopub.execute_input":"2023-11-20T22:19:35.931640Z","iopub.status.idle":"2023-11-20T22:19:35.946627Z","shell.execute_reply.started":"2023-11-20T22:19:35.931607Z","shell.execute_reply":"2023-11-20T22:19:35.945513Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# process the training data in chunks to save on memory\nchunks = []\n\nfor chunk in pd.read_csv('/kaggle/input/predict-student-performance-from-game-play/train.csv', chunksize=25000, dtype=train_dtypes):\n    chunks.append(chunk)\n\ntrain_data = pd.concat(chunks, axis=0)\n\n#train_data = pd.read_csv('/kaggle/input/predict-student-performance-from-game-play/train.csv', dtype=train_dtypes)\ntrain_labels = pd.read_csv('/kaggle/input/predict-student-performance-from-game-play/train_labels.csv', dtype=test_dtypes)\ntest_data = pd.read_csv('/kaggle/input/predict-student-performance-from-game-play/test.csv')","metadata":{"execution":{"iopub.status.busy":"2023-11-20T22:19:45.538100Z","iopub.execute_input":"2023-11-20T22:19:45.538489Z","iopub.status.idle":"2023-11-20T22:22:43.418457Z","shell.execute_reply.started":"2023-11-20T22:19:45.538457Z","shell.execute_reply":"2023-11-20T22:22:43.417370Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# do some memory clean-up\nimport gc\n\ndel chunks\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-11-20T22:22:48.725615Z","iopub.execute_input":"2023-11-20T22:22:48.726499Z","iopub.status.idle":"2023-11-20T22:22:48.880876Z","shell.execute_reply.started":"2023-11-20T22:22:48.726461Z","shell.execute_reply":"2023-11-20T22:22:48.880031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data.iloc[len(train_data)-1]","metadata":{"execution":{"iopub.status.busy":"2023-11-20T22:31:41.330552Z","iopub.execute_input":"2023-11-20T22:31:41.331274Z","iopub.status.idle":"2023-11-20T22:31:41.340918Z","shell.execute_reply.started":"2023-11-20T22:31:41.331234Z","shell.execute_reply":"2023-11-20T22:31:41.339889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# combine the train and test data for feature engineering and do a group-by\ntrain_data = pd.concat([train_data, test_data.drop('session_level', axis=1)], axis=0).groupby(['session_id', 'level_group'])","metadata":{"execution":{"iopub.status.busy":"2023-11-20T22:12:18.418984Z","iopub.execute_input":"2023-11-20T22:12:18.419395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create a new dataframe to store the formatted features by first saving the session id and level group features\nX = train_data['hq'].unique().index.to_frame(index=False)","metadata":{"execution":{"iopub.status.busy":"2023-11-20T21:33:37.317738Z","iopub.execute_input":"2023-11-20T21:33:37.318160Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create two columns out of the elapsed time, one to store the total time per session id and one to store the average time per event\nX['total_time'] = train_data['elapsed_time'].max().reset_index(drop=True)\nX['avg_time'] = ((train_data['elapsed_time'].max() - train_data['elapsed_time'].min()) / (train_data['index'].max() - train_data['index'].min())).reset_index(drop=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get the average x and y coordinates\nX[['avg_room_coor_x', 'avg_room_coor_y', 'avg_screen_coor_x', 'avg_screen_coor_y']] = train_data[['room_coor_x', 'room_coor_y', 'screen_coor_x', 'screen_coor_y']].mean().reset_index(drop=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get the unique number of texts and ids\nX[['n_text', 'n_fqid', 'n_room_fqid', 'n_text_fqid']] = train_data[['text', 'fqid', 'room_fqid', 'text_fqid']].nunique().reset_index(drop=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get the mode of the fullscreen, hq and music features\nX[['mode_fullscreen', 'mode_hq', 'mode_music']] = train_data[['fullscreen', 'hq', 'music']].apply(pd.DataFrame.mode).reset_index(drop=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# do some more memory clean-up\n\ndel train_data\ngc.collect()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create a list with column names q1 through q18\nq_cols = []\nfor i in range(1, 19):\n    col = 'q' + str(i)\n    q_cols.append(col)\n\nprint(q_cols)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# format the train_labels data, starting with creating a dataframe full of 0's\nq_zeros = pd.DataFrame(np.zeros((len(train_labels), 18)), columns=q_cols)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# combine the zero matrix DF with the train_label DF\nq_train_label = pd.concat([train_labels, q_zeros], axis=1)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# loop through each column to get the value for correct/incorrect label\n\nfor col in q_cols:\n    q_train_label.loc[(q_train_label['session_id'].str.endswith(col), col)] = q_train_label.loc[(q_train_label['session_id'].str.endswith(col), 'correct')]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create a function to group question numbers and save in level_group column\ndef group_qs(session_id):\n    q_num = int(session_id.split('_q')[1])\n    if 1 <= q_num <= 3:\n        return '0-4'\n    elif 4 <= q_num <= 13:\n        return '5-12'\n    else:\n        return '13-22'\n\nq_train_label['level_group'] = q_train_label['session_id'].apply(group_qs)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# format the name of session_id\n\nq_train_label['session_id'] = q_train_label['session_id'].apply(lambda x: x.split('_')[0])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# do a groupby by session_id and save it as a new dataframe\nX_train_label = q_train_label.drop('correct', axis=1).groupby(['session_id', 'level_group']).sum().reset_index()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# convert the session_id column to object for merging\nX_train_label['session_id'] = X_train_label['session_id'].astype('int64')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# merge the two X dataframes\nX_merged = X.merge(X_train_label, how='left', left_on=['session_id', 'level_group'], right_on=['session_id', 'level_group'])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# one-hot encode the level_group column\nX_merged = pd.get_dummies(X_merged, columns=['level_group'], dtype='int8')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# split into train and test datasets\ntest_len = len(test_data.groupby(['session_id', 'level_group'])['index'].min())\n\nX_test = X_merged.iloc[:test_len]\nX_train = X_merged.iloc[test_len:]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# split the data into X's and y's\ny_train = X_train[q_cols]\nX_train.drop(q_cols, axis=1, inplace=True)\n\nX_test.drop(q_cols, axis=1, inplace=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# set up XGBoost\n# for the interest of time, parameters taken from Chris Deotte's notebook: https://www.kaggle.com/code/cdeotte/xgboost-baseline-0-680\nxgb_params = {'objective': 'binary:logistic',\n              'eval_metric':'logloss',\n              'learning_rate': 0.05,\n              'max_depth': 4,\n              'tree_method': 'hist',\n              'subsample': 0.8,\n              'colsample_bytree': 0.4}","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get a 2d list of predictions\n\ny_preds = []\n\nfor col in q_cols:\n    dtrain = xgb.DMatrix(X_train, label=y_train[col])\n    dtest = xgb.DMatrix(X_test)\n    #evallist = [(dtest, 'evals'), (dtrain, 'train')]\n    num_round = 100\n\n    bst = xgb.train(xgb_params, dtrain, num_round)\n    \n    y_pred = bst.predict(dtest)\n    y_preds.append(np.where(y_pred > 0.5, 1, 0).tolist())","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# transpose the array of predictions\ny_preds_transposed = np.array(y_preds).T","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# dataframe containing answers\ny_preds_df = pd.DataFrame(y_preds_transposed, columns=q_cols)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# set up the dataframe to output the prediction results\noutput = test_data.groupby(['session_id', 'level_group'])['hq'].unique().index.to_frame(index=False)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# merge the dataframes\noutput_df = pd.concat([output, y_preds_df], axis=1)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# convert the level group to an ordered categorical one\noutput_df['level_group'] = pd.Categorical(output_df['level_group'], categories=['0-4', '5-12', '13-22'], ordered=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# sort the output dataframe by session id and level group\noutput_df = output_df.sort_values(by=['session_id', 'level_group'])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# reset the index and then create a new session level column using index values\noutput_df.reset_index(drop=True, inplace=True)\noutput_df['session_level'] = output_df.index","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create lists with session names, correct/incorrect binary labels and session levels\nsession_ids = []\ncorrects = []\nsession_levels = []\nfor session_id in output_df['session_id'].unique():\n    for col in q_cols:\n        session_ids.append(str(session_id) + '_' + col)\n        q_num = int(col.split('q')[1])\n        if 1 <= q_num <= 3:\n            corrects.append(int((output_df[(output_df['session_id'] == session_id) & (output_df['level_group'] == '0-4')][col]).iloc[0]))\n            session_levels.append(int((output_df[(output_df['session_id'] == session_id) & (output_df['level_group'] == '0-4')]['session_level']).iloc[0]))\n        elif 4 <= q_num <= 13:\n            corrects.append(int((output_df[(output_df['session_id'] == session_id) & (output_df['level_group'] == '5-12')][col]).iloc[0]))\n            session_levels.append(int((output_df[(output_df['session_id'] == session_id) & (output_df['level_group'] == '5-12')]['session_level']).iloc[0]))\n        else:\n            corrects.append(int((output_df[(output_df['session_id'] == session_id) & (output_df['level_group'] == '13-22')][col]).iloc[0]))\n            session_levels.append(int((output_df[(output_df['session_id'] == session_id) & (output_df['level_group'] == '13-22')]['session_level']).iloc[0]))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# combine the lists into a dataframe and export to CSV\nfinal_output = pd.DataFrame(data=np.array([session_ids, corrects, session_levels]).T, columns=['session_id', 'correct', 'session_level'])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# add session_id numerical and question number column for sorting purposes\nfinal_output['session_id_num'] = final_output['session_id'].apply(lambda x: x.split('_q')[0])\nfinal_output['question_num'] = final_output['session_id'].apply(lambda x: x.split('_q')[1])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# convert dtypes to integer\nfinal_output['session_id_num'] = final_output['session_id_num'].astype('int64')\nfinal_output['question_num'] = final_output['question_num'].astype('int8')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# order the dataframe\nfinal_output = final_output.sort_values(by=['question_num', 'session_id_num']).reset_index(drop=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# save the output as a CSV file\nfinal_output.drop(['session_id_num', 'question_num'], axis=1).to_csv('/kaggle/working/submission.csv', index=False)","metadata":{},"execution_count":null,"outputs":[]}]}