{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":31254,"databundleVersionId":3103714,"sourceType":"competition"},{"sourceId":14241442,"sourceType":"datasetVersion","datasetId":9085906}],"dockerImageVersionId":31239,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport pickle\n\ntrans = pd.read_csv(\"/kaggle/input/h-and-m-personalized-fashion-recommendations/transactions_train.csv\")\nuid2idx = pickle.load(open('/kaggle/input/mapping/index_id_map/user_id2index.pkl', \"rb\"))\niid2idx = pickle.load(open('/kaggle/input/mapping/index_id_map/item_id2index.pkl', \"rb\"))\n# đảm bảo đúng thứ tự thời gian\ntrans['customer_id'] = trans['customer_id'].map(uid2idx)\ntrans['article_id'] = trans['article_id'].map(iid2idx)\n\n\ntrans[\"t_dat\"] = pd.to_datetime(trans[\"t_dat\"])\n\ntrans = trans.sort_values([\"customer_id\", \"t_dat\"])\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-08T13:54:33.818800Z","iopub.execute_input":"2026-01-08T13:54:33.819202Z","iopub.status.idle":"2026-01-08T13:55:39.803089Z","shell.execute_reply.started":"2026-01-08T13:54:33.819175Z","shell.execute_reply":"2026-01-08T13:55:39.801994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trans","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T13:55:41.947605Z","iopub.execute_input":"2026-01-08T13:55:41.947981Z","iopub.status.idle":"2026-01-08T13:55:41.962481Z","shell.execute_reply.started":"2026-01-08T13:55:41.947959Z","shell.execute_reply":"2026-01-08T13:55:41.961549Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trans['week'] = (pd.to_datetime('2020-09-29') - pd.to_datetime(trans['t_dat'])).dt.days // 7\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T13:55:48.881718Z","iopub.execute_input":"2026-01-08T13:55:48.882019Z","iopub.status.idle":"2026-01-08T13:55:50.381643Z","shell.execute_reply.started":"2026-01-08T13:55:48.881998Z","shell.execute_reply":"2026-01-08T13:55:50.380696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sentences = (\n    trans[(trans['week']>1) & (trans['week']<=6)]\n    .groupby(\"customer_id\")[\"article_id\"]\n    .apply(list)\n    .tolist()\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:03:19.864591Z","iopub.execute_input":"2026-01-08T14:03:19.865160Z","iopub.status.idle":"2026-01-08T14:03:24.497930Z","shell.execute_reply.started":"2026-01-08T14:03:19.865125Z","shell.execute_reply":"2026-01-08T14:03:24.496855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from gensim.models import Word2Vec\n\nitem2vec = Word2Vec(\n    sentences=sentences,\n    vector_size=64,     # 32–128 đều ổn\n    window=5,           # 3–7\n    min_count=5,        # bỏ item quá hiếm\n    workers=8,\n    sg=1,               # skip-gram (BẮT BUỘC)\n    negative=10,\n    epochs=10\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:03:33.580247Z","iopub.execute_input":"2026-01-08T14:03:33.580905Z","iopub.status.idle":"2026-01-08T14:04:19.978699Z","shell.execute_reply.started":"2026-01-08T14:03:33.580876Z","shell.execute_reply":"2026-01-08T14:04:19.977674Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"item2vec.save(\"item2vec_12w.model\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:04:19.980403Z","iopub.execute_input":"2026-01-08T14:04:19.980972Z","iopub.status.idle":"2026-01-08T14:04:20.010945Z","shell.execute_reply.started":"2026-01-08T14:04:19.980937Z","shell.execute_reply":"2026-01-08T14:04:20.009825Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nTOPK = 50\nitem_neighbors = {}\n\nfor item in item2vec.wv.index_to_key:\n    sims = item2vec.wv.most_similar(item, topn=TOPK)\n    item_neighbors[item] = [(i, s) for i, s in sims]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:04:20.012024Z","iopub.execute_input":"2026-01-08T14:04:20.012550Z","iopub.status.idle":"2026-01-08T14:04:31.096906Z","shell.execute_reply.started":"2026-01-08T14:04:20.012526Z","shell.execute_reply":"2026-01-08T14:04:31.095719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pickle\n\nwith open(\"item2vec_neighbors.pkl\", \"wb\") as f:\n    pickle.dump(item_neighbors, f)\n","metadata":{"trusted":true,"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"user_hist = (\n    trans[(trans[\"week\"] >= 2) & (trans[\"week\"] <= 3)]\n    .sort_values(\"t_dat\", ascending=False)\n    .groupby(\"customer_id\")[\"article_id\"]\n    .apply(list)\n    .to_dict()\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:04:31.098798Z","iopub.execute_input":"2026-01-08T14:04:31.099114Z","iopub.status.idle":"2026-01-08T14:04:33.494986Z","shell.execute_reply.started":"2026-01-08T14:04:31.099057Z","shell.execute_reply":"2026-01-08T14:04:33.493920Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def item2vec_candidates(\n    user_items,\n    item_neighbors,\n    topk_per_item=20,\n    max_candidates=200,\n    item_col=\"article_id\",\n    score_col=\"score\"\n):\n    scores = {}\n\n    for idx, item in enumerate(user_items):\n        if item not in item_neighbors:\n            continue\n        \n        weight = 1 / (idx + 1)\n        \n        for nei, sim in item_neighbors[item][:topk_per_item]:\n            scores[nei] = scores.get(nei, 0) + sim * weight\n\n    if not scores:\n        return pd.DataFrame(columns=[item_col, score_col])\n\n    df = (\n        pd.DataFrame(scores.items(), columns=[item_col, score_col])\n        .sort_values(score_col, ascending=False)\n        .head(max_candidates)\n        .reset_index(drop=True)\n    )\n\n    return df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:14:43.263683Z","iopub.execute_input":"2026-01-08T14:14:43.264658Z","iopub.status.idle":"2026-01-08T14:14:43.271580Z","shell.execute_reply.started":"2026-01-08T14:14:43.264584Z","shell.execute_reply":"2026-01-08T14:14:43.270398Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"user_candidates = {}\n\nfor user, items in user_hist.items():\n    user_candidates[user] = item2vec_candidates(\n        items,\n        item_neighbors\n    )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:12:11.719033Z","iopub.execute_input":"2026-01-08T14:12:11.719547Z","iopub.status.idle":"2026-01-08T14:12:16.285626Z","shell.execute_reply.started":"2026-01-08T14:12:11.719515Z","shell.execute_reply":"2026-01-08T14:12:16.284579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def last_item_candidates(\n    user_items,\n    item_neighbors,\n    topk=50\n):\n    last_item = user_items[-1]\n    if last_item not in item_neighbors:\n        return []\n\n    return [i for i, _ in item_neighbors[last_item][:topk]]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:05:50.947155Z","iopub.execute_input":"2026-01-08T14:05:50.947919Z","iopub.status.idle":"2026-01-08T14:05:50.953098Z","shell.execute_reply.started":"2026-01-08T14:05:50.947889Z","shell.execute_reply":"2026-01-08T14:05:50.951990Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rows = []\n\nfor user, items in user_hist.items():\n    df_cand = item2vec_candidates(\n        user_items=items,\n        item_neighbors=item_neighbors,\n        max_candidates=200\n    )\n\n    df_cand[\"customer_id\"] = user\n    df_cand[\"method\"] = \"Item2Vec\"\n\n    rows.append(df_cand)\n\ncand_df = pd.concat(rows, ignore_index=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:15:50.689524Z","iopub.execute_input":"2026-01-08T14:15:50.690516Z","iopub.status.idle":"2026-01-08T14:18:25.342860Z","shell.execute_reply.started":"2026-01-08T14:15:50.690463Z","shell.execute_reply":"2026-01-08T14:18:25.341868Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cand_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T14:18:25.344262Z","iopub.execute_input":"2026-01-08T14:18:25.344725Z","iopub.status.idle":"2026-01-08T14:18:25.357167Z","shell.execute_reply.started":"2026-01-08T14:18:25.344694Z","shell.execute_reply":"2026-01-08T14:18:25.356013Z"}},"outputs":[],"execution_count":null}]}