{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":10445,"databundleVersionId":200747,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pandas as pd\nfrom sklearn.preprocessing import LabelEncoder\nfrom xgboost import XGBRegressor\nimport matplotlib.pyplot as plt\n\nrseed = 71  \nDATA_DIR = \"/kaggle/input/elo-merchant-category-recommendation\"","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#―――――――――――――――――――――――\n# 1. データ読み込み\n#―――――――――――――――――――――――\ntrain = pd.read_csv(f\"{DATA_DIR}/train.csv\")\ntest  = pd.read_csv(f\"{DATA_DIR}/test.csv\")\n\nhist = pd.read_csv(\n    f\"{DATA_DIR}/historical_transactions.csv\",\n    parse_dates=[\"purchase_date\"],\n    low_memory=True\n)\nnew_trans = pd.read_csv(\n    f\"{DATA_DIR}/new_merchant_transactions.csv\",\n    parse_dates=[\"purchase_date\"],\n    low_memory=True\n)\nmerch = pd.read_csv(f\"{DATA_DIR}/merchants.csv\", low_memory=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#―――――――――――――――――――――――\n# 2. 主テーブル前処理\n#―――――――――――――――――――――――\ntrain_y      = train[\"target\"]\ntrain_x      = train.drop(columns=[\"target\", \"first_active_month\"])\ntest_x       = test .drop(columns=[\"first_active_month\"])\ntest_card_id = test[\"card_id\"]\n\n# first_active_month → LabelEncoding\nle = LabelEncoder()\nall_fam = pd.concat([train[\"first_active_month\"], test[\"first_active_month\"]]).fillna(\"Unknown\")\nle.fit(all_fam)\ntrain_x[\"fam_label\"] = le.transform(train[\"first_active_month\"].fillna(\"Unknown\"))\ntest_x [\"fam_label\"] = le.transform(test [\"first_active_month\"].fillna(\"Unknown\"))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#―――――――――――――――――――――――\n# 3. 全サブテーブルのカテゴリ変数を一括変換\n#―――――――――――――――――――――――\ndef code_columns(df, cols):\n    for c in cols:\n        if df[c].dtype == \"object\":\n            df[c] = df[c].astype(\"category\").cat.codes\n    return df\n\n# ヒストリカル＆ニューは同じ列名が多いのでまとめてコード化\nhist = code_columns(hist, [\"authorized_flag\",\"category_1\",\"category_2\",\"category_3\"])\nnew_trans = code_columns(new_trans, [\"authorized_flag\",\"category_1\",\"category_2\",\"category_3\"])\nmerch = code_columns(merch, merch.select_dtypes(\"object\").columns)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#―――――――――――――――――――――――\n# 4. groupby＋agg の重複解消ヘルパー\n#―――――――――――――――――――――――\ndef aggregate_features(df, group_key, agg_dict, prefix):\n    \"\"\"df: 入力DataFrame\n       group_key: 集約キー列名 (例：\"card_id\")\n       agg_dict: {カラム名: [func1,func2,...], ...}\n       prefix: 出力特徴の接頭辞 (例：\"hist\" or \"new\")\"\"\"\n    agg = df.groupby(group_key).agg(agg_dict)\n    agg.columns = [f\"{prefix}_{col}_{fn if isinstance(fn,str) else fn.__name__}\"\n                   for col,fn in agg.columns.to_flat_index()]\n    agg.reset_index(inplace=True)\n    return agg","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#―――――――――――――――――――――――\n# 5. 各サブテーブルの集約パターン設定\n#―――――――――――――――――――――――\ncommon_agg = {\n    \"purchase_amount\": [\"min\",\"max\",\"mean\",\"var\"],\n    \"installments\":    [\"min\",\"max\",\"mean\"],\n    \"month_lag\":       [\"min\",\"max\",\"mean\"],\n    \"authorized_flag\": [\"mean\"],\n    \"category_1\":      [\"mean\"],\n    \"category_2\":      [\"mean\",\"nunique\"],\n    \"category_3\":      [\"nunique\"],\n    \"merchant_id\":     [\"nunique\"],\n    \"city_id\":         [\"nunique\"],\n    \"state_id\":        [\"nunique\"],\n    \"subsector_id\":    [\"nunique\"],\n    \"purchase_date\":   [\"count\"]\n}\n\nnew_feat  = aggregate_features(new_trans, \"card_id\", common_agg, \"new\")\nhist_feat = aggregate_features(hist,      \"card_id\", common_agg, \"hist\")\n\n# merchants だけは別パターン\nmerch_agg = {\n    \"numerical_1\":       [\"mean\",\"var\"],\n    \"numerical_2\":       [\"mean\",\"var\"],\n    \"avg_sales_lag3\":    [\"mean\",\"max\"],\n    \"avg_purchases_lag3\":[\"mean\",\"max\"],\n    \"category_1\":        [\"nunique\"],\n    \"city_id\":           [\"nunique\"],\n    \"state_id\":          [\"nunique\"]\n}\nmerch_feat = aggregate_features(merch, \"merchant_id\", merch_agg, \"merch\")\n# ─── merchant_id の型を文字列に揃える ───\nhist[\"merchant_id\"]        = hist[\"merchant_id\"].astype(str)\nmerch_feat[\"merchant_id\"]  = merch_feat[\"merchant_id\"].astype(str)\n# ────────────────────────────────────────\n\n# その上でマージ\nhist_merch = hist[[\"card_id\",\"merchant_id\"]].merge(\n    merch_feat,\n    on=\"merchant_id\",\n    how=\"left\"\n)\n# merchant_id 列を落としてから平均を取る\nmc_feat = (\n    hist_merch\n    .drop(columns=[\"merchant_id\"])\n    .groupby(\"card_id\", as_index=False)\n    .mean()\n)\n\n# merchant_id→card_id 再集約\nhist_merch = hist[[\"card_id\",\"merchant_id\"]].merge(\n    merch_feat,\n    on=\"merchant_id\",\n    how=\"left\"\n)\n\nmc_feat = (\n    hist_merch\n    .drop(columns=[\"merchant_id\"])\n    .groupby(\"card_id\", as_index=False)\n    .mean()\n)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#―――――――――――――――――――――――\n# 6. 主テーブルへマージ＆学習準備\n#―――――――――――――――――――――――――\nfor feat in (new_feat, hist_feat, mc_feat):\n    train_x = train_x.merge(feat, on=\"card_id\", how=\"left\")\n    test_x  = test_x .merge(feat, on=\"card_id\", how=\"left\")\n\ntrain_x.fillna(-1, inplace=True)\ntest_x .fillna(-1, inplace=True)\n\ntrain_x.drop(columns=[\"card_id\"], inplace=True)\ntest_x .drop(columns=[\"card_id\"], inplace=True)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#―――――――――――――――――――――――\n# 7. モデル学習・予測・出力\n#―――――――――――――――――――――――\nmodel = XGBRegressor(random_state=rseed)\nmodel.fit(train_x, train_y)\npreds = model.predict(test_x)\n\npd.DataFrame({\"card_id\": test_card_id, \"target\": preds})\\\n  .to_csv(\"submission.csv\", index=False)\n\nprint(\"最終特徴量数:\", train_x.shape[1])","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}