{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"!ls ../input/","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# https://www.kaggle.com/its7171/lgbm-with-loop-feature-engineering\nimport os\nimport gc\nimport time\nimport gzip\nimport pickle\nimport numpy as np\nimport pandas as pd\nimport lightgbm as lgb\nfrom tqdm import tqdm\nfrom collections import defaultdict\nfrom sklearn.metrics import roc_auc_score\n\nimport warnings\n\nwarnings.filterwarnings('ignore')\npd.set_option('display.max_columns', 100)\npd.set_option('display.max_rows', 100)\n\nDATA_PATH = '../input/riiid-test-answer-prediction/'\nMY_DATA_PATH = '../input/my_data/'\nCACHE_PATH = '../input/lgb1215weights/'\nif not os.path.exists(CACHE_PATH):\n    os.mkdir(CACHE_PATH)\n\nDEBUG = False\nOFFLINE = False\nif DEBUG:\n    MY_DATA_PATH = f'{MY_DATA_PATH}/debug/'\n    CACHE_PATH = f'{CACHE_PATH}/debug/'\n    if not os.path.exists(CACHE_PATH):\n        os.mkdir(CACHE_PATH)\nconfig_file = f'{CACHE_PATH}/config.pkl'\n\n########################################################################################################################\n##### Util Fnc\ndef save_pickle(dic, save_path):\n    # with open(save_path, 'wb') as f:\n    with gzip.open(save_path, 'wb') as f:\n        pickle.dump(dic, f)\n\ndef load_pickle(load_path):\n    # with open(load_path, 'rb') as f:\n    with gzip.open(load_path, 'rb') as f:\n        message_dict = pickle.load(f)\n    return message_dict\n\ndef reduce_mem_usage(df, verbose=True):\n    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']\n    start_mem = df.memory_usage().sum() / 1024 ** 2\n    for col in df.columns:\n        col_type = df[col].dtypes\n        if col_type in numerics:\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                elif 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    end_mem = df.memory_usage().sum() / 1024 ** 2\n    if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (\n                start_mem - end_mem) / start_mem))\n    return df\n\n\n########################################################################################################################\n##### Load Data\n## Get Question_Data\nquestions_df = pd.read_csv(f'{DATA_PATH}/questions.csv')\nquestions_df['content_bundle_same'] = (questions_df['question_id'] == questions_df['bundle_id']).astype(int)\n# questions_df['bundle_content_nunique'] = questions_df.groupby('bundle_id')['question_id'].nunique()\nquestions_df['tags_len'] = questions_df['tags'].apply(lambda x: 0 if str(x) == 'nan' else len(str(x).split(' ')))\nquestions_df['tags'] = questions_df['tags'].apply(lambda x: [] if str(x) == 'nan' else str(x).split(' '))\n\nquestions_df['part_content_num'] = questions_df.groupby('part')['question_id'].transform('count')\n\nquestion_bundle_dict = dict(zip(questions_df.question_id.values, questions_df.bundle_id.values))\nquestion_part_dict = dict(zip(questions_df.question_id.values, questions_df.part.values))\nquestion_tags_dict = dict(zip(questions_df.question_id.values, questions_df.tags.values))\n\nbundle_df = questions_df.groupby('bundle_id')['question_id'].unique()\nbundle_df = bundle_df[bundle_df.apply(len) > 1]\nbundle_mapping = {}\nfor id_list in bundle_df.values:\n    bid = id_list[0]\n    for qid in id_list:\n        bundle_mapping[qid] = (bid, len(id_list))\n# print('bundle_mapping:\\n', bundle_mapping)\n\n## Get Lecture_Data\nlectures_df = pd.read_csv(f'{DATA_PATH}/lectures.csv')\nlecture_tag_dict = dict(zip(lectures_df.lecture_id.values, lectures_df.tag.values))\nlecture_part_dict = dict(zip(lectures_df.lecture_id.values, lectures_df.part.values))\nlecture_type_dict = dict(zip(lectures_df.lecture_id.values, lectures_df.type_of.values))\n\n## Get Samples\nif OFFLINE:\n    feld_needed = ['row_id', 'timestamp', 'user_id', 'content_id', 'content_type_id', 'task_container_id',\n                   'user_answer', 'answered_correctly', 'prior_question_elapsed_time', 'prior_question_had_explanation']\n    train = pd.read_pickle(f'{MY_DATA_PATH}/train.pickle')[feld_needed]\n    valid = pd.read_pickle(f'{MY_DATA_PATH}/valid.pickle')[feld_needed]\n\n    train['prior_question_elapsed_time'] //= 1000\n    train['timestamp'] /= 1000\n    valid['prior_question_elapsed_time'] //= 1000\n    valid['timestamp'] /= 1000\n\n    # train['day'] = train['timestamp'] // 1000 // 60 // 60# // 24\n    # valid['day'] = valid['timestamp'] // 1000 // 60 // 60# // 24\n    # train = train[:10000]\n    # valid = valid[:10000]\n    print(f'Train|Valid: {len(train)}|{len(valid)}')\n\n    # train_index = train.loc[train.content_type_id == False].reset_index(drop=True).groupby('user_id').tail(800).index\n    # print(f'Chosen train index num: {len(train_index)} | {len(train_index)/len(train):.4f}')\n\n    '''\n    Make feat for valid:\n    * ques: quest_train\n    * lect: lect_train\n\n    Make feat for test:\n    * ques: quest_train/quest_valid\n    * lect: lect_train/lect_valid\n    '''\n    ques_train = train.loc[train.content_type_id == False, ['row_id', 'content_id', 'answered_correctly']].reset_index(\n        drop=True)\n    ques_valid = valid.loc[valid.content_type_id == False, ['row_id', 'content_id', 'answered_correctly']].reset_index(\n        drop=True)\n    ques_train = reduce_mem_usage(ques_train, verbose=True)\n    ques_valid = reduce_mem_usage(ques_valid, verbose=True)\n    # lect_train = train.loc[train.content_type_id == True, ['row_id', 'user_id', 'content_id', 'task_container_id']].reset_index(drop=True)\n    # lect_valid = valid.loc[valid.content_type_id == True, ['row_id', 'user_id', 'content_id', 'task_container_id']].reset_index(drop=True)\n    # print(f'Train|Valid: {len(train)}|{len(valid)}')\n\n    train['prior_question_had_explanation'] = train['prior_question_had_explanation'].fillna(False).astype('int8')\n    valid['prior_question_had_explanation'] = valid['prior_question_had_explanation'].fillna(False).astype('int8')\n\n    prior_question_elapsed_time_mean = train.loc[train.content_type_id == False].prior_question_elapsed_time.dropna().values.mean()\n    train['prior_question_elapsed_time'] = train['prior_question_elapsed_time'].fillna(prior_question_elapsed_time_mean).astype(np.int32)\n    valid['prior_question_elapsed_time'] = valid['prior_question_elapsed_time'].fillna(prior_question_elapsed_time_mean).astype(np.int32)\n    train = reduce_mem_usage(train, verbose=True)\n    valid = reduce_mem_usage(valid, verbose=True)\n\n    save_pickle(prior_question_elapsed_time_mean, f'{CACHE_PATH}/prior_question_elapsed_time_mean.pkl')\n\n    # part_elapse_time_mean_dict = \\\n    #     dict(pd.concat([ques_train, ques_valid]).merge(questions_df, left_on='content_id', right_on='question_id', how='left').\\\n    #     groupby('part')['prior_question_elapsed_time'].mean())\n    # save_pickle(part_elapse_time_mean_dict, f'{CACHE_PATH}/part_elapse_time_mean_dict.pkl')\n\n    config_dict = {}\nelse:\n    config_dict = load_pickle(config_file)\n    # part_elapse_time_mean_dict = load_pickle(f'{CACHE_PATH}/part_elapse_time_mean_dict.pkl')\n    prior_question_elapsed_time_mean = load_pickle(f'{CACHE_PATH}/prior_question_elapsed_time_mean.pkl')\n\n########################################################################################################################\n##### Content Static Feat\ndef make_content_feat(df, type):\n    df = df.loc[df.content_type_id == False].reset_index(drop=True)\n    file_name = f'content_feat_{type}.pkl'\n\n    # df['timestamp_diff'] = df['timestamp'] - df.groupby('user_id')['timestamp'].shift(1)\n    feat_df = df.groupby('content_id', as_index=False)['answered_correctly'].mean(). \\\n        rename(columns={'answered_correctly': 'content_target_mean'})\n\n    # content_timestamp_diff_median = df.groupby('content_id')['timestamp_diff'].median()\n    # feat_df['content_timestamp_diff_median'] = content_timestamp_diff_median.reindex(feat_df.content_id.values).values\n\n    content_cnt = df.groupby('content_id')['user_id'].count()\n    feat_df['content_cnt'] = content_cnt.reindex(feat_df.content_id.values).values\n\n    save_pickle(feat_df, save_path=f'{CACHE_PATH}/{file_name}')\n    feat_df = reduce_mem_usage(feat_df, verbose=True)\n    return feat_df\n\n\nif OFFLINE:\n    content_feat = make_content_feat(df=train.copy(deep=True), type='train')\n    content_feat_test = make_content_feat(df=pd.concat([train, valid]), type='test')\n    print('content_feat:\\n', content_feat.head())\nelse:\n    content_feat_test = load_pickle(f'{CACHE_PATH}/content_feat_test.pkl')\n    content_feat_test = reduce_mem_usage(content_feat_test, verbose=True)\ncontent_target_mean_dict = dict(zip(content_feat_test.content_id.values,\n                                    content_feat_test.content_target_mean.values))\ncontent_feat_cols = [col for col in content_feat_test if col != 'content_id']\n\n########################################################################################################################\n##### Part Feat\ndef make_part_mean_dict(df):\n    df = df.loc[df.content_type_id == False].reset_index(drop=True)\n    df = df.merge(questions_df[['question_id', 'part']], left_on='content_id', right_on='question_id', how='left')\n\n    feat_df = df.groupby('part', as_index=False)['answered_correctly'].mean(). \\\n        rename(columns={'answered_correctly': 'part_target_mean'})\n\n    return dict(zip(feat_df.part.values, feat_df.part_target_mean.values))\nif OFFLINE:\n    part_target_mean_dict = make_part_mean_dict(df=pd.concat([train[['content_id', 'content_type_id', 'answered_correctly']],\n                                                              valid[['content_id', 'content_type_id', 'answered_correctly']]]))\n    save_pickle(part_target_mean_dict, f'{CACHE_PATH}/part_target_mean_dict.pkl')\nelse:\n    part_target_mean_dict = load_pickle(f'{CACHE_PATH}/part_target_mean_dict.pkl')\nquestions_df['part_target_mean'] = questions_df['part'].apply(lambda x: part_target_mean_dict[x])\n\n########################################################################################################################\n##### Id Static Feat\nstatic_feat_cols = ['part', 'prior_question_elapsed_time', 'content_id'] # + [f'tags_w2v{i}' for i in range(w2v_dim)] , 'tag1', 'tag2', 'tag3', 'tag4'\n\n\ndef get_stat_feat(df, feat_cols):\n    df = df.loc[df.content_type_id == False].reset_index(drop=True)\n    df = df.merge(questions_df[['question_id', 'part']], left_on='content_id', right_on='question_id', how='left')\n\n    return df[feat_cols]\n\n\nif OFFLINE:\n    state_feat_train = get_stat_feat(df=train.copy(deep=True), feat_cols=static_feat_cols)\n    state_feat_valid = get_stat_feat(df=valid.copy(deep=True), feat_cols=static_feat_cols)\n    state_feat_train = reduce_mem_usage(state_feat_train, verbose=True)\n    state_feat_valid = reduce_mem_usage(state_feat_valid, verbose=True)\n\n    # for _part in part_elapse_time_mean_dict:\n    #     state_feat_train.loc[state_feat_train.part == _part, ['prior_question_elapsed_time']] = \\\n    #         state_feat_train.loc[state_feat_train.part == _part, ['prior_question_elapsed_time']].fillna(part_elapse_time_mean_dict[_part])\n    #     state_feat_valid.loc[state_feat_valid.part == _part, ['prior_question_elapsed_time']] = \\\n    #         state_feat_valid.loc[state_feat_valid.part == _part, ['prior_question_elapsed_time']].fillna(part_elapse_time_mean_dict[_part])\n\n########################################################################################################################\n##### User Loop Feat\nwindow_size = 25\nif OFFLINE:\n    user_cnt_dict = defaultdict(int)\n    user_pos_cnt_dict = defaultdict(int)\n    user_part_cnt_dict = defaultdict(int)\n    user_part_pos_cnt_dict = defaultdict(int)\n    user_content_cnt_dict = defaultdict(int)\n    user_content_pos_cnt_dict = defaultdict(int)\n    user_content_redo_cnt_dict = defaultdict(int)\n    user_content_mean_sum_dict = defaultdict(int)\n    user_consecutive_pos_cnt_dict = defaultdict(int)\n    user_target_win25_dict = defaultdict(list)\n    user_content_mean_win10_dict = defaultdict(list)\n\n    user_explanation_cnt_dict = defaultdict(int)\n    user_explanation_pos_cnt_dict = defaultdict(int)\n    user_elapse_time_sum_dict = defaultdict(int)\n    user_elapse_time_win10_dict = defaultdict(list)\n    user_last_timestamp_dict = defaultdict(int)\n    user_last_task_dict = defaultdict(int)\n    user_content_win5_dict = defaultdict(list)\n    user_part_win10_dict = defaultdict(list)\n\n    bundle_state_dict = defaultdict(list) # bundle_id, time_diff\n    # user_order_in_session_dict = defaultdict(int)\n    user_cum_time_dict = defaultdict(int)\n    user_timespan_win10_dict = defaultdict(list)\n\n    user_tags_cnt_dict = defaultdict(int)\n    user_tags_pos_cnt_dict = defaultdict(int)\n\n    user_continue_quest_cnt_dict = defaultdict(int)\nelse:\n    user_content_feat_df = pd.read_pickle(f'{CACHE_PATH}/user_content_feat.pkl')\n    user_content_feat_df = reduce_mem_usage(user_content_feat_df)\n    user_content_cnt_dict = defaultdict(int)\n    user_content_pos_cnt_dict = defaultdict(int)\n\n    user_cnt_dict = load_pickle(f'{CACHE_PATH}/user_cnt_dict.pkl')\n    user_pos_cnt_dict = load_pickle(f'{CACHE_PATH}/user_pos_cnt_dict.pkl')\n    user_part_cnt_dict = load_pickle(f'{CACHE_PATH}/user_part_cnt_dict.pkl')\n    user_part_pos_cnt_dict = load_pickle(f'{CACHE_PATH}/user_part_pos_cnt_dict.pkl')\n    # user_content_cnt_dict = load_pickle(f'{CACHE_PATH}/user_content_cnt_dict.pkl')\n    user_content_redo_cnt_dict = load_pickle(f'{CACHE_PATH}/user_content_redo_cnt_dict.pkl')\n    user_content_mean_sum_dict = load_pickle(f'{CACHE_PATH}/user_content_mean_sum_dict.pkl')\n    user_consecutive_pos_cnt_dict = load_pickle(f'{CACHE_PATH}/user_consecutive_pos_cnt_dict.pkl')\n    user_target_win25_dict = load_pickle(f'{CACHE_PATH}/user_target_win25_dict.pkl')\n    user_content_mean_win10_dict = load_pickle(f'{CACHE_PATH}/user_content_mean_win10_dict.pkl')\n\n    user_explanation_cnt_dict = load_pickle(f'{CACHE_PATH}/user_explanation_cnt_dict.pkl')\n    user_explanation_pos_cnt_dict = load_pickle(f'{CACHE_PATH}/user_explanation_pos_cnt_dict.pkl')\n    user_elapse_time_sum_dict = load_pickle(f'{CACHE_PATH}/user_elapse_time_sum_dict.pkl')\n    user_elapse_time_win10_dict = load_pickle(f'{CACHE_PATH}/user_elapse_time_win10_dict.pkl')\n    user_last_timestamp_dict = load_pickle(f'{CACHE_PATH}/user_last_timestamp_dict.pkl')\n    user_last_task_dict = load_pickle(f'{CACHE_PATH}/user_last_task_dict.pkl')\n    user_content_win5_dict = load_pickle(f'{CACHE_PATH}/user_content_win5_dict.pkl')\n    user_part_win10_dict = load_pickle(f'{CACHE_PATH}/user_part_win10_dict.pkl')\n\n    bundle_state_dict = load_pickle(f'{CACHE_PATH}/bundle_state_dict.pkl')\n    # user_order_in_session_dict = load_pickle(f'{CACHE_PATH}/user_order_in_session_dict.pkl')\n    user_cum_time_dict = load_pickle(f'{CACHE_PATH}/user_cum_time_dict.pkl')\n    user_timespan_win10_dict = load_pickle(f'{CACHE_PATH}/user_timespan_win10_dict.pkl')\n\n    user_tags_cnt_dict = load_pickle(f'{CACHE_PATH}/user_tags_cnt_dict.pkl')\n    user_tags_pos_cnt_dict = load_pickle(f'{CACHE_PATH}/user_tags_pos_cnt_dict.pkl')\n\n    user_continue_quest_cnt_dict = load_pickle(f'{CACHE_PATH}/user_continue_quest_cnt_dict.pkl')\n\nused_cols = ['user_id', 'content_id', 'task_container_id', 'answered_correctly', 'prior_question_elapsed_time',\n             'prior_question_had_explanation', 'content_type_id', 'timestamp']\n\n\ndef make_user_loop_features(df, content_target_mean_dict,\n                            user_cnt_dict, user_pos_cnt_dict,\n                            user_part_cnt_dict, user_part_pos_cnt_dict,\n                            user_content_cnt_dict, user_content_pos_cnt_dict, user_content_redo_cnt_dict, user_content_mean_sum_dict,\n                            user_consecutive_pos_cnt_dict, user_target_win25_dict, user_content_mean_win10_dict,\n                            user_explanation_cnt_dict, user_explanation_pos_cnt_dict, user_elapse_time_sum_dict, user_elapse_time_win10_dict,\n                            user_last_timestamp_dict, user_last_task_dict,\n                            user_content_win5_dict, user_part_win10_dict,\n                            bundle_state_dict, user_cum_time_dict, user_timespan_win10_dict,# user_order_in_session_dict,\n                            user_tags_cnt_dict, user_tags_pos_cnt_dict,\n                            user_continue_quest_cnt_dict,\n                            update=True, isTrain=True):\n    sample_num = len(df.loc[df.content_type_id == False])\n\n    user_cnt_npy = np.zeros(sample_num)\n    user_pos_cnt_npy = np.zeros(sample_num)\n    user_part_cnt_npy = np.zeros(sample_num)\n    user_part_pos_cnt_npy = np.zeros(sample_num)\n    user_content_cnt_npy = np.zeros(sample_num)\n    user_content_pos_cnt_npy = np.zeros(sample_num)\n    user_content_redo_cnt_npy = np.zeros(sample_num)\n    user_content_mean_mean_npy = np.zeros(sample_num)\n    user_consecutive_pos_cnt_npy = np.zeros(sample_num)\n    user_pos_cnt_win25_npy = np.zeros(sample_num)\n    user_content_mean_win10_npy = np.zeros(sample_num)\n\n    user_explanation_cnt_npy = np.zeros(sample_num)\n    user_explanation_pos_cnt_npy = np.zeros(sample_num)\n    user_elapse_time_mean_npy = np.zeros(sample_num)\n    user_elapse_time_mean_win10_npy = np.zeros(sample_num)\n    user_last_timespan_npy = np.zeros(sample_num)\n    user_last_task_diff_npy = np.zeros(sample_num)\n    user_content_appear_in_win5_npy = np.zeros(sample_num)\n    user_part_cnt_in_win10_npy = np.zeros(sample_num)\n\n    # user_order_in_session_npy = np.zeros(sample_num)\n    user_cum_time_npy = np.zeros(sample_num)\n    user_timespan_win10_mean_npy = np.zeros(sample_num)\n\n    user_tags_cnt_mean_npy = np.zeros(sample_num)\n    user_tags_pos_rate_npy = np.zeros(sample_num)\n\n    user_continue_quest_cnt_npy = np.zeros(sample_num)\n\n    if update:\n        tk0 = tqdm(df[used_cols].values)\n    else:\n        tk0 = df[used_cols].values\n    idx = 0\n    for (_user_id, _content_id, _task_container_id, _answered_correctly, _prior_question_elapsed_time,\n         _prior_question_had_explanation, _content_type_id, _timestamp) in tk0:\n\n        if _content_id in content_target_mean_dict:\n            _content_target_mean = content_target_mean_dict[_content_id]\n        else:\n            _content_target_mean = 0\n\n        if _content_type_id == False:\n            _bundle_id = question_bundle_dict[_content_id]\n            _part = question_part_dict[_content_id]\n            _tags = question_tags_dict[_content_id]\n            # print('_content_id: ', _content_id)\n            # print('_part: ', _part)\n\n            # user_cnt\n            user_cnt_npy[idx] = user_cnt_dict[_user_id]\n            user_cnt_dict[_user_id] += 1\n            # user_pos_cnt\n            user_pos_cnt_npy[idx] = user_pos_cnt_dict[_user_id]\n            if update:\n                user_pos_cnt_dict[_user_id] += _answered_correctly\n            _user_part = str(_user_id) + '_' + str(_part)\n            # user_part_cnt\n            user_part_cnt_npy[idx] = user_part_cnt_dict[_user_part]\n            user_part_cnt_dict[_user_part] += 1\n            # user_part_pos_cnt\n            user_part_pos_cnt_npy[idx] = user_part_pos_cnt_dict[_user_part]\n            if update:\n                user_part_pos_cnt_dict[_user_part] += _answered_correctly\n            # user_content_cnt\n            # _user_content = str(_user_id) + '_' + str(_content_id)\n            _user_content = np.int64(_user_id) * 10_0000 + _content_id\n            if isTrain:\n                user_content_cnt_npy[idx] = user_content_cnt_dict[_user_content]\n                user_content_cnt_dict[_user_content] += 1\n            else:\n                if _user_content in user_content_feat_df.index:\n                    user_content_cnt_npy[idx] = user_content_feat_df.loc[_user_content]['user_content_cnt'] + user_content_cnt_dict[_user_content]\n                else:\n                    user_content_cnt_npy[idx] = user_content_cnt_dict[_user_content]\n                user_content_cnt_dict[_user_content] += 1\n            # user_content_pos_cnt\n            if isTrain:\n                user_content_pos_cnt_npy[idx] = user_content_pos_cnt_dict[_user_content]\n                if update:\n                    user_content_pos_cnt_dict[_user_content] += _answered_correctly\n            else:\n                if _user_content in user_content_feat_df.index:\n                    user_content_pos_cnt_npy[idx] = user_content_feat_df.loc[_user_content]['user_content_pos_cnt'] + user_content_pos_cnt_dict[_user_content]\n                else:\n                    user_content_pos_cnt_npy[idx] = user_content_pos_cnt_dict[_user_content]\n            # user_content_redo_cnt\n            user_content_redo_cnt_npy[idx] = user_content_redo_cnt_dict[_user_id]\n            if isTrain:\n                if user_content_cnt_dict[_user_content] > 0:\n                    user_content_redo_cnt_dict[_user_id] += 1\n            else:\n                if _user_content in user_content_feat_df.index:\n                    if user_content_feat_df.loc[_user_content]['user_content_cnt'] > 0:\n                        user_content_redo_cnt_dict[_user_id] += 1\n                else:\n                    if user_content_cnt_dict[_user_content] > 0:\n                        user_content_redo_cnt_dict[_user_id] += 1\n            # user_content_mean_mean\n            user_content_mean_mean_npy[idx] = user_content_mean_sum_dict[_user_id]\n            user_content_mean_sum_dict[_user_id] += _content_target_mean\n            # user_consecutive_pos_cnt\n            user_consecutive_pos_cnt_npy[idx] = user_consecutive_pos_cnt_dict[_user_id]\n            if update:\n                if _answered_correctly:\n                    user_consecutive_pos_cnt_dict[_user_id] += 1\n                else:\n                    user_consecutive_pos_cnt_dict[_user_id] = 0\n            # user_pos_cnt_win25\n            user_pos_cnt_win25_npy[idx] = sum(user_target_win25_dict[_user_id])\n            if update:\n                user_target_win25_dict[_user_id].append(_answered_correctly)\n                if len(user_target_win25_dict[_user_id]) > 25:\n                    tmp = user_target_win25_dict[_user_id]\n                    user_target_win25_dict[_user_id] = tmp[-25:]\n            # user_content_mean_win10\n            div_num = len(user_content_mean_win10_dict[_user_id])\n            if div_num > 0:\n                user_content_mean_win10_npy[idx] = sum(user_content_mean_win10_dict[_user_id]) / div_num\n            user_content_mean_win10_dict[_user_id].append(_content_target_mean)\n            if len(user_content_mean_win10_dict[_user_id]) > 10:\n                tmp = user_content_mean_win10_dict[_user_id]\n                user_content_mean_win10_dict[_user_id] = tmp[-10:]\n            # user_explanation_cnt\n            user_explanation_cnt_npy[idx] = user_explanation_cnt_dict[_user_id]\n            user_explanation_cnt_dict[_user_id] += _prior_question_had_explanation\n            # user_explanation_pos_cnt\n            user_explanation_pos_cnt_npy[idx] =user_explanation_pos_cnt_dict[_user_id]\n            if update:\n                if _answered_correctly:\n                    user_explanation_pos_cnt_dict[_user_id] += _prior_question_had_explanation\n            # user_elapse_time_mean\n            user_elapse_time_mean_npy[idx] = user_elapse_time_sum_dict[_user_id]\n            user_elapse_time_sum_dict[_user_id] += _prior_question_elapsed_time\n            # user_elapse_time_mean_win10\n            div_num = len(user_elapse_time_win10_dict[_user_id])\n            if div_num > 0:\n                user_elapse_time_mean_win10_npy[idx] = sum(user_elapse_time_win10_dict[_user_id]) / div_num\n            user_elapse_time_win10_dict[_user_id].append(_prior_question_elapsed_time)\n            if len(user_elapse_time_win10_dict[_user_id]) > 10:\n                tmp = user_elapse_time_win10_dict[_user_id]\n                user_elapse_time_win10_dict[_user_id] = tmp[-10:]\n            # user_last_timespan\n            time_diff = _timestamp - user_last_timestamp_dict[_user_id]\n            if _content_id in bundle_mapping:\n                # is bundle\n                if len(bundle_state_dict[_user_id]) > 1:\n                    # previous is also bundle\n                    previous_bundle_id, previous_diff = bundle_state_dict[_user_id]\n                    if bundle_mapping[_content_id][0] == previous_bundle_id:\n                        # current = previous\n                        time_diff = previous_diff\n                    else:\n                        # current != previous\n                        time_diff = time_diff // bundle_mapping[_content_id][1]\n                        bundle_state_dict[_user_id] = [bundle_mapping[_content_id][0], time_diff]\n                else:\n                    # previous is not bundle\n                    time_diff = time_diff // bundle_mapping[_content_id][1]\n                    bundle_state_dict[_user_id] = [bundle_mapping[_content_id][0], time_diff]\n            else:\n                # not bundle, clear state\n                bundle_state_dict[_user_id] = []\n            user_last_timespan_npy[idx] = time_diff\n            user_last_timestamp_dict[_user_id] = _timestamp\n            # user_last_task_diff\n            user_last_task_diff_npy[idx] = _task_container_id - user_last_task_dict[_user_id]\n            user_last_task_dict[_user_id] = _task_container_id\n            # user_content_appear_in_win5\n            user_content_appear_in_win5_npy[idx] = _content_id in user_content_win5_dict[_user_id]\n            user_content_win5_dict[_user_id].append(_content_id)\n            if len(user_content_win5_dict[_user_id]) > 5:\n                del user_content_win5_dict[_user_id][0]\n            # user_part_cnt_in_win10\n            user_part_cnt_in_win10_npy[idx] = user_part_win10_dict[_user_id].count(_part)\n            user_part_win10_dict[_user_id].append(_part)\n            if len(user_part_win10_dict[_user_id]) > 10:\n                del user_part_win10_dict[_user_id][0]\n\n            # # user_order_in_session\n            # if time_diff > 5 * 60:\n            #     user_order_in_session_dict[_user_id] = 1\n            # else:\n            #     user_order_in_session_dict[_user_id] += 1\n            # user_order_in_session_npy[idx] = user_order_in_session_dict[_user_id]\n\n            # user_tags_feat\n            div_num = 0\n            for _tag in _tags:\n                _user_tag = str(_user_id) + '_' + str(_tag)\n                user_tags_cnt_mean_npy[idx] += user_tags_cnt_dict[_user_tag]\n                if user_tags_cnt_dict[_user_tag] > 0:\n                    div_num += 1\n\n                if user_tags_cnt_dict[_user_tag] > 1:\n                    _pos_rate = user_tags_pos_cnt_dict[_user_tag] / user_tags_cnt_dict[_user_tag]\n                    user_tags_pos_rate_npy[idx] += _pos_rate\n\n                user_tags_cnt_dict[_user_tag] += 1\n                if update:\n                    user_tags_pos_cnt_dict[_user_tag] += _answered_correctly\n            if div_num > 0:\n                user_tags_cnt_mean_npy[idx] //= div_num\n                user_tags_pos_rate_npy[idx] /= div_num\n            # user_cum_time\n            if time_diff <= 5 * 60:\n                user_cum_time_dict[_user_id] += time_diff\n            user_cum_time_npy[idx] = user_cum_time_dict[_user_id]\n            # user_timespan_win10\n            if time_diff <= 5 * 60:\n                user_timespan_win10_dict[_user_id].append(time_diff)\n                if len(user_timespan_win10_dict[_user_id]) > 10:\n                    del user_timespan_win10_dict[_user_id][0]\n            div_num = len(user_timespan_win10_dict[_user_id])\n            if div_num > 0:\n                user_timespan_win10_mean_npy[idx] = sum(user_timespan_win10_dict[_user_id]) / div_num\n\n            # user_continue_quest_cnt\n            user_continue_quest_cnt_npy[idx] = user_continue_quest_cnt_dict[_user_id]\n            user_continue_quest_cnt_dict[_user_id] += 1\n\n            idx += 1\n        else:\n            _tag = lecture_tag_dict[_content_id]\n            _part = lecture_part_dict[_content_id]\n            _type_of = lecture_type_dict[_content_id]\n\n            _user_part = str(_user_id) + '_' + str(_part)\n\n            # user_continue_quest_cnt\n            user_continue_quest_cnt_dict[_user_id] = 0\n\n\n    feats_df = pd.DataFrame({\n        'user_cnt': user_cnt_npy,\n        'user_pos_cnt': user_pos_cnt_npy,\n        'user_part_cnt': user_part_cnt_npy,\n        'user_part_pos_cnt': user_part_pos_cnt_npy,\n        'user_content_cnt': user_content_cnt_npy,\n        'user_content_pos_cnt': user_content_pos_cnt_npy,\n        'user_content_redo_cnt': user_content_redo_cnt_npy,\n        'user_content_mean_mean': user_content_mean_mean_npy,\n        'user_consecutive_pos_cnt': user_consecutive_pos_cnt_npy,\n        'user_pos_cnt_win25': user_pos_cnt_win25_npy,\n        'user_content_mean_win10': user_content_mean_win10_npy,\n        'user_explanation_cnt': user_explanation_cnt_npy,\n        'user_explanation_pos_cnt': user_explanation_pos_cnt_npy,\n        'user_elapse_time_mean': user_elapse_time_mean_npy,\n        'user_elapse_time_mean_win10': user_elapse_time_mean_win10_npy,\n        'user_last_timespan': user_last_timespan_npy,\n        'user_last_task_diff': user_last_task_diff_npy,\n        'user_content_appear_in_win5': user_content_appear_in_win5_npy,\n        'user_part_cnt_in_win10': user_part_cnt_in_win10_npy,\n        # 'user_order_in_session': user_order_in_session_npy,\n        'user_cum_time': user_cum_time_npy,\n        'user_timespan_win10_mean': user_timespan_win10_mean_npy,\n\n        'user_tags_cnt_mean': user_tags_cnt_mean_npy,\n        'user_tags_pos_rate': user_tags_pos_rate_npy,\n\n        'user_continue_quest_cnt': user_continue_quest_cnt_npy,\n    })\n    feats_df['user_target_mean'] = feats_df['user_pos_cnt'] / feats_df['user_cnt']\n    feats_df['user_part_target_mean'] = feats_df['user_part_pos_cnt'] / feats_df['user_part_cnt']\n    feats_df['user_content_target_mean'] = feats_df['user_content_pos_cnt'] / feats_df['user_content_cnt']\n    feats_df['user_content_mean_mean'] /= feats_df['user_cnt']\n\n    feats_df['user_explanation_mean'] = feats_df['user_explanation_cnt'] / feats_df['user_cnt']\n    feats_df['user_explanation_rate'] = feats_df['user_explanation_pos_cnt'] / feats_df['user_explanation_cnt']\n\n    feats_df['user_elapse_time_mean'] /= feats_df['user_cnt']\n\n    feats_df = feats_df.drop(['user_explanation_cnt', 'user_explanation_pos_cnt'], axis=1)\n    return feats_df\n\n\ndef update_user_feats(df, user_content_pos_cnt_dict,\n                      user_pos_cnt_dict, user_part_pos_cnt_dict, user_consecutive_pos_cnt_dict, user_target_win25_dict,\n                      user_explanation_pos_cnt_dict, user_tags_pos_cnt_dict\n                      ):\n    for (_user_id, _content_id, _task_container_id, _answered_correctly, _prior_question_elapsed_time,\n         _prior_question_had_explanation, _content_type_id, _timestamp) in df[used_cols].values:\n\n        if _content_type_id == False:\n            _bundle_id = question_bundle_dict[_content_id]\n            _part = question_part_dict[_content_id]\n            _tags = question_tags_dict[_content_id]\n\n            # user_pos_cnt\n            user_pos_cnt_dict[_user_id] += _answered_correctly\n            _user_part = str(_user_id) + '_' + str(_part)\n            # user_part_pos_cnt\n            user_part_pos_cnt_dict[_user_part] += _answered_correctly\n\n            _user_content = np.int64(_user_id) * 10_0000 + _content_id\n            # user_content_pos_cnt_dict[_user_content] += _answered_correctly\n#             user_content_feat_df.loc[_user_content, ['user_content_cnt']] += _answered_correctly\n            user_content_pos_cnt_dict[_user_content] += _answered_correctly\n            # user_consecutive_pos_cnt\n            if _answered_correctly:\n                user_consecutive_pos_cnt_dict[_user_id] += 1\n            else:\n                user_consecutive_pos_cnt_dict[_user_id] = 0\n            # user_target_win25_dict\n            user_target_win25_dict[_user_id].append(_answered_correctly)\n            if len(user_target_win25_dict[_user_id]) > 25:\n                tmp = user_target_win25_dict[_user_id]\n                user_target_win25_dict[_user_id] = tmp[-25:]\n            # user_explanation_pos_cnt\n            if _answered_correctly:\n                user_explanation_pos_cnt_dict[_user_id] += _prior_question_had_explanation\n            # user_tags_pos_cnt_dict\n            for _tag in _tags:\n                _user_tag = str(_user_id) + '_' + str(_tag)\n                user_tags_pos_cnt_dict[_user_tag] += _answered_correctly\n\n        else:\n            _tag = lecture_tag_dict[_content_id]\n            _part = lecture_part_dict[_content_id]\n            _type_of = lecture_type_dict[_content_id]\n\nif OFFLINE:\n    user_feat_train = make_user_loop_features(\n        df=train, content_target_mean_dict=content_target_mean_dict,\n        user_cnt_dict=user_cnt_dict,\n        user_pos_cnt_dict=user_pos_cnt_dict,\n        user_part_cnt_dict=user_part_cnt_dict,\n        user_part_pos_cnt_dict=user_part_pos_cnt_dict,\n        user_content_cnt_dict=user_content_cnt_dict,\n        user_content_pos_cnt_dict=user_content_pos_cnt_dict,\n        user_content_redo_cnt_dict=user_content_redo_cnt_dict,\n        user_content_mean_sum_dict=user_content_mean_sum_dict,\n        user_consecutive_pos_cnt_dict=user_consecutive_pos_cnt_dict,\n        user_target_win25_dict=user_target_win25_dict,\n        user_content_mean_win10_dict=user_content_mean_win10_dict,\n        user_explanation_cnt_dict=user_explanation_cnt_dict,\n        user_explanation_pos_cnt_dict=user_explanation_pos_cnt_dict,\n        user_elapse_time_sum_dict=user_elapse_time_sum_dict,\n        user_elapse_time_win10_dict=user_elapse_time_win10_dict,\n        user_last_timestamp_dict=user_last_timestamp_dict,\n        user_last_task_dict=user_last_task_dict,\n        user_content_win5_dict=user_content_win5_dict,\n        user_part_win10_dict=user_part_win10_dict,\n        bundle_state_dict=bundle_state_dict,\n        # user_order_in_session_dict=user_order_in_session_dict,\n        user_cum_time_dict=user_cum_time_dict,\n        user_timespan_win10_dict=user_timespan_win10_dict,\n        user_tags_cnt_dict=user_tags_cnt_dict,\n        user_tags_pos_cnt_dict=user_tags_pos_cnt_dict,\n        user_continue_quest_cnt_dict=user_continue_quest_cnt_dict,\n        update=True, isTrain=True\n    )\n    user_feat_train = reduce_mem_usage(user_feat_train, verbose=True)\n\n    user_feat_valid = make_user_loop_features(\n        df=valid, content_target_mean_dict=content_target_mean_dict,\n        user_cnt_dict=user_cnt_dict,\n        user_pos_cnt_dict=user_pos_cnt_dict,\n        user_part_cnt_dict=user_part_cnt_dict,\n        user_part_pos_cnt_dict=user_part_pos_cnt_dict,\n        user_content_cnt_dict=user_content_cnt_dict,\n        user_content_pos_cnt_dict=user_content_pos_cnt_dict,\n        user_content_redo_cnt_dict=user_content_redo_cnt_dict,\n        user_content_mean_sum_dict=user_content_mean_sum_dict,\n        user_consecutive_pos_cnt_dict=user_consecutive_pos_cnt_dict,\n        user_target_win25_dict=user_target_win25_dict,\n        user_content_mean_win10_dict=user_content_mean_win10_dict,\n        user_explanation_cnt_dict=user_explanation_cnt_dict,\n        user_explanation_pos_cnt_dict=user_explanation_pos_cnt_dict,\n        user_elapse_time_sum_dict=user_elapse_time_sum_dict,\n        user_elapse_time_win10_dict=user_elapse_time_win10_dict,\n        user_last_timestamp_dict=user_last_timestamp_dict,\n        user_last_task_dict=user_last_task_dict,\n        user_content_win5_dict=user_content_win5_dict,\n        user_part_win10_dict=user_part_win10_dict,\n        bundle_state_dict=bundle_state_dict,\n        # user_order_in_session_dict=user_order_in_session_dict,\n        user_cum_time_dict=user_cum_time_dict,\n        user_timespan_win10_dict=user_timespan_win10_dict,\n        user_tags_cnt_dict=user_tags_cnt_dict,\n        user_tags_pos_cnt_dict=user_tags_pos_cnt_dict,\n        user_continue_quest_cnt_dict=user_continue_quest_cnt_dict,\n        update=True, isTrain=True\n    )\n    user_feat_valid = reduce_mem_usage(user_feat_valid, verbose=True)\n    del train, valid\n    gc.collect()\n    user_feat_cols = user_feat_train.columns.values.tolist()\n    config_dict['user_feat_cols'] = user_feat_cols\nuser_feat_cols = config_dict['user_feat_cols']\n\n########################################################################################################################\n##### Content Static Feat\ndef make_content_feat2(id_df, df, type):\n    df['content_id'] = id_df['content_id'].values\n    file_name = f'content_feat2_{type}.pkl'\n\n    feat_df = df.groupby('content_id', as_index=False)['user_last_timespan'].median(). \\\n        rename(columns={'answered_correctly': 'content_timespan_median'})\n\n    save_pickle(feat_df, save_path=f'{CACHE_PATH}/{file_name}')\n    feat_df = reduce_mem_usage(feat_df, verbose=True)\n    return feat_df\n\n\nif OFFLINE:\n    content_feat2 = make_content_feat2(id_df=ques_train, df=user_feat_train.copy(deep=True), type='train')\n    content_feat2_test = make_content_feat2(id_df=pd.concat([ques_train, ques_valid]), df=pd.concat([user_feat_train, user_feat_valid]), type='test')\n    print('content_feat2:\\n', content_feat2.head())\nelse:\n    content_feat2_test = load_pickle(f'{CACHE_PATH}/content_feat2_test.pkl')\n    content_feat2_test = reduce_mem_usage(content_feat2_test, verbose=True)\ncontent_feat2_cols = [col for col in content_feat2_test if col != 'content_id']\n########################################################################################################################\n##### Merge Feat\ndef merge_features(df, static_feat, content_feat, content_feat2, user_feat):\n    feat_df = static_feat.copy(deep=True)\n    # print('1')\n    feat_df[content_feat_cols] = content_feat.set_index('content_id').reindex(df['content_id'].values).values\n    feat_df[content_feat2_cols] = content_feat2.set_index('content_id').reindex(df['content_id'].values).values\n    # print('2')\n    # feat_df[part_feat_cols] = part_feat.set_index('part').reindex(feat_df['part'].values).values\n    for col in user_feat_cols:\n        feat_df[col] = user_feat[col].values\n    # print('3')\n\n    # # ### cross feat\n    # feat_df['timestamp_pos_cnt_mean'] = feat_df['timestamp'] / feat_df['user_pos_cnt']\n\n    return feat_df\n# cross_feat_cols = ['timestamp_pos_cnt_mean']\n\nif OFFLINE:\n    train_feat = merge_features(df=ques_train,\n                                static_feat=state_feat_train,\n                                content_feat=content_feat,\n                                content_feat2=content_feat2,\n                                # part_feat=part_feat,\n                                user_feat=user_feat_train)\n    del state_feat_train, user_feat_train\n    gc.collect()\n    train_feat = reduce_mem_usage(train_feat, verbose=True)\n\n    valid_feat = merge_features(df=ques_valid,\n                                static_feat=state_feat_valid,\n                                content_feat=content_feat,\n                                content_feat2=content_feat2,\n                                # part_feat=part_feat,\n                                user_feat=user_feat_valid)\n    del state_feat_valid, user_feat_valid, content_feat\n    gc.collect()\n    valid_feat = reduce_mem_usage(valid_feat, verbose=True)\n    # print('train_feat: ', len(train_feat))\n    # train_feat.to_csv('train_feat2.csv', index=False)\n\nfeat_cols = static_feat_cols + content_feat_cols + content_feat2_cols + user_feat_cols# + cross_feat_cols# + part_feat_cols\nprint(f'Feat Nums: {len(feat_cols)}')\n# ########################################################################################################################\n# ##### Cross Feat\n# if OFFLINE:\n#     train['answered_correctly_avg_u_c'] = train['answered_correctly_avg_u'] * train['answered_correctly_avg_c'] / \\\n#                                           (train['answered_correctly_avg_u'] + train['answered_correctly_avg_c'])\n#     valid['answered_correctly_avg_u_c'] = valid['answered_correctly_avg_u'] * valid['answered_correctly_avg_c'] / \\\n#                                           (valid['answered_correctly_avg_u'] + valid['answered_correctly_avg_c'])\n#\n########################################################################################################################\n##### Train Model\nTARGET = 'answered_correctly'\nif OFFLINE:\n    # train_feat = train_feat.loc[train_index]\n    # train_labels = ques_train.loc[train_index, TARGET]\n    train_labels = ques_train[TARGET]\n    valid_labels = ques_valid[TARGET]\n    del ques_train, ques_valid\n    gc.collect()\n\n    lgb_train = lgb.Dataset(train_feat[feat_cols], train_labels, categorical_feature=['content_id'])\n    lgb_valid = lgb.Dataset(valid_feat[feat_cols], valid_labels, categorical_feature=['content_id'])\n    del train_feat, train_labels\n    gc.collect()\n\n    params = {\n        'objective': 'binary',\n        'seed': 28,\n        'num_leaves': 256,\n        'max_bin': 1024,\n        'feature_fraction': 0.6,\n        'max_depth': 8,\n        'verbose': -1,\n        'cat_l2': 30,\n        'cat_smooth': 20,\n        'num_threads': 20,\n    }\n    feature_importance_df = pd.DataFrame()\n    feature_importance_df[\"feature\"] = feat_cols\n\n    model = lgb.train(\n        params, lgb_train,\n        valid_sets=lgb_valid,\n        verbose_eval=50,\n        num_boost_round=10000,\n        early_stopping_rounds=50\n    )\n    model.save_model(f'{CACHE_PATH}/model.txt')\n\n    feature_importance_df['importance'] = model.feature_importance()\n    feature_importance_df = feature_importance_df.sort_values('importance', ascending=False)\n    feature_importance_df.to_csv(f'{CACHE_PATH}/feature_importance.csv', index=False)\n\n    valid_score = roc_auc_score(valid_labels, model.predict(valid_feat[feat_cols]))\n    print(f'auc: {valid_score:.4f}')\n    # lgb.plot_importance(model)\n\n    ### save user_content feat\n    user_content_feat_df = pd.DataFrame()\n    user_content_feat_df['user_content_id'] = user_content_cnt_dict.keys()\n    user_content_feat_df['user_content_cnt'] = user_content_cnt_dict.values()\n\n    user_content_pos_cnt_feat_buff = pd.DataFrame()\n    user_content_pos_cnt_feat_buff['user_content_id'] = user_content_pos_cnt_dict.keys()\n    user_content_pos_cnt_feat_buff['user_content_pos_cnt'] = user_content_pos_cnt_dict.values()\n    user_content_feat_df = user_content_feat_df.merge(user_content_pos_cnt_feat_buff, on='user_content_id', how='left')\n    del user_content_pos_cnt_feat_buff\n\n    user_content_feat_df = user_content_feat_df.set_index('user_content_id')\n    user_content_feat_df.to_pickle(f'{CACHE_PATH}/user_content_feat.pkl')\n\n    save_pickle(config_dict, config_file)\n    save_pickle(user_cnt_dict, f'{CACHE_PATH}/user_cnt_dict.pkl')\n    save_pickle(user_pos_cnt_dict, f'{CACHE_PATH}/user_pos_cnt_dict.pkl')\n    save_pickle(user_part_cnt_dict, f'{CACHE_PATH}/user_part_cnt_dict.pkl')\n    save_pickle(user_part_pos_cnt_dict, f'{CACHE_PATH}/user_part_pos_cnt_dict.pkl')\n    save_pickle(user_content_redo_cnt_dict, f'{CACHE_PATH}/user_content_redo_cnt_dict.pkl')\n    save_pickle(user_content_mean_sum_dict, f'{CACHE_PATH}/user_content_mean_sum_dict.pkl')\n    save_pickle(user_consecutive_pos_cnt_dict, f'{CACHE_PATH}/user_consecutive_pos_cnt_dict.pkl')\n    save_pickle(user_target_win25_dict, f'{CACHE_PATH}/user_target_win25_dict.pkl')\n    save_pickle(user_content_mean_win10_dict, f'{CACHE_PATH}/user_content_mean_win10_dict.pkl')\n    save_pickle(user_explanation_cnt_dict, f'{CACHE_PATH}/user_explanation_cnt_dict.pkl')\n    save_pickle(user_explanation_pos_cnt_dict, f'{CACHE_PATH}/user_explanation_pos_cnt_dict.pkl')\n    save_pickle(user_elapse_time_sum_dict, f'{CACHE_PATH}/user_elapse_time_sum_dict.pkl')\n    save_pickle(user_elapse_time_win10_dict, f'{CACHE_PATH}/user_elapse_time_win10_dict.pkl')\n    save_pickle(user_last_timestamp_dict, f'{CACHE_PATH}/user_last_timestamp_dict.pkl')\n    save_pickle(user_last_task_dict, f'{CACHE_PATH}/user_last_task_dict.pkl')\n    save_pickle(user_content_win5_dict, f'{CACHE_PATH}/user_content_win5_dict.pkl')\n    save_pickle(user_part_win10_dict, f'{CACHE_PATH}/user_part_win10_dict.pkl')\n    save_pickle(bundle_state_dict, f'{CACHE_PATH}/bundle_state_dict.pkl')\n    # save_pickle(user_order_in_session_dict, f'{CACHE_PATH}/user_order_in_session_dict.pkl')\n    save_pickle(user_cum_time_dict, f'{CACHE_PATH}/user_cum_time_dict.pkl')\n    save_pickle(user_timespan_win10_dict, f'{CACHE_PATH}/user_timespan_win10_dict.pkl')\n    save_pickle(user_tags_cnt_dict, f'{CACHE_PATH}/user_tags_cnt_dict.pkl')\n    save_pickle(user_tags_pos_cnt_dict, f'{CACHE_PATH}/user_tags_pos_cnt_dict.pkl')\n    save_pickle(user_continue_quest_cnt_dict, f'{CACHE_PATH}/user_continue_quest_cnt_dict.pkl')\nelse:\n    model = lgb.Booster(model_file=f'{CACHE_PATH}/model.txt')\n    print('load model finished')\n\n\n########################################################################################################################\n##### Inference\nclass Iter_Valid(object):\n    def __init__(self, df, max_user=1000):\n        df = df.reset_index(drop=True)\n        self.df = df\n        self.user_answer = df['user_answer'].astype(str).values\n        self.answered_correctly = df['answered_correctly'].astype(str).values\n        df['prior_group_responses'] = \"[]\"\n        df['prior_group_answers_correct'] = \"[]\"\n        self.sample_df = df[df['content_type_id'] == 0][['row_id']]\n        self.sample_df['answered_correctly'] = 0\n        self.len = len(df)\n        self.user_id = df.user_id.values\n        self.task_container_id = df.task_container_id.values\n        self.content_type_id = df.content_type_id.values\n        self.max_user = max_user\n        self.current = 0\n        self.pre_user_answer_list = []\n        self.pre_answered_correctly_list = []\n\n    def __iter__(self):\n        return self\n\n    def fix_df(self, user_answer_list, answered_correctly_list, pre_start):\n        df = self.df[pre_start:self.current].copy()\n        sample_df = self.sample_df[pre_start:self.current].copy()\n        df.loc[pre_start, 'prior_group_responses'] = '[' + \",\".join(self.pre_user_answer_list) + ']'\n        df.loc[pre_start, 'prior_group_answers_correct'] = '[' + \",\".join(self.pre_answered_correctly_list) + ']'\n        self.pre_user_answer_list = user_answer_list\n        self.pre_answered_correctly_list = answered_correctly_list\n        return df, sample_df\n\n    def __next__(self):\n        added_user = set()\n        pre_start = self.current\n        pre_added_user = -1\n        pre_task_container_id = -1\n        pre_content_type_id = -1\n        user_answer_list = []\n        answered_correctly_list = []\n        while self.current < self.len:\n            crr_user_id = self.user_id[self.current]\n            crr_task_container_id = self.task_container_id[self.current]\n            crr_content_type_id = self.content_type_id[self.current]\n            if crr_user_id in added_user and (crr_user_id != pre_added_user or (\n                    crr_task_container_id != pre_task_container_id and crr_content_type_id == 0 and pre_content_type_id == 0)):\n                # known user(not prev user or (differnt task container and both question))\n                return self.fix_df(user_answer_list, answered_correctly_list, pre_start)\n            if len(added_user) == self.max_user:\n                if crr_user_id == pre_added_user and (\n                        crr_task_container_id == pre_task_container_id or crr_content_type_id == 1):\n                    user_answer_list.append(self.user_answer[self.current])\n                    answered_correctly_list.append(self.answered_correctly[self.current])\n                    self.current += 1\n                    continue\n                else:\n                    return self.fix_df(user_answer_list, answered_correctly_list, pre_start)\n            added_user.add(crr_user_id)\n            pre_added_user = crr_user_id\n            pre_task_container_id = crr_task_container_id\n            pre_content_type_id = crr_content_type_id\n            user_answer_list.append(self.user_answer[self.current])\n            answered_correctly_list.append(self.answered_correctly[self.current])\n            self.current += 1\n        if pre_start < self.current:\n            return self.fix_df(user_answer_list, answered_correctly_list, pre_start)\n        else:\n            raise StopIteration()\n\n\nif OFFLINE:\n    target_df = pd.read_pickle(f'{MY_DATA_PATH}/valid.pickle')\n    iter_test = Iter_Valid(target_df, max_user=1000)\n    predicted = []\n\n\n    def set_predict(df):\n        predicted.append(df)\nelse:\n    import riiideducation\n\n    env = riiideducation.make_env()\n    iter_test = env.iter_test()\n    set_predict = env.predict\n\nprevious_test_df = None\nidx = 0\nfor (test_df, sample_prediction_df) in iter_test:\n    print('idx: ', idx)\n    test_df[TARGET] = 0\n    test_df['prior_question_elapsed_time'] //= 1000\n    test_df['timestamp'] /= 1000\n    test_df['prior_question_had_explanation'] = test_df['prior_question_had_explanation'].fillna(False).astype('int8')\n    test_df['prior_question_elapsed_time'] = test_df['prior_question_elapsed_time'].fillna(prior_question_elapsed_time_mean).astype(np.int32)\n#     test_df.loc[test_df['content_id'] > 13522, 'content_id'] = 13522\n    test_df = reduce_mem_usage(test_df, verbose=True)\n    # test_df['day'] = test_df['timestamp'] // 1000 // 60 // 60# // 24\n    ques_test = test_df.loc[test_df.content_type_id == False, ['row_id', 'content_id']].reset_index(drop=True)\n    ques_test = reduce_mem_usage(ques_test, verbose=True)\n    if previous_test_df is not None:\n        # print('np.array(eval(test_df[\"prior_group_answers_correct\"].iloc[0])):\\n', np.array(eval(test_df[\"prior_group_answers_correct\"].iloc[0])))\n        previous_test_df[TARGET] = eval(test_df[\"prior_group_answers_correct\"].iloc[0])\n\n        update_user_feats(\n            df=previous_test_df, user_content_pos_cnt_dict=user_content_pos_cnt_dict,\n            user_pos_cnt_dict=user_pos_cnt_dict,\n            user_part_pos_cnt_dict=user_part_pos_cnt_dict,\n            user_consecutive_pos_cnt_dict=user_consecutive_pos_cnt_dict,\n            user_target_win25_dict=user_target_win25_dict,\n            user_explanation_pos_cnt_dict=user_explanation_pos_cnt_dict,\n            user_tags_pos_cnt_dict=user_tags_pos_cnt_dict,\n        )\n    previous_test_df = test_df.copy(deep=True)\n\n    state_feat_test = get_stat_feat(df=test_df.copy(deep=True), feat_cols=static_feat_cols)\n    state_feat_test = reduce_mem_usage(state_feat_test, verbose=True)\n    # for _part in part_elapse_time_mean_dict:\n    #     state_feat_test.loc[state_feat_test.part == _part, ['prior_question_elapsed_time']] = \\\n    #         state_feat_test.loc[state_feat_test.part == _part, ['prior_question_elapsed_time']].fillna(part_elapse_time_mean_dict[_part])\n\n    user_feat_test = make_user_loop_features(\n        df=test_df, content_target_mean_dict=content_target_mean_dict,\n        user_cnt_dict=user_cnt_dict,\n        user_pos_cnt_dict=user_pos_cnt_dict,\n        user_part_cnt_dict=user_part_cnt_dict,\n        user_part_pos_cnt_dict=user_part_pos_cnt_dict,\n        user_content_cnt_dict=user_content_cnt_dict,\n        user_content_pos_cnt_dict=user_content_pos_cnt_dict,\n        user_content_redo_cnt_dict=user_content_redo_cnt_dict,\n        user_content_mean_sum_dict=user_content_mean_sum_dict,\n        user_consecutive_pos_cnt_dict=user_consecutive_pos_cnt_dict,\n        user_target_win25_dict=user_target_win25_dict,\n        user_content_mean_win10_dict=user_content_mean_win10_dict,\n        user_explanation_cnt_dict=user_explanation_cnt_dict,\n        user_explanation_pos_cnt_dict=user_explanation_pos_cnt_dict,\n        user_elapse_time_sum_dict=user_elapse_time_sum_dict,\n        user_elapse_time_win10_dict=user_elapse_time_win10_dict,\n        user_last_timestamp_dict=user_last_timestamp_dict,\n        user_last_task_dict=user_last_task_dict,\n        user_content_win5_dict=user_content_win5_dict,\n        user_part_win10_dict=user_part_win10_dict,\n        bundle_state_dict=bundle_state_dict,\n        # user_order_in_session_dict=user_order_in_session_dict,\n        user_cum_time_dict=user_cum_time_dict,\n        user_timespan_win10_dict=user_timespan_win10_dict,\n        user_tags_cnt_dict=user_tags_cnt_dict,\n        user_tags_pos_cnt_dict=user_tags_pos_cnt_dict,\n        user_continue_quest_cnt_dict=user_continue_quest_cnt_dict,\n        update=False, isTrain=False\n    )\n    test_feat = merge_features(df=ques_test,\n                               static_feat=state_feat_test,\n                               content_feat=content_feat_test,\n                               content_feat2=content_feat2_test,\n                               # part_feat=part_feat_test,\n                               user_feat=user_feat_test)\n    # test_feat = reduce_mem_usage(test_feat, verbose=True)\n\n    ques_test[TARGET] = model.predict(test_feat[feat_cols].values.reshape(len(test_feat), len(feat_cols)))\n    set_predict(ques_test[['row_id', TARGET]])\n\n    idx += 1\n    if OFFLINE:\n        if idx >= 5:\n            break\n\n# auc: 0.7265\n# auc: 0.7274\n# auc: 0.7527 add user 2feat\n# auc: 0.7652\n# auc: 0.7728 add user_last_timespan\n# auc: 0.7733 add user_part_last_timespan\n# auc: 0.7732 num_leaves32-->64 (all data auc: 0.7812, LB 0.780)\n# auc: 0.7734 add user_lect_cnt\n# auc: 0.7738 add user_part_lect_cnt\n# auc: 0.7741 add user_last_target\n# auc: 0.7743 add user_part_last_target\n# auc: 0.7744 add user_last_part\n# auc: 0.7752 add tag1234\n# auc: 0.7755 add content_cnt\n# auc: 0.7764 add user_last_task_diff\n# auc: 0.7766 add content_bundle_same\n# auc: 0.7775 add user_part_elapsed_time_mean\n# auc: 0.7778 add user_part_last_elapsed_time_diff\n# auc: 0.7781 add user_last_content_type\n# auc: 0.7783 add user_same_content\n# auc: 0.7784 add user_content_repeat_num\n# auc: 0.7789 add tags_w2v_feat\n\n# auc: 0.7742 del user_content_cnt\n# auc: 0.7745 add user_lect_cnt_rate\n# auc: 0.7794 add user_content_mean_mean all data auc: 0.7877\n# auc: 0.7835 add user_content_cnt (all data auc: 0.7916, LB ??)\n# auc: 0.7839 add user_last_pos_timespan\n# auc: 0.7841 add user_part_last_pos_timespan\n\n# auc: 0.7826 del tags_w2v (all data auc: 0.7916, LB 0.789)\n# auc: 0.7828 add user_part_mean_mean\n# auc: 0.7834 add part_content_num (all data auc: 0.7919, LB ??)\n# auc: 0.7832 time/1000\n# auc: 0.7834 exlanation pos_cnt, target_mean\n# auc: 0.7839 add user_pos_cnt 5/10/30\n# auc: 0.7941 add_user_times\n# auc: 0.7845 add user_tags cnt pos_rate\n# auc: 0.7850 prior_question_elapsed_time fillna\n# auc: 0.7853 add timestamp_pos_cnt_mean\n# auc: 0.7858 add content_id\n# auc: 0.7864 change params\n# auc: 0.7872 change bugs\n# auc: 0.7877 add user_continue_pos_cnt\n# auc: 0.7866 del tags_feat\n# auc: 0.7884 add user_5min_cnt user_5min_pos_cnt user_5min_target_mean\n# auc: 0.7888 add user_30min_cnt user_30min_pos_cnt user_30min_target_mean\n# auc: 0.7890 add user_2min_cnt user_2min_pos_cnt user_2min_target_mean\n# auc: 0.7894 add content_timestamp_diff_median\n# auc: 0.7898 add tags_feat\n\n##############################\n### new\n\n# auc: 0.7626 user_cnt/pos_cnt/rate, user_content_cnt/pos_cnt/rate part prior_question_elapsed_time content_id\n# auc: 0.7638 add user_continue_quest_cnt\n# auc: 0.7675 add user_part_cnt/pos_cnt/rate\n# auc: 0.7680 add user_content_redo_cnt\n# auc: 0.7752 add user_content_mean_mean\n# auc: 0.7757 add user_consecutive_pos_cnt\n# auc: 0.7760 add user_pos_cnt_win25\n# auc: 0.7762 add user_content_mean_win10\n# auc: 0.7761 add user_explanation_mean/rate\n# auc: 0.7764 add user_elapse_time_mean\n# auc: 0.7764 add user_elapse_time_mean_win10\n# auc: 0.7857 add user_last_timespan\n# auc: 0.7867 add user_last_task_diff\n# auc: 0.7865 del user_content_pos_cnt\n# auc: 0.7872 add user_content_win5\n# auc: 0.7873 add user_part_win10\n# auc: 0.7874 del content_user_mean_mean content_explanation_mean\n# auc: 0.7884 change timediff\n# auc: 0.7884 change content_timespan_median\n# auc: 0.7876 add user_order_in_session\n# auc: 0.7887 add user_tags_cnt_mean user_tags_pos_rate\n# auc: 0.7888 del user_order_in_session\n# auc: 0.7894 add user_cum_time\n# auc: 0.7901 add user_timespan_win10_mean(all data auc: 0.7980)","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}