{"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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:04:50.853625Z","iopub.execute_input":"2025-06-30T12:04:50.854739Z","iopub.status.idle":"2025-06-30T12:04:50.859892Z","shell.execute_reply.started":"2025-06-30T12:04:50.854707Z","shell.execute_reply":"2025-06-30T12:04:50.858789Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------------------------\n# データ読み込み\n# ----------------------------------------------\nDATA_DIR = \"/kaggle/input/elo-merchant-category-recommendation\"\ntrain = pd.read_csv(f\"{DATA_DIR}/train.csv\")\ntest  = pd.read_csv(f\"{DATA_DIR}/test.csv\")\n\n# 元テーブルからID列と目的変数をコピー\ntrain_card_id = train['card_id'].copy()\ntest_card_id  = test['card_id'].copy()\ntrain_y       = train['target'].copy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:04:53.600713Z","iopub.execute_input":"2025-06-30T12:04:53.601031Z","iopub.status.idle":"2025-06-30T12:04:53.971885Z","shell.execute_reply.started":"2025-06-30T12:04:53.601008Z","shell.execute_reply":"2025-06-30T12:04:53.970858Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------------------------\n# メインテーブルの特徴量作成\n# ・年月文字列は欠損補完・Label Encoding\n# ・他はそのまま\n# ----------------------------------------------\n\n# 元テーブルから特徴量をコピー（目的変数は削除）\ntrain_x = train.copy().drop(columns=[\"target\"])\ntest_x  = test.copy()\n\n# 年月文字列に欠損があるので補完\ntrain_x[\"first_active_month\"] = train_x[\"first_active_month\"].fillna(\"Unknown\")\ntest_x[\"first_active_month\"]  = test_x[\"first_active_month\"].fillna(\"Unknown\")\n\n# 年月文字列をLabel Encoding\nle = LabelEncoder()\nall_months = pd.concat([train_x[\"first_active_month\"], test_x[\"first_active_month\"]])\nle.fit(all_months)\ntrain_x[\"fam_label\"] = le.transform(train_x[\"first_active_month\"])\ntest_x[\"fam_label\"]  = le.transform(test_x[\"first_active_month\"])\n\n# 変換前の年月文字列を削除\ntrain_x = train_x.drop(columns=[\"first_active_month\"])\ntest_x  = test_x.drop(columns=[\"first_active_month\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:04:56.589790Z","iopub.execute_input":"2025-06-30T12:04:56.590079Z","iopub.status.idle":"2025-06-30T12:04:56.719838Z","shell.execute_reply.started":"2025-06-30T12:04:56.590061Z","shell.execute_reply":"2025-06-30T12:04:56.719045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------------------------\n# 追加テーブルの特徴量作成\n# ----------------------------------------------\n\n# 追加テーブルの読み込み\nnew_trans = pd.read_csv(f\"{DATA_DIR}/new_merchant_transactions.csv\")\nprint(new_trans.describe())\n\n# 集約特徴の作成パターンを辞書化\nagg_funcs = {\n    \"purchase_amount\": [\n        \"min\", \"max\", \"mean\", \"var\", \n        lambda x: x.skew(), lambda x: x.kurt(),\n        lambda x: x.quantile(0.25), \n        lambda x: x.quantile(0.75),\n        lambda x: x.max() - x.min()\n    ],\n    \"installments\": [\n        \"min\", \"max\", \"mean\", \"var\", \"nunique\", \n        lambda x: x.max() - x.min()\n    ],\n    \"month_lag\": [\n        \"min\", \"max\", \"mean\", \"nunique\", \n        lambda x: x.max() - x.min()\n    ],\n    \"purchase_date\": [\n        \"count\", \"nunique\", \"first\", \"last\"\n    ],\n    \"city_id\": [\"nunique\"],\n    \"category_1\": [\"nunique\"]\n}\n\n# 例）\"month_lag\": [\"min\", \"mean\", \"max\"],\n# 　　⇔　変数 \"month_lag\" について，同一IDの中で\n# 　　　　最小値，平均値，最大値を取って集約特徴に追加する\n\n# 全ての行を card_id の値でグループ化\n# ⇒　上の集約パターンに従って実際に特徴を集約\nagg_new = new_trans.groupby(\"card_id\").agg(agg_funcs)\n\n# 備考）上の集約特徴のテーブルは，列が MultiIndex の状態になっている\n# 　　　⇒　列名のリストを表示して確認してみよう\nprint(agg_new.columns.to_list())\n# 1列を指定するために「変数名と集約関数」をペアで指定する必要があることがわかる\n\n# 新特徴の名前（列名）を機械的に生成してリスト化\n# 　　命名規則：元変数名_集約方法（「purchase_amount_mean」など）\nflat_cols = []\nfor col_name, func in agg_new.columns:\n    # ラムダ関数などを文字列化\n    func_name = func.__name__ if hasattr(func, '__name__') else str(func)\n    func_name = func_name.replace(\"<lambda>\", \"lambda\")\n    new_name = f\"new_{col_name}_{func_name}\"\n    flat_cols.append(new_name)\n\n# MultiIndex状態の列を通常の状態に変換（列方向に平坦化）\nagg_new.columns = flat_cols\nprint(agg_new.columns.to_list())\n\n# groupby によるグループ化を解除（card_idを通常の列に戻す）\nagg_new = agg_new.reset_index()\n\n# メインテーブルの特徴と結合\ntrain_x = train_x.merge(agg_new, on=\"card_id\", how=\"left\")\ntest_x  = test_x.merge(agg_new, on=\"card_id\", how=\"left\")\n\n# Fill missing values created by the left join\ntrain_x = train_x.fillna(-1)\ntest_x  = test_x.fillna(-1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:05:00.317221Z","iopub.execute_input":"2025-06-30T12:05:00.317625Z","iopub.status.idle":"2025-06-30T12:10:42.537777Z","shell.execute_reply.started":"2025-06-30T12:05:00.317597Z","shell.execute_reply":"2025-06-30T12:10:42.536722Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# ----------------------------------------------\n# 過去取引データ（historical_transactions）による特徴量の追加\n# ----------------------------------------------\n\nhist_trans = pd.read_csv(f\"{DATA_DIR}/historical_transactions.csv\")\n\nagg_funcs_hist = {\n    \"purchase_amount\": [\n        \"min\", \"max\", \"mean\", \"var\",\n        lambda x: x.skew(),\n        lambda x: x.kurt(),\n        lambda x: x.quantile(0.25),\n        lambda x: x.quantile(0.75),\n        lambda x: x.max() - x.min()\n    ],\n    \"installments\": [\n        \"min\", \"max\", \"mean\", \"var\", \"nunique\",\n        lambda x: x.max() - x.min()\n    ],\n    \"month_lag\": [\n        \"min\", \"max\", \"mean\", \"nunique\",\n        lambda x: x.max() - x.min()\n    ],\n    \"purchase_date\": [\n        \"count\", \"nunique\",\n        lambda x: x.min(),  # first の代用\n        lambda x: x.max()   # last の代用\n    ],\n    \"city_id\": [\"nunique\"],\n    \"category_1\": [\"nunique\"]\n}\n\n\nagg_hist = hist_trans.groupby(\"card_id\").agg(agg_funcs_hist)\n\nflat_cols_hist = []\nfor col_name, func in agg_hist.columns:\n    func_name = func.__name__ if hasattr(func, '__name__') else str(func)\n    func_name = func_name.replace(\"<lambda>\", \"lambda\")\n    new_name = f\"hist_{col_name}_{func_name}\"\n    flat_cols_hist.append(new_name)\nagg_hist.columns = flat_cols_hist\nagg_hist = agg_hist.reset_index()\n\ntrain_x = train_x.merge(agg_hist, on=\"card_id\", how=\"left\")\ntest_x  = test_x.merge(agg_hist, on=\"card_id\", how=\"left\")\n\ntrain_x = train_x.fillna(-1)\ntest_x = test_x.fillna(-1)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:13:09.823871Z","iopub.execute_input":"2025-06-30T12:13:09.824840Z","iopub.status.idle":"2025-06-30T12:22:49.392642Z","shell.execute_reply.started":"2025-06-30T12:13:09.824810Z","shell.execute_reply":"2025-06-30T12:22:49.391830Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ID列を削除\ntrain_x = train_x.drop(columns=[\"card_id\"])\ntest_x  = test_x.drop(columns=[\"card_id\"])\n\nobject_cols = train_x.select_dtypes(include=\"object\").columns\nprint(\"削除する object 型の列：\", object_cols.tolist())\ntrain_x = train_x.drop(columns=object_cols)\ntest_x = test_x.drop(columns=object_cols)\n\ndef clean_column_names(df):\n    new_cols = []\n    for col in df.columns:\n        col = col.replace('<', '_').replace('>', '_')\n        col = col.replace('[', '_').replace(']', '_')\n        col = col.replace('(', '_').replace(')', '_')\n        col = col.replace(' ', '_').replace(',', '_')\n        new_cols.append(col)\n    df.columns = new_cols\n    return df\n\ntrain_x = clean_column_names(train_x)\ntest_x = clean_column_names(test_x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:23:07.783797Z","iopub.execute_input":"2025-06-30T12:23:07.784130Z","iopub.status.idle":"2025-06-30T12:23:08.566312Z","shell.execute_reply.started":"2025-06-30T12:23:07.784107Z","shell.execute_reply":"2025-06-30T12:23:08.565368Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------------------------\n# 使用する特徴量の可視化\n# ----------------------------------------------\n\n# 各特徴の統計量\nprint(pd.concat([train_x, train_y], axis=1).describe())\n\n# 各特徴のヒストグラム\ntrain_x.hist(bins=100, color=\"blue\", grid=True, label='pandas')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:23:14.171464Z","iopub.execute_input":"2025-06-30T12:23:14.171876Z","iopub.status.idle":"2025-06-30T12:23:25.933084Z","shell.execute_reply.started":"2025-06-30T12:23:14.171849Z","shell.execute_reply":"2025-06-30T12:23:25.932080Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ------------------------------\n# XGBoostの学習・推論・submit\n# ------------------------------\nmodel = XGBRegressor(random_state=rseed)\nmodel.fit(train_x, train_y)\npreds = model.predict(test_x)\n\nsubmission = pd.DataFrame({\n    \"card_id\": test_card_id,\n    \"target\":  preds\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T12:23:31.908861Z","iopub.execute_input":"2025-06-30T12:23:31.909174Z","iopub.status.idle":"2025-06-30T12:23:34.758092Z","shell.execute_reply.started":"2025-06-30T12:23:31.909152Z","shell.execute_reply":"2025-06-30T12:23:34.757051Z"}},"outputs":[],"execution_count":null}]}