{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","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":16559,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### 介绍\n\n![Elo](https://www.cloudera.com/content/dam/www/marketing/images/logos/customers/cartao-elo.png)\n\n[Elo](https://www.cartaoelo.com.br/), Elo 是巴西最大的支付品牌之一，已与商户建立合作关系，为持卡人提供促销或折扣活动。本次竞赛的目标是通过挖掘客户忠诚度中的信号，为个人识别并提供最相关的机会。\n\n### 数据分析文档","metadata":{"_uuid":"3b2cd3d87f335dc35d537ca0f31530563ab56725"}},{"cell_type":"code","source":"# 重新加载数据（确保路径正确）\ntrain_df = pd.read_csv(\"../input/train.csv\", parse_dates=[\"first_active_month\"])\ntest_df = pd.read_csv(\"../input/test.csv\", parse_dates=[\"first_active_month\"])\n\nprint(\"数据加载验证:\")\nprint(\"训练集形状:\", train_df.shape)\nprint(\"测试集形状:\", test_df.shape)\nprint(\"训练集列名:\", list(train_df.columns))\n\n# 检查关键列是否存在\nassert 'target' in train_df.columns, \"target列不存在！\"\nassert 'first_active_month' in train_df.columns, \"first_active_month列不存在！\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-25T07:55:54.966588Z","iopub.execute_input":"2025-11-25T07:55:54.966972Z","iopub.status.idle":"2025-11-25T07:55:55.451042Z","shell.execute_reply.started":"2025-11-25T07:55:54.966905Z","shell.execute_reply":"2025-11-25T07:55:55.450200Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport numpy as np\nimport pandas as pd\nfrom pandas.io.json import json_normalize\nimport matplotlib.pyplot as plt\nimport seaborn as sns\ncolor = sns.color_palette()\n\n%matplotlib inline\n\nfrom plotly import tools\nimport plotly.offline as py\npy.init_notebook_mode(connected=True)\nimport plotly.graph_objs as go\n\nfrom sklearn import model_selection, preprocessing, metrics\nimport lightgbm as lgb\n\npd.options.mode.chained_assignment = None\npd.options.display.max_columns = 999\n\n\nimport plotly.offline as py\nimport plotly.graph_objs as go\n\npy.init_notebook_mode(connected=True)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-11-25T07:46:55.842134Z","iopub.execute_input":"2025-11-25T07:46:55.842451Z","iopub.status.idle":"2025-11-25T07:46:55.853656Z","shell.execute_reply.started":"2025-11-25T07:46:55.842393Z","shell.execute_reply":"2025-11-25T07:46:55.852894Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 数据探索","metadata":{"_uuid":"b80cd7ae160231cb08f85b75a8e1743ed5198d98"}},{"cell_type":"code","source":"!ls ../input/","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"execution":{"iopub.status.busy":"2025-11-25T07:47:00.851747Z","iopub.execute_input":"2025-11-25T07:47:00.852109Z","iopub.status.idle":"2025-11-25T07:47:02.003114Z","shell.execute_reply.started":"2025-11-25T07:47:00.852037Z","shell.execute_reply":"2025-11-25T07:47:02.002152Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"train.csv - 训练集\ntest.csv - 测试集\nsample_submission.csv - 格式正确的提交示例文件，包含所有需预测的卡片 ID\nhistorical_transactions.csv - 每张卡片 ID 最长 3 个月的历史交易记录\nmerchants.csv - 数据集中所有商家 / 商家 ID 的补充信息\nnew_merchant_transactions.csv - 每张卡片 ID 两个月的数据，包含该卡片在历史数据中未访问过的商家 ID 处的所有消费记录","metadata":{"_uuid":"7332e3e20838e5097c0e56e2f5697198372dd4ec"}},{"cell_type":"code","source":"train_df = pd.read_csv(\"../input/train.csv\", parse_dates=[\"first_active_month\"])\ntest_df = pd.read_csv(\"../input/test.csv\", parse_dates=[\"first_active_month\"])\nprint(\"Number of rows and columns in train set : \",train_df.shape)\nprint(\"Number of rows and columns in test set : \",test_df.shape)\n\ntrain_df[\"active_duration\"] = (pd.to_datetime(\"2018-01-01\") - train_df[\"first_active_month\"]).dt.days\ntest_df[\"active_duration\"] = (pd.to_datetime(\"2018-01-01\") - test_df[\"first_active_month\"]).dt.days","metadata":{"trusted":true,"_uuid":"bfdab255f309f4ca9239eaa07f17398e643010a3","execution":{"iopub.status.busy":"2025-11-25T08:02:07.807777Z","iopub.execute_input":"2025-11-25T08:02:07.808137Z","iopub.status.idle":"2025-11-25T08:02:08.293909Z","shell.execute_reply.started":"2025-11-25T08:02:07.808070Z","shell.execute_reply":"2025-11-25T08:02:08.292742Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.head()","metadata":{"trusted":true,"_uuid":"a00bb53642516cefacf7159a02e84f0ce26a5d58","execution":{"iopub.status.busy":"2025-11-25T07:47:11.571534Z","iopub.execute_input":"2025-11-25T07:47:11.571857Z","iopub.status.idle":"2025-11-25T07:47:11.592602Z","shell.execute_reply.started":"2025-11-25T07:47:11.571782Z","shell.execute_reply":"2025-11-25T07:47:11.591966Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"目标列探索","metadata":{"_uuid":"dcc76ca4c430f5c90b4089ebe0dbf55ada020d2b"}},{"cell_type":"code","source":"target_col = \"target\"\n\nplt.figure(figsize=(8,6))\nplt.scatter(range(train_df.shape[0]), np.sort(train_df[target_col].values))\nplt.xlabel('index', fontsize=12)\nplt.ylabel('Loyalty Score', fontsize=12)\nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"584ac32fd52b6d74472779681a671cc66afa67c6","execution":{"iopub.status.busy":"2025-11-25T08:02:10.805172Z","iopub.execute_input":"2025-11-25T08:02:10.805499Z","iopub.status.idle":"2025-11-25T08:02:12.741195Z","shell.execute_reply.started":"2025-11-25T08:02:10.805421Z","shell.execute_reply":"2025-11-25T08:02:12.739708Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12,8))\nsns.distplot(train_df[target_col].values, bins=50, kde=False, color=\"red\")\nplt.title(\"Histogram of Loyalty score\")\nplt.xlabel('Loyalty score', fontsize=12)\nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"93b6dd9e83e3d762a7dd0ac80efedd94a98defb8","execution":{"iopub.status.busy":"2025-11-25T08:02:15.794496Z","iopub.execute_input":"2025-11-25T08:02:15.794777Z","iopub.status.idle":"2025-11-25T08:02:16.199073Z","shell.execute_reply.started":"2025-11-25T08:02:15.794733Z","shell.execute_reply":"2025-11-25T08:02:16.198318Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"部分忠诚度数值与其他数值差异显著（小于 - 30），下面统计这类数值的数量。\n","metadata":{"_uuid":"3ac53af6e71346176abafc19666c18d5ee512840"}},{"cell_type":"code","source":"(train_df[target_col]<-30).sum()","metadata":{"trusted":true,"_uuid":"9a6b77591f0cd86505e6bcaac84240af09143011","execution":{"iopub.status.busy":"2025-11-25T08:02:21.475412Z","iopub.execute_input":"2025-11-25T08:02:21.475754Z","iopub.status.idle":"2025-11-25T08:02:21.485274Z","shell.execute_reply.started":"2025-11-25T08:02:21.475691Z","shell.execute_reply":"2025-11-25T08:02:21.484463Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"大约 2207 行数据（约占总数据的 1%），这些行的数值与其他行存在差异。鉴于 RMSE 指标的特性，这些行可能会起到重要作用，因此需留意它们。","metadata":{"_uuid":"0132162b004ec52f5f42c5080886fd932f8aea97"}},{"cell_type":"markdown","source":"查看卡片首次激活月份在训练集和测试集之间是否存在分布差异。","metadata":{"_uuid":"f0077823b76b67ff16106bdce2bc03c30db16543"}},{"cell_type":"code","source":"cnt_srs = train_df['first_active_month'].dt.date.value_counts()\ncnt_srs = cnt_srs.sort_index()\nplt.figure(figsize=(14,6))\nsns.barplot(cnt_srs.index, cnt_srs.values, alpha=0.8, color='green')\nplt.xticks(rotation='vertical')\nplt.xlabel('First active month', fontsize=12)\nplt.ylabel('Number of cards', fontsize=12)\nplt.title(\"First active month count in train set\")\nplt.show()\n\ncnt_srs = test_df['first_active_month'].dt.date.value_counts()\ncnt_srs = cnt_srs.sort_index()\nplt.figure(figsize=(14,6))\nsns.barplot(cnt_srs.index, cnt_srs.values, alpha=0.8, color='green')\nplt.xticks(rotation='vertical')\nplt.xlabel('First active month', fontsize=12)\nplt.ylabel('Number of cards', fontsize=12)\nplt.title(\"First active month count in test set\")\nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"e1bad37ff53af54a0e42f3ab2512e9b7e5615f00","execution":{"iopub.status.busy":"2025-11-25T08:02:23.665838Z","iopub.execute_input":"2025-11-25T08:02:23.666185Z","iopub.status.idle":"2025-11-25T08:02:26.003989Z","shell.execute_reply.started":"2025-11-25T08:02:23.666134Z","shell.execute_reply":"2025-11-25T08:02:26.003005Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"训练集和测试集的分布看起来较为相似，无需进行基于时间的划分。","metadata":{"_uuid":"98e7987dcb9628e93107fe4ad9fe8f6e6e054bf4"}},{"cell_type":"markdown","source":"查看训练数据集中的其他变量在预测忠诚度得分方面是否具备良好的预测能力。","metadata":{"_uuid":"a2da071d99e4006ba77b8669e5a1a21acc557906"}},{"cell_type":"code","source":"# Feature 1\nplt.figure(figsize=(8,4))\nsns.violinplot(x=\"feature_1\", y=target_col, data=train_df)\nplt.xticks(rotation='vertical')\nplt.xlabel('Feature 1', fontsize=12)\nplt.ylabel('Loyalty score', fontsize=12)\nplt.title(\"Feature 1 distribution\")\nplt.show()\n\n# Feature 2\nplt.figure(figsize=(8,4))\nsns.violinplot(x=\"feature_2\", y=target_col, data=train_df)\nplt.xticks(rotation='vertical')\nplt.xlabel('Feature 2', fontsize=12)\nplt.ylabel('Loyalty score', fontsize=12)\nplt.title(\"Feature 2 distribution\")\nplt.show()\n\n# Feature 3\nplt.figure(figsize=(8,4))\nsns.violinplot(x=\"feature_3\", y=target_col, data=train_df)\nplt.xticks(rotation='vertical')\nplt.xlabel('Feature 3', fontsize=12)\nplt.ylabel('Loyalty score', fontsize=12)\nplt.title(\"Feature 3 distribution\")\nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"bfca0eaf96632146c7a0f725ce2075ff1405b6f0","execution":{"iopub.status.busy":"2025-11-25T08:02:28.725540Z","iopub.execute_input":"2025-11-25T08:02:28.725828Z","iopub.status.idle":"2025-11-25T08:02:33.586229Z","shell.execute_reply.started":"2025-11-25T08:02:28.725784Z","shell.execute_reply":"2025-11-25T08:02:33.585056Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"肉眼可见，这三个特征中不同类别的分布看起来较为相似。或许模型能从中发现一些规律。","metadata":{"_uuid":"80b2a72f84348e2008d2d948a23239917f676461"}},{"cell_type":"markdown","source":"历史交易记录","metadata":{"_uuid":"f829143f2ca82af92aa793c7cee8effcc5fb1d8e"}},{"cell_type":"code","source":"hist_df = pd.read_csv(\"../input/historical_transactions.csv\")\nhist_df.head()\n\nfrom sklearn import preprocessing\nfor col in [\"category_1\", \"category_2\", \"category_3\"]:\n    lbl = preprocessing.LabelEncoder()\n    # 先填充缺失值为字符串\"-1\"，再统一转为字符串类型\n    hist_df[col] = hist_df[col].fillna(\"-1\").astype(str)  # 把所有值转为字符串，包括原来的数字\n    lbl.fit(hist_df[col])  # 现在都是字符串，可正常排序和编码\n    hist_df[col] = lbl.transform(hist_df[col])\n\n# 检查每个类别列的数据类型和唯一值\nfor col in [\"category_1\", \"category_2\", \"category_3\"]:\n    print(f\"列 {col} 数据类型：\", hist_df[col].dtype)\n    print(f\"列 {col} 唯一值：\", hist_df[col].unique())\n    print(\"-\" * 20)","metadata":{"trusted":true,"_uuid":"ebb27ee19be1dc40b94731d4dcd0ce670aacc86d","execution":{"iopub.status.busy":"2025-11-25T08:02:37.175591Z","iopub.execute_input":"2025-11-25T08:02:37.175911Z","iopub.status.idle":"2025-11-25T08:04:28.849033Z","shell.execute_reply.started":"2025-11-25T08:02:37.175864Z","shell.execute_reply":"2025-11-25T08:04:28.847958Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"字段说明如下：\ncard_id - 卡片标识符\nmonth_lag - 相对于参考日期的月份滞后值\npurchase_date - 购买日期\nauthorized_flag - 批准为 'Y'，拒绝为 'N'\ncategory_3 - 匿名分类\ninstallments - 购买的分期付款期数\ncategory_1 - 匿名分类\nmerchant_category_id - 商家类别标识符（已匿名）\nsubsector_id - 商家类别组标识符（已匿名）\nmerchant_id - 商家标识符（已匿名）\npurchase_amount - 标准化购买金额\ncity_id - 城市标识符（已匿名）\nstate_id - 州 / 省份标识符（已匿名）\ncategory_2 - 匿名分类","metadata":{"_uuid":"9603854979b42cd70d2d1f7fa714b63dc8c1e933"}},{"cell_type":"code","source":"# 序号13：历史交易数据多维度聚合（替换原单一交易次数统计）\nagg_funcs = {\n    # 交易金额相关统计量\n    \"purchase_amount\": [\"sum\", \"mean\", \"std\", \"min\", \"max\", \"count\"],  # count就是原交易次数\n    # 分期付款相关统计量\n    \"installments\": [\"sum\", \"mean\", \"std\", \"min\", \"max\"],\n    # 已编码的类别特征统计量（新增，捕捉类别分布）\n    \"category_1\": [\"mean\", \"count\"],  # 类别1的均值（可理解为某类别占比）、非空数量\n    \"category_2\": [\"mean\", \"nunique\"],  # 类别2的均值、不同类别数量\n    \"category_3\": [\"mean\", \"nunique\"]   # 类别3的均值、不同类别数量\n}\n\n# 按card_id分组聚合，计算每个用户的统计特征\nhist_agg = hist_df.groupby(\"card_id\").agg(agg_funcs).reset_index()\n\n# 重命名列名（避免多级列名，让特征名更清晰）\nhist_agg.columns = [\"card_id\"] + [f\"hist_{col[0]}_{col[1]}\" for col in hist_agg.columns[1:]]\n\n# 合并聚合特征到训练集和测试集（左连接，保留所有用户）\ntrain_df = pd.merge(train_df, hist_agg, on=\"card_id\", how=\"left\")\ntest_df = pd.merge(test_df, hist_agg, on=\"card_id\", how=\"left\")\n\n# 可选：填充可能的缺失值（无历史交易的用户，特征值设为0）\ntrain_df = train_df.fillna(0)\ntest_df = test_df.fillna(0)\n\n# 验证合并结果\nprint(\"训练集合并后列数：\", train_df.shape[1])\nprint(\"新增的历史交易特征：\", [col for col in train_df.columns if col.startswith(\"hist_\")])","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"756c8ef8d3756fb21365e0a66540b7eb92a0b5d3","execution":{"iopub.status.busy":"2025-11-25T08:05:06.156275Z","iopub.execute_input":"2025-11-25T08:05:06.156558Z","iopub.status.idle":"2025-11-25T08:05:41.902502Z","shell.execute_reply.started":"2025-11-25T08:05:06.156509Z","shell.execute_reply":"2025-11-25T08:05:41.901692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\nplt.rcParams['font.sans-serif'] = ['DejaVu Sans']  # Kaggle默认支持的英文黑体\nplt.rcParams['axes.unicode_minus'] = False  # 正常显示负号\n\n# 1. 确保历史交易次数特征存在（兜底处理）\nif \"hist_purchase_amount_count\" not in train_df.columns:\n    print(\"特征缺失，临时生成历史交易次数...\")\n    hist_count = train_df.groupby(\"card_id\").size().reset_index(name=\"hist_purchase_amount_count\")\n    train_df = pd.merge(train_df, hist_count, on=\"card_id\", how=\"left\")\n\n# 2. 按交易次数分组，计算平均忠诚度评分\ncnt_srs = train_df.groupby(\"hist_purchase_amount_count\")[\"target\"].mean().sort_index()\n\n# 3. 筛选数据（避免极端值，用简单切片，适配旧版本Pandas）\n# 只保留交易次数<=200的数据（避免少数极端值让图表变形）\ncnt_srs = cnt_srs[cnt_srs.index <= 200]\n\n# 4. 用Matplotlib绘制散点图\nplt.figure(figsize=(10, 6)) \nplt.scatter(\n    cnt_srs.index,  # x轴：交易次数\n    cnt_srs.values, # y轴：平均忠诚度评分\n    color='orange', \n    s=50,  \n    alpha=0.7  \n)\n\nplt.title('Loyalty Score by Number of Historical Transactions', fontsize=14, pad=20)\nplt.xlabel('Number of Historical Transactions', fontsize=12)\nplt.ylabel('Average Loyalty Score', fontsize=12)\nplt.grid(True, alpha=0.3)  \nplt.tight_layout()  \n\nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"2778542c23b6484319e7818c120a30adc07a4ccb","execution":{"iopub.status.busy":"2025-11-25T08:05:50.186384Z","iopub.execute_input":"2025-11-25T08:05:50.186654Z","iopub.status.idle":"2025-11-25T08:05:50.595313Z","shell.execute_reply.started":"2025-11-25T08:05:50.186603Z","shell.execute_reply":"2025-11-25T08:05:50.594478Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"现在我们对历史交易次数进行分箱处理，然后绘制箱线图以便更清晰地查看数据分布。","metadata":{"_uuid":"41fe55f2834824cf44578cec42c81e840e4ba64f"}},{"cell_type":"code","source":"bins = [0, 10, 20, 30, 40, 50, 75, 100, 150, 200, 500, 10000]\ntrain_df['binned_num_hist_transactions'] = pd.cut(train_df['hist_purchase_amount_count'], bins)\ncnt_srs = train_df.groupby(\"binned_num_hist_transactions\")[target_col].mean()\n\nplt.figure(figsize=(12,8))\nsns.boxplot(x=\"binned_num_hist_transactions\", y=target_col, data=train_df, showfliers=False)\nplt.xticks(rotation='vertical')\nplt.xlabel('binned_num_hist_transactions', fontsize=12)\nplt.ylabel('Loyalty score', fontsize=12)\nplt.title(\"binned_num_hist_transactions distribution\")\nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"scrolled":true,"_uuid":"068b1339422f063cb0daaf7649bd96c4eba00fb9","execution":{"iopub.status.busy":"2025-11-25T08:06:03.106496Z","iopub.execute_input":"2025-11-25T08:06:03.106792Z","iopub.status.idle":"2025-11-25T08:06:03.468634Z","shell.execute_reply.started":"2025-11-25T08:06:03.106736Z","shell.execute_reply":"2025-11-25T08:06:03.467798Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"查看各卡片的历史交易金额，并基于该金额分析忠诚度得分的分布情况。","metadata":{"_uuid":"172321f1785060cc9aab7e8d8aeab884f6aea9fd"}},{"cell_type":"code","source":"gdf = hist_df.groupby(\"card_id\")\ngdf = gdf[\"purchase_amount\"].agg(['sum', 'mean', 'std', 'min', 'max']).reset_index()\ngdf.columns = [\"card_id\", \"sum_hist_trans\", \"mean_hist_trans\", \"std_hist_trans\", \"min_hist_trans\", \"max_hist_trans\"]\ntrain_df = pd.merge(train_df, gdf, on=\"card_id\", how=\"left\")\ntest_df = pd.merge(test_df, gdf, on=\"card_id\", how=\"left\")","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"216d9ff997e3cafaf1be538f5a7227eabc6fa9af","execution":{"iopub.status.busy":"2025-11-25T08:06:09.905814Z","iopub.execute_input":"2025-11-25T08:06:09.906159Z","iopub.status.idle":"2025-11-25T08:06:14.609286Z","shell.execute_reply.started":"2025-11-25T08:06:09.906108Z","shell.execute_reply":"2025-11-25T08:06:14.608242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"bins = np.percentile(train_df[\"hist_purchase_amount_sum\"], range(0,101,10))\ntrain_df['binned_sum_hist_trans'] = pd.cut(train_df['hist_purchase_amount_sum'], bins)\n\nplt.figure(figsize=(12,8))\nsns.boxplot(x=\"binned_sum_hist_trans\", y=target_col, data=train_df, showfliers=False)\nplt.xticks(rotation='vertical')\nplt.xlabel('Binned Sum of Historical Transaction Value', fontsize=12)  \nplt.ylabel('Loyalty Score', fontsize=12)\nplt.title(\"Sum of Historical Transaction Value (Binned) Distribution\", fontsize=14)\nplt.tight_layout() \nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"7da6b4337b8f8f10bab70175b2031561d0fcc111","execution":{"iopub.status.busy":"2025-11-25T08:06:19.476268Z","iopub.execute_input":"2025-11-25T08:06:19.476573Z","iopub.status.idle":"2025-11-25T08:06:20.042976Z","shell.execute_reply.started":"2025-11-25T08:06:19.476513Z","shell.execute_reply":"2025-11-25T08:06:20.041981Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"忠诚度得分随着 “历史交易金额总和” 的增加而上升。","metadata":{"_uuid":"631c654233d8a277047dd4c9c70ef1e3373d2f56"}},{"cell_type":"code","source":"bins = np.percentile(train_df[\"hist_purchase_amount_mean\"], range(0,101,10))\ntrain_df['binned_mean_hist_trans'] = pd.cut(train_df['hist_purchase_amount_mean'], bins)\n\nplt.figure(figsize=(12,8))\nsns.boxplot(x=\"binned_mean_hist_trans\", y=target_col, data=train_df, showfliers=False)\nplt.xticks(rotation='vertical')\nplt.xlabel('Binned Mean Historical Transactions', fontsize=12)\nplt.ylabel('Loyalty score', fontsize=12)\nplt.title(\"Mean of historical transaction value (Binned) distribution\")\nplt.tight_layout() \nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"816c04fd10019149fe1fd6f20e06f2a22a29ff87","execution":{"iopub.status.busy":"2025-11-25T08:06:28.946189Z","iopub.execute_input":"2025-11-25T08:06:28.946475Z","iopub.status.idle":"2025-11-25T08:06:29.340604Z","shell.execute_reply.started":"2025-11-25T08:06:28.946427Z","shell.execute_reply":"2025-11-25T08:06:29.339610Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"查看新商家交易数据并进行相关分析。","metadata":{"_uuid":"80d9a378203a3cfc75a7a189c66ada9478a6d67e"}},{"cell_type":"code","source":"new_trans_df = pd.read_csv(\"../input/new_merchant_transactions.csv\")\nnew_trans_df.head()","metadata":{"trusted":true,"_uuid":"191aed6554c8698d5a0a4b782fe1ed4e5a306a8a","execution":{"iopub.status.busy":"2025-11-25T08:06:33.785359Z","iopub.execute_input":"2025-11-25T08:06:33.785637Z","iopub.status.idle":"2025-11-25T08:06:38.788204Z","shell.execute_reply.started":"2025-11-25T08:06:33.785592Z","shell.execute_reply":"2025-11-25T08:06:38.787138Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"new_trans_df = pd.read_csv(\"../input/new_merchant_transactions.csv\")\nnew_trans_df.head()\n\ngdf = new_trans_df.groupby(\"card_id\")\ngdf = gdf[\"purchase_amount\"].size().reset_index()\ngdf.columns = [\"card_id\", \"new_num_merch_transactions\"]\n# 合并时添加缺失值填充（无新商户交易的用户，交易次数设为0）\ntrain_df = pd.merge(train_df, gdf, on=\"card_id\", how=\"left\").fillna({\"new_num_merch_transactions\": 0})\ntest_df = pd.merge(test_df, gdf, on=\"card_id\", how=\"left\").fillna({\"new_num_merch_transactions\": 0})","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"e2c425d22c6100926bf1ca200271e80a4c86ec34","execution":{"iopub.status.busy":"2025-11-25T08:06:42.135341Z","iopub.execute_input":"2025-11-25T08:06:42.135611Z","iopub.status.idle":"2025-11-25T08:06:48.043714Z","shell.execute_reply.started":"2025-11-25T08:06:42.135567Z","shell.execute_reply":"2025-11-25T08:06:48.042978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"bins = [0, 10, 20, 30, 40, 50, 75, 10000]\ntrain_df['binned_num_merch_transactions'] = pd.cut(train_df['new_num_merch_transactions'], bins)\ncnt_srs = train_df.groupby(\"binned_num_merch_transactions\")[target_col].mean()\n\nplt.figure(figsize=(12,8))\nsns.boxplot(x=\"binned_num_merch_transactions\", y=target_col, data=train_df, showfliers=False)\nplt.xticks(rotation='vertical')\nplt.xlabel('Binned Number of New Merchant Transactions', fontsize=12)  \nplt.ylabel('Loyalty Score', fontsize=12)\nplt.title(\"Number of New Merchant Transactions (Binned) Distribution\", fontsize=14)\nplt.tight_layout() \nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"ac6e86379a359f5156eb1eb36a85cd3cc0ca0a57","execution":{"iopub.status.busy":"2025-11-25T08:06:55.296244Z","iopub.execute_input":"2025-11-25T08:06:55.296517Z","iopub.status.idle":"2025-11-25T08:06:55.789703Z","shell.execute_reply.started":"2025-11-25T08:06:55.296475Z","shell.execute_reply":"2025-11-25T08:06:55.789008Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"除最后一个分箱外，忠诚度得分似乎随着新商家交易次数的增加而下降。","metadata":{"_uuid":"0599ae300ccdb23a5285b35f267837f25421b718"}},{"cell_type":"code","source":"gdf = new_trans_df.groupby(\"card_id\")\ngdf = gdf[\"purchase_amount\"].agg(['sum', 'mean', 'std', 'min', 'max']).reset_index()\ngdf.columns = [\n    \"card_id\", \n    \"new_sum_merch_trans\",    # 新商户交易金额总和\n    \"new_mean_merch_trans\",   # 新商户交易金额均值\n    \"new_std_merch_trans\",    # 新商户交易金额标准差\n    \"new_min_merch_trans\",    # 新商户交易金额最小值\n    \"new_max_merch_trans\"     # 新商户交易金额最大值\n]\ntrain_df = pd.merge(train_df, gdf, on=\"card_id\", how=\"left\").fillna({\n    \"new_sum_merch_trans\": 0,\n    \"new_mean_merch_trans\": 0,\n    \"new_std_merch_trans\": 0,\n    \"new_min_merch_trans\": 0,\n    \"new_max_merch_trans\": 0\n})\ntest_df = pd.merge(test_df, gdf, on=\"card_id\", how=\"left\").fillna({\n    \"new_sum_merch_trans\": 0,\n    \"new_mean_merch_trans\": 0,\n    \"new_std_merch_trans\": 0,\n    \"new_min_merch_trans\": 0,\n    \"new_max_merch_trans\": 0\n})","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"b860890fa58461a623b3706e8f1509b33902c660","execution":{"iopub.status.busy":"2025-11-25T08:06:59.315488Z","iopub.execute_input":"2025-11-25T08:06:59.315762Z","iopub.status.idle":"2025-11-25T08:07:00.951436Z","shell.execute_reply.started":"2025-11-25T08:06:59.315719Z","shell.execute_reply":"2025-11-25T08:07:00.950126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"bins = np.nanpercentile(train_df[\"new_sum_merch_trans\"], range(0,101,10))\ntrain_df['binned_sum_merch_trans'] = pd.cut(train_df['new_sum_merch_trans'], bins)\n\nplt.figure(figsize=(12,8))\nsns.boxplot(x=\"binned_sum_merch_trans\", y=target_col, data=train_df, showfliers=False)\nplt.xticks(rotation='vertical')\nplt.xlabel('Binned Sum of New Merchant Transactions', fontsize=12) \nplt.ylabel('Loyalty Score', fontsize=12)\nplt.title(\"Sum of New Merchant Transaction Value (Binned) Distribution\", fontsize=14)\nplt.tight_layout()  \nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"9b3717a18720aa24f520c113161f2cb227367da9","execution":{"iopub.status.busy":"2025-11-25T08:07:04.195542Z","iopub.execute_input":"2025-11-25T08:07:04.195833Z","iopub.status.idle":"2025-11-25T08:07:04.753912Z","shell.execute_reply.started":"2025-11-25T08:07:04.195784Z","shell.execute_reply":"2025-11-25T08:07:04.753013Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"忠诚度分数会随着新商户交易总额的增加而提高，但最后一组除外。","metadata":{"_uuid":"03292ab9712d0f97da286fcbb2de672b5c0d70c0"}},{"cell_type":"code","source":"bins = np.nanpercentile(train_df[\"new_mean_merch_trans\"], range(0,101,10))\ntrain_df['binned_mean_merch_trans'] = pd.cut(train_df['new_mean_merch_trans'], bins)\n\nplt.figure(figsize=(12,8))\nsns.boxplot(x=\"binned_mean_merch_trans\", y=target_col, data=train_df, showfliers=False)\nplt.xticks(rotation='vertical')\nplt.xlabel('Binned Mean of New Merchant Transactions', fontsize=12)  \nplt.ylabel('Loyalty Score', fontsize=12)\nplt.title(\"Mean of New Merchant Transaction Value (Binned) Distribution\", fontsize=14)\nplt.tight_layout()  \nplt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"3ba9ac6783176fd0e369d659b8a7bd9b2259b2e5","execution":{"iopub.status.busy":"2025-11-25T08:07:08.185480Z","iopub.execute_input":"2025-11-25T08:07:08.185769Z","iopub.status.idle":"2025-11-25T08:07:08.853713Z","shell.execute_reply.started":"2025-11-25T08:07:08.185728Z","shell.execute_reply":"2025-11-25T08:07:08.852707Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 基准模型","metadata":{"_uuid":"555afcb2ab2a11a4f0c667c8e8f18a3bd0c0acf9"}},{"cell_type":"code","source":"import lightgbm as lgb\nfrom sklearn.model_selection import KFold\nimport numpy as np\n\ndef run_lgb(train_df, test_df, target_col, cols_to_use, n_folds=5):\n    # 1. 简化参数（去掉可能冲突的类别特征、复杂正则，回归基础稳定配置）\n    params = {\n        \"objective\": \"regression\",\n        \"metric\": \"rmse\",\n        \"num_leaves\": 31,  \n        \"min_child_samples\": 20,  \n        \"learning_rate\": 0.05,\n        \"bagging_fraction\": 0.7,\n        \"feature_fraction\": 0.7,\n        \"bagging_freq\": 3,\n        \"verbosity\": -1,\n        \"n_estimators\": 1000,  # 适当减少迭代次数，加快训练\n        \"boosting_type\": \"gbdt\",\n        \"device_type\": \"cpu\",\n        \"seed\": 2025,\n        \"early_stopping_round\": 100 \n    }\n    \n    # 2. 初始化变量\n    kf = KFold(n_splits=n_folds, shuffle=True, random_state=2025)\n    pred_test = np.zeros(test_df.shape[0])\n    models = []\n    fold_scores = []\n    \n    # 3. 5折交叉验证（简化逻辑，去掉冗余）\n    for n_fold, (train_idx, val_idx) in enumerate(kf.split(train_df[cols_to_use])):\n        train_X, val_X = train_df[cols_to_use].iloc[train_idx], train_df[cols_to_use].iloc[val_idx]\n        train_y, val_y = train_df[target_col].iloc[train_idx], train_df[target_col].iloc[val_idx]\n        \n        # 构建数据集（不指定reference，避免依赖冲突）\n        train_data = lgb.Dataset(train_X, label=train_y)\n        val_data = lgb.Dataset(val_X, label=val_y)\n        \n        # 训练模型（简化参数传递）\n        model = lgb.train(\n            params,\n            train_data,\n            valid_sets=[val_data],\n            verbose_eval=200,\n            num_boost_round=params[\"n_estimators\"]\n        )\n        \n        models.append(model)\n        fold_scores.append(model.best_score[\"valid_0\"][\"rmse\"])\n        pred_test += model.predict(test_df[cols_to_use], num_iteration=model.best_iteration) / kf.n_splits\n        \n        print(f\"第{n_fold+1}折 验证集RMSE：{fold_scores[-1]:.4f}\")\n    \n    # 4. 输出结果\n    print(\"=\"*50)\n    print(f\"5折平均RMSE：{np.mean(fold_scores):.4f}\")\n    print(f\"RMSE标准差：{np.std(fold_scores):.4f}\")\n    print(\"=\"*50)\n    \n    return pred_test, models\n\n# 定义特征：排除非数值列，避免数据类型冲突\ncols_to_use = [col for col in train_df.columns if col not in [\"card_id\", \"target\", \"first_active_month\"]]\n# 额外过滤：只保留数值型特征（彻底避免类别特征冲突）\ncols_to_use = [col for col in cols_to_use if train_df[col].dtype in [np.float64, np.float32, np.int64, np.int32]]\nprint(\"用于建模的特征数：\", len(cols_to_use))\nprint(\"前10个特征：\", cols_to_use[:10])\n\n# 调用训练\npred_test, models = run_lgb(train_df, test_df, target_col=\"target\", cols_to_use=cols_to_use)","metadata":{"trusted":true,"_uuid":"5471ee15f494ad6977a89eef7c12f130622099aa","execution":{"iopub.status.busy":"2025-11-25T08:07:11.816307Z","iopub.execute_input":"2025-11-25T08:07:11.816621Z","iopub.status.idle":"2025-11-25T08:07:30.935789Z","shell.execute_reply.started":"2025-11-25T08:07:11.816560Z","shell.execute_reply":"2025-11-25T08:07:30.934972Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(12,18))\n# 使用 models 列表中的最后一个模型\nlgb.plot_importance(models[-1], max_num_features=50, height=0.8, ax=ax)\nax.grid(False)\nplt.title(\"LightGBM - Feature Importance (last)\", fontsize=15)\nplt.show()","metadata":{"trusted":true,"_uuid":"a6479cb3ed15c5556045d2fc048c56d614bee3a5","execution":{"iopub.status.busy":"2025-11-25T08:13:14.946464Z","iopub.execute_input":"2025-11-25T08:13:14.946786Z","iopub.status.idle":"2025-11-25T08:13:15.757790Z","shell.execute_reply.started":"2025-11-25T08:13:14.946737Z","shell.execute_reply":"2025-11-25T08:13:15.756506Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    \"card_id\": test_df[\"card_id\"],\n    \"target\": pred_test\n})\nsubmission.to_csv(\"submission.csv\", index=False)\n\nimport os\nprint(os.listdir(\"/kaggle/working\"))","metadata":{"trusted":true,"_uuid":"7371f1139137edebc6a475d2f3bffcbcdf6c1b00","execution":{"iopub.status.busy":"2025-11-25T08:13:19.543526Z","iopub.execute_input":"2025-11-25T08:13:19.543907Z","iopub.status.idle":"2025-11-25T08:13:19.949316Z","shell.execute_reply.started":"2025-11-25T08:13:19.543809Z","shell.execute_reply":"2025-11-25T08:13:19.948014Z"}},"outputs":[],"execution_count":null}]}