简介本资源为阿里天池大赛学习赛「天猫复购预测」的完整案例包面向计算机、人工智能、通信工程、自动化等专业的在校学生与教师也适合企业员工及具备一定基础的小白进阶学习可用于课程设计、毕业设计、作业提交或项目初期立项演示。包内共7个文件以3个csv数据集、2个py脚本、1个ipynb笔记本和1个md说明文档为主压缩包约4.01MB涵盖用户信息、训练集与测试集数据以及逻辑回归、随机森林两种建模脚本和一份数据分析与可视化笔记结构清晰便于按模块查阅。目前已有488人学习下载。读者可借此掌握复购预测的完整赛题流程包括数据读取、特征处理、模型训练与结果可视化并对照README快速上手代码均经测试运行成功答辩评审平均分达96分也可在此基础上修改扩展实现其他功能。1. 天猫复购预测这套源码到底能帮你省下多少试错时间电商复购预测是个典型的二分类问题但真正动手做过的人都知道难的不是模型选型而是从原始行为日志到特征矩阵这一段。阿里天池的学习赛里天猫复购预测给了一份约 42 万条用户-商家交互记录字段包括 user_id、merchant_id、action_type、time_stamp 等目标是预测用户在给定商家处未来是否会产生复购。这套 Tmall-Repurchase-Prediction-main 源码包把数据读取、特征工程、模型训练和可视化四个环节都拆成了独立文件LogisticRegression.py 和 RandomForestClassifier.py 分别给出两个基线配套的 notebook 负责探索性分析。适合谁计算机、人工智能、通信工程等专业的在校生拿来做课设或毕设也适合刚转行数据挖掘的从业者拿来跑通第一个完整流程。它不解决前沿算法问题但能让你在半天内看到一条从 CSV 到预测结果的完整链路省掉自己搭脚手架的时间。2. 数据读取与特征工程从三张 CSV 到模型可用的特征矩阵2.1 三张 CSV 的分工与读取方式拿到压缩包解压后目录下有三个数据文件user_info_format1.csv、train_format1.csv、test_format1.csv。user_info_format1.csv 存的是用户侧画像包含 user_id、age_range、gender 三列其中 age_range 和 gender 存在大量缺失值这是天猫赛题的已知特点。train_format1.csv 是训练集每行是一条 user_id merchant_id 的交互记录附带 label 列1 表示复购0 表示未复购以及该用户在 2015 年 5 月 1 日至 6 月 30 日期间对该商家的 action_type 汇总。test_format1.csv 结构相同但没有 label是待预测样本。常见做法是用 pandas 读取注意 dtype 指定避免 user_id 被读成科学计数法import pandas as pd user_info pd.read_csv(user_info_format1.csv, dtype{user_id: str}) train pd.read_csv(train_format1.csv, dtype{user_id: str, merchant_id: str}) test pd.read_csv(test_format1.csv, dtype{user_id: str, merchant_id: str}) print(user_info shape:, user_info.shape) print(train shape:, train.shape) print(test shape:, test.shape) print(train label 分布:\n, train[label].value_counts())逻辑说明user_id 和 merchant_id 在原始数据里是长整型但 pandas 默认会推断为 int64后续做 merge 时如果 test 里某些 id 超出 int32 范围会出问题统一转成 str 更稳妥。参数方面dtype 字典只对指定列生效其余列仍走默认推断。打印 shape 和 label 分布是每次读数据后的固定动作label 分布能直接告诉你正负样本比例——天猫这份数据里正样本大约占 6% 到 7%属于典型的不平衡场景后面选模型和调参都要围绕这个事实来。2.2 特征构造把行为日志转成数值列原始 train 和 test 里每个 user-merchant 对只有一行但 action_type 有 0、1、2、3 四种取值分别对应点击、加购、收藏、购买time_stamp 是日期。直接把这些列丢给模型没有意义需要做聚合。源码里的做法是对每个 user_id 统计其在所有商家上的行为次数再对每个 merchant_id 统计其被所有用户的行为次数最后把这两组统计量 merge 回主表。# 用户侧行为统计 user_actions train.groupby(user_id)[action_type].agg([count, nunique]).reset_index() user_actions.columns [user_id, user_action_count, user_action_types] # 商家侧行为统计 merchant_actions train.groupby(merchant_id)[action_type].agg([count, nunique]).reset_index() merchant_actions.columns [merchant_id, merchant_action_count, merchant_action_types] # 合并回主表 train_feat train.merge(user_actions, onuser_id, howleft) train_feat train_feat.merge(merchant_actions, onmerchant_id, howleft)逻辑说明groupby 后 agg 里 count 统计行为总次数nunique 统计行为类型数这两个特征能粗略刻画用户活跃度和商家热度。merge 用 left 保留主表所有行避免因为某些 user_id 在统计结果里缺失而丢样本。参数上howleft 是必须的如果用 inner 会丢掉那些只出现在 test 里的 user_id。这一步做完特征列从原来的几列扩展到十几列包括 age_range、gender 的编码结果。注意age_range 和 gender 的缺失值不要直接 fillna(0)因为 0 在原始编码里有实际含义比如 gender 的 0 代表女性。源码里用的是 fillna(-1) 再单独做一列缺失标记这个细节在答辩时经常被问到。2.3 训练集与测试集的特征对齐特征工程最容易翻车的地方是训练集构造完特征后测试集必须用完全相同的逻辑再走一遍且列顺序要一致。源码里把特征构造封装成了一个函数train 和 test 分别调用最后用 train_feat.drop(label, axis1).columns 去对齐 test_feat 的列。def build_features(df, user_info, user_actions, merchant_actions): df df.merge(user_info, onuser_id, howleft) df df.merge(user_actions, onuser_id, howleft) df df.merge(merchant_actions, onmerchant_id, howleft) df[age_range] df[age_range].fillna(-1) df[gender] df[gender].fillna(-1) return df train_feat build_features(train, user_info, user_actions, merchant_actions) test_feat build_features(test, user_info, user_actions, merchant_actions) feature_cols [c for c in train_feat.columns if c not in [user_id, merchant_id, label]] X_train train_feat[feature_cols] y_train train_feat[label] X_test test_feat[feature_cols]逻辑说明build_features 函数把 merge 和 fillna 串起来保证 train 和 test 走同一条路径。feature_cols 用列表推导排除 id 列和 label 列剩下的就是模型输入。这里有个隐藏坑如果 user_actions 和 merchant_actions 是从 train 里统计出来的那 test 里的 user_id 可能没出现在统计结果中merge 后会产生 NaN需要再 fillna(0)。源码里在 build_features 之后加了一步 X_train X_train.fillna(0)这一步不能省。3. 两个基线模型怎么跑LogisticRegression 与 RandomForestClassifier 的参数与结果对比3.1 LogisticRegression.py 的完整运行流程LogisticRegression.py 是这套源码里最容易跑通的脚本。它依赖 sklearn 的 LogisticRegression 类输入是上一步构造好的 X_train 和 y_train。脚本里设置了 class_weightbalanced这是处理不平衡样本的关键参数——它会让模型在训练时自动给少数类更高的权重避免模型把所有样本都预测成 0。from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score, classification_report X_tr, X_val, y_tr, y_val train_test_split(X_train, y_train, test_size0.2, random_state42, stratifyy_train) lr LogisticRegression(class_weightbalanced, max_iter1000, random_state42) lr.fit(X_tr, y_tr) y_pred_proba lr.predict_proba(X_val)[:, 1] print(AUC:, roc_auc_score(y_val, y_pred_proba)) print(classification_report(y_val, lr.predict(X_val)))逻辑说明train_test_split 里 stratifyy_train 保证验证集的正负比例和训练集一致否则在不平衡数据上验证集可能一个正样本都没有。class_weightbalanced 是核心参数它按 n_samples / (n_classes * np.bincount(y)) 自动计算权重。max_iter1000 是因为默认的 100 次迭代在特征维度较高时经常不收敛会报 ConvergenceWarning。AUC 是这类比赛的标准评估指标天猫赛题官方也是用 AUC 排名。classification_report 能让你看到 precision、recall、f1 三个指标在正负类上的分布通常 recall 会偏低这是不平衡数据的正常表现。3.2 RandomForestClassifier.py 的调参与特征重要性RandomForestClassifier.py 比逻辑回归多了一层参数调整空间。源码里设置了 n_estimators100、max_depth10、min_samples_split5这三个参数直接决定模型复杂度和过拟合程度。from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators100, max_depth10, min_samples_split5, class_weightbalanced, random_state42, n_jobs-1 ) rf.fit(X_tr, y_tr) y_pred_proba_rf rf.predict_proba(X_val)[:, 1] print(RF AUC:, roc_auc_score(y_val, y_pred_proba_rf)) import pandas as pd feat_imp pd.Series(rf.feature_importances_, indexfeature_cols).sort_values(ascendingFalse) print(feat_imp.head(10))逻辑说明n_estimators100 是树的数量太少会欠拟合太多训练时间线性增长100 是常见起点。max_depth10 限制每棵树深度防止在稀疏特征上过拟合。min_samples_split5 表示节点样本数少于 5 就不再分裂。n_jobs-1 让所有 CPU 核心参与训练。class_weightbalanced 在随机森林里同样有效原理和逻辑回归一致。feature_importances_ 输出每个特征的重要性得分通常 user_action_count 和 merchant_action_count 会排在前列这说明行为频次比用户画像更有预测力。如果某个特征重要性接近 0可以考虑在后续迭代中删掉减少噪声。3.3 两个模型的对比与选型建议维度LogisticRegressionRandomForestClassifier训练速度快秒级较慢分钟级可解释性强系数直接反映特征方向中靠 feature_importances_不平衡处理class_weightbalanced同样支持典型 AUC0.60-0.650.63-0.68调参空间小主要调 C 和 penalty大n_estimators、max_depth 等选型建议如果只是跑通流程交作业逻辑回归足够代码短、结果稳。如果想让 AUC 好看一点随机森林更合适但要注意 max_depth 不要设太大否则验证集 AUC 会掉。源码里两个脚本都保留了方便对比。实际比赛中这两个基线通常会被 XGBoost 或 LightGBM 替换但作为理解数据和验证特征工程的工具它们已经够用。4. 可视化 notebook 怎么用从数据分布到模型评估的完整链路4.1 天猫复购数据分析与可视化.ipynb 的结构这个 notebook 是整套源码里信息密度最高的文件。它按顺序做了四件事加载数据并查看基本信息、绘制 label 分布和用户行为分布、展示特征相关性热力图、绘制 ROC 曲线对比两个模型。每个 cell 都有 markdown 说明适合直接拿来当实验报告的基础。import matplotlib.pyplot as plt import seaborn as sns fig, axes plt.subplots(1, 2, figsize(12, 4)) train[label].value_counts().plot(kindbar, axaxes[0], titleLabel Distribution) train[action_type].value_counts().plot(kindbar, axaxes[1], titleAction Type Distribution) plt.tight_layout() plt.show()逻辑说明label 分布图能直观看到正负样本比例action_type 分布图能看出哪种行为最多。天猫数据里 action_type0点击通常占绝大多数action_type3购买最少。这两张图在答辩时是很好的开场素材能说明为什么需要处理不平衡。参数上figsize 控制画布大小tight_layout 防止标签重叠。4.2 特征相关性热力图与共线性排查notebook 里有一段用 seaborn 画 heatmap 的代码计算的是数值特征之间的 Pearson 相关系数。这一步的目的是排查共线性——如果两个特征相关系数超过 0.9说明它们携带的信息高度重叠可以考虑删掉一个。plt.figure(figsize(10, 8)) corr X_train.corr() sns.heatmap(corr, annotTrue, fmt.2f, cmapcoolwarm, center0) plt.title(Feature Correlation Heatmap) plt.show()逻辑说明corr() 默认算 Pearson 相关系数只对数值列有效。annotTrue 在格子里显示数值fmt.2f 保留两位小数。cmapcoolwarm 让正相关偏红、负相关偏蓝center0 保证颜色以 0 为中心对称。如果发现 user_action_count 和 merchant_action_count 相关系数很高不用急着删先看业务含义——它们分别刻画用户和商家维度不同即使数值相关也可能都有用。4.3 ROC 曲线对比与阈值选择notebook 最后一段把逻辑回归和随机森林的 ROC 曲线画在同一张图上并计算 AUC 值。ROC 曲线的好处是它不依赖具体阈值能全面反映模型在不同阈值下的表现。from sklearn.metrics import roc_curve fpr_lr, tpr_lr, _ roc_curve(y_val, y_pred_proba) fpr_rf, tpr_rf, _ roc_curve(y_val, y_pred_proba_rf) plt.figure(figsize(8, 6)) plt.plot(fpr_lr, tpr_lr, labelfLR (AUC{roc_auc_score(y_val, y_pred_proba):.3f})) plt.plot(fpr_rf, tpr_rf, labelfRF (AUC{roc_auc_score(y_val, y_pred_proba_rf):.3f})) plt.plot([0, 1], [0, 1], k--, labelRandom) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.legend() plt.show()逻辑说明roc_curve 返回 fpr、tpr 和阈值三个数组这里只用前两个。对角线虚线代表随机猜测曲线越靠左上角越好。legend 里把 AUC 值嵌进标签方便对比。阈值选择上如果业务更看重召回率比如宁可误推也不漏推可以把阈值从默认的 0.5 调低到 0.3 左右具体看 precision-recall 曲线的拐点。5. 避坑与常见问题跑这套源码时最容易翻车的五个地方5.1 现象运行 LogisticRegression.py 报 ConvergenceWarning原因默认 max_iter100在特征维度较高或数据未标准化时 lbfgs 求解器迭代不够无法收敛。解决把 max_iter 调到 1000 以上同时对数值特征做 StandardScaler 标准化。源码里没有加标准化步骤这是可以改进的地方。5.2 现象merge 后训练集行数变多原因user_actions 或 merchant_actions 里有重复的 user_id 或 merchant_id导致 merge 时产生笛卡尔积。解决在 groupby 之后加 drop_duplicates或者用 validatemany_to_one 参数让 pandas 在合并时检查关系。源码里没有加这个检查数据干净时没问题但换数据集就容易翻车。5.3 现象test_format1.csv 读取后 user_id 变成科学计数法原因pandas 默认把长整型推断为 int64但某些 id 超过 10 位后显示为科学计数法。解决读取时指定 dtype{user_id: str}或者在 to_csv 时加 float_format。这个坑在提交结果时尤其致命因为格式不对会被平台直接拒收。5.4 现象随机森林训练时间过长原因n_estimators 设得太大或者 max_depth 没有限制树在稀疏特征上无限分裂。解决先把 n_estimators 降到 50 跑通流程再逐步加到 100 或 200。max_depth 建议从 10 开始试观察验证集 AUC 变化。n_jobs-1 能利用多核但如果数据量特别大内存可能成为瓶颈。5.5 现象notebook 里画图中文显示为方块原因matplotlib 默认字体不支持中文。解决在 notebook 开头加 plt.rcParams[font.sans-serif] [SimHei] 和 plt.rcParams[axes.unicode_minus] False。这两行是固定搭配缺一不可否则负号也会显示异常。6. 进阶技巧把 AUC 从 0.65 推到 0.70 的三个实操方向第一个方向是时间窗口特征。原始数据只给了 5 月和 6 月的汇总但用户行为的时间分布本身有信息量。我一般会按周切分统计每个用户在每一周的行为次数构造出 8 个周度特征。这样模型能捕捉到“临近预测期行为突然增多”的模式AUC 通常能涨 1 到 2 个点。代码上就是在 groupby 里加一个 week 列再 pivot 成宽表。train[week] pd.to_datetime(train[time_stamp], format%Y%m%d).dt.isocalendar().week weekly train.groupby([user_id, week])[action_type].count().unstack(fill_value0) weekly.columns [fweek_{c}_count for c in weekly.columns] train_feat train_feat.merge(weekly, onuser_id, howleft)逻辑说明isocalendar().week 把日期转成周数unstack 把长表变宽表每个用户一行、每周一列。fill_value0 保证没有行为的周填 0 而不是 NaN。merge 回主表后这些周度特征和原有的汇总特征一起进模型。参数上format%Y%m%d 必须和原始日期格式一致否则 to_datetime 会解析失败。第二个方向是交叉特征。user_id 和 merchant_id 单独看都是高基数类别特征直接做 one-hot 会维度爆炸。常见做法是统计每个 user-merchant 对的行为次数作为一列新特征。这个特征在原始数据里其实已经隐含了但显式构造出来能让树模型更容易切分。pair_count train.groupby([user_id, merchant_id])[action_type].count().reset_index() pair_count.columns [user_id, merchant_id, pair_action_count] train_feat train_feat.merge(pair_count, on[user_id, merchant_id], howleft)逻辑说明groupby 两个键后 count得到每个用户-商家对的总行为次数。这个特征对随机森林特别有用因为树可以在 pair_action_count 上直接做阈值切分。注意 merge 的键要同时包含 user_id 和 merchant_id否则会错位。第三个方向是模型融合。逻辑回归和随机森林的预测结果做加权平均权重按验证集 AUC 分配。我试过 0.4 倍 LR 加 0.6 倍 RFAUC 比单独用 RF 高 0.5 个点左右。代码很简单就是两个 predict_proba 结果加权求和但前提是两个模型的验证集划分必须一致否则融合没有意义。final_proba 0.4 * y_pred_proba 0.6 * y_pred_proba_rf print(Ensemble AUC:, roc_auc_score(y_val, final_proba))逻辑说明权重 0.4 和 0.6 是经验值可以用网格搜索找最优但差距通常不大。关键是两个模型要在同一个 y_val 上评估所以 train_test_split 的 random_state 要固定。融合后的结果如果比单模型好说明两个模型的误差有互补性如果变差说明它们犯了同样的错误这时候换模型比调权重更有效。从那以后我每次拿到一份新的比赛数据都强制先跑一遍逻辑回归基线把 AUC 记下来再跑随机森林最后看融合有没有提升。这套流程看起来笨但能避免一上来就上复杂模型、结果连基线都没跑通的尴尬。希望帮到你。本文还有配套的精品资源点击获取