{"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"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":38760,"databundleVersionId":4493939,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import json, time\nimport orjson\nfrom collections import Counter\nimport pandas as pd\nfrom lightgbm import LGBMClassifier\nfrom sklearn.model_selection import train_test_split\n\n# 1) Параметры и пути\nDATA_DIR       = '/kaggle/input/otto-recommender-system'\nTRAIN_PATH     = f'{DATA_DIR}/train.jsonl'\nTEST_PATH      = f'{DATA_DIR}/test.jsonl'\nMAX_TRAIN_SESS = 2_000_000\nTOP_K_POP      = 100\nWEIGHTS        = {'clicks':1,'carts':3,'orders':5}\n\n# 2) Собираем глобальную популярность (пул кандидатов)\npop = Counter()\nt0 = time.time()\nwith open(TRAIN_PATH,'r') as f:\n    for i, line in enumerate(f,1):\n        sess = orjson.loads(line)\n        for ev in sess['events']:\n            pop[ev['aid']] += WEIGHTS[ev['type']]\n        if i >= MAX_TRAIN_SESS:\n            break\nprint(f\"[pop] {i} сессий за {time.time()-t0:.1f}s\")\ntop_pop = [aid for aid,_ in pop.most_common(TOP_K_POP)]\n\n# 3) Формируем обучающую выборку с фичами и метками\nrows, t0 = [], time.time()\nwith open(TRAIN_PATH,'r') as f:\n    for i, line in enumerate(f,1):\n        if i > MAX_TRAIN_SESS: break\n        evs = orjson.loads(line)['events']\n        # последние 5 уникальных aid\n        last5 = []\n        for ev in reversed(evs):\n            a = ev['aid']\n            if a not in last5: last5.append(a)\n            if len(last5)==5: break\n        # кандидаты = last5 + дополняем популярными\n        cands = list(last5)\n        for a in top_pop:\n            if len(cands)>=10: break\n            if a not in cands: cands.append(a)\n        # инициализируем stats с правильными ключами\n        stats = {a:{'cnt_clicks':0,'cnt_carts':0,'cnt_orders':0,\n                    'first_pos':-1,'last_pos':-1} for a in cands}\n        for idx, ev in enumerate(evs):\n            a,t = ev['aid'], ev['type']\n            if a in stats:\n                s = stats[a]\n                if s['first_pos']==-1: s['first_pos']=idx\n                s['last_pos']=idx\n                s[f'cnt_{t}'] += 1\n        # записываем строки\n        for a,s in stats.items():\n            rows.append({\n                'cnt_clicks': s['cnt_clicks'],\n                'cnt_carts':  s['cnt_carts'],\n                'cnt_orders': s['cnt_orders'],\n                'first_pos':  s['first_pos'],\n                'last_pos':   s['last_pos'],\n                'pop':        pop[a],\n                'label':      int(s['cnt_orders']>0)\n            })\nprint(f\"[train df] {len(rows)} строк за {time.time()-t0:.1f}s\")\ndf = pd.DataFrame(rows)\nfeatures = ['cnt_clicks','cnt_carts','cnt_orders','first_pos','last_pos','pop']\n\n# 4) Обучаем LGBMClassifier\nX, y = df[features], df['label']\ntr, vl = train_test_split(df, test_size=0.1, random_state=42, stratify=y)\nmodel = LGBMClassifier(objective='binary', n_estimators=200, learning_rate=0.1, num_leaves=31, random_state=42)\nt1 = time.time()\nmodel.fit(tr[features], tr['label'])\nprint(f\"[lgbm] обучено за {time.time()-t1:.1f}s\")\n\n# 5) Предсказание и формирование submission_two_level.csv\nout, t2 = [], time.time()\nwith open(TEST_PATH,'r') as f:\n    for line in f:\n        sess = orjson.loads(line)\n        evs  = sess['events']\n        # recency\n        last5 = []\n        for ev in reversed(evs):\n            a = ev['aid']\n            if a not in last5: last5.append(a)\n            if len(last5)==5: break\n        # кандидаты\n        cands = list(last5)\n        for a in top_pop:\n            if len(cands)>=10: break\n            if a not in cands: cands.append(a)\n        # фичи теста\n        feats = []\n        for a in cands:\n            s = {'cnt_clicks':0,'cnt_carts':0,'cnt_orders':0,\n                 'first_pos':-1,'last_pos':-1}\n            for idx, ev in enumerate(evs):\n                if ev['aid']==a:\n                    if s['first_pos']==-1: s['first_pos']=idx\n                    s['last_pos']=idx\n                    s[f'cnt_{ev[\"type\"]}'] += 1\n            feats.append({**s, 'pop': pop[a], 'aid': a})\n        tmp = pd.DataFrame(feats)\n        tmp['score'] = model.predict_proba(tmp[features])[:,1]\n        top10 = tmp.sort_values('score', ascending=False).head(10)['aid'].tolist()\n        lab = ' '.join(map(str, top10))\n        sid = sess['session']\n        out += [\n            {'session_type':f'{sid}_clicks','labels':lab},\n            {'session_type':f'{sid}_carts', 'labels':lab},\n            {'session_type':f'{sid}_orders','labels':lab}\n        ]\nprint(f\"[pred] сформировано {len(out)} строк за {time.time()-t2:.1f}s\")\npd.DataFrame(out).to_csv('submission_two_level.csv', index=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}