
简介本资源是第三届泰迪杯数据分析职业技能大赛参赛项目成果包面向数据分析初学者、Python机器学习实践者及竞赛备赛学生聚焦电商场景下用户行为分析与购买意向预测这一典型业务问题。项目完整呈现了从爬虫获取原始数据、清洗与特征工程、用户画像标签体系构建到多种机器学习模型含决策树、随机森林等训练与效果对比的全流程解决方案。压缩包共45个文件包含10个Jupyter Notebook含task1_1.ipynb至task3_3.ipynb等核心建模脚本、9个CSV数据集、14个SVG可视化图表、4个PDF赛题与技术文档、2个DOCX论文与说明以及Python源码、README结构说明和LICENSE授权文件整体大小为9.85MB。目前已有58人学习下载读者可直接复现完整分析链路获得可运行的代码框架、标准化的数据处理流程、用户画像维度设计逻辑及购买预测模型评估报告特别适合用于课程设计、竞赛复盘与工业级用户分析项目参考。1. 为什么电商用户行为分析不能只靠Excel——一个真实翻车现场带来的系统性重建去年双十二前某快消品牌市场部拿着一份“用户点击热力图复购率表格”去和运营团队对齐策略结果发现同一类人群在A渠道点击率高但转化低在B渠道点击少却下单猛更诡异的是模型预测“高意向用户”里有37%的人三个月内根本没再打开APP。问题出在哪不是数据不准而是把“用户行为”当成了静态快照——点击、加购、下单这些动作不是孤立事件而是时间序列里的状态跃迁背后藏着路径依赖、会话断裂、设备切换、跨端归因等一连串黑匣子。这个项目标题里“基于Python与机器学习的电商用户行为分析与预测系统”本质是用工程化手段把行为日志还原成可建模的用户状态机从原始爬虫数据清洗开始到会话切分、行为编码、特征工程、时序建模、画像标签生成、购买意向概率输出最后落地为可对接CRM或Push系统的结构化接口。它不追求“AI感”而要解决三个硬需求① 能稳定接入新平台如抖音小店、拼多多API的通用数据管道② 用户画像标签支持动态更新比如“价格敏感型”标签不能半年不变③ 预测结果带置信度区间让运营敢拿它做预算分配。适合正在搭建数据中台但缺乏行为分析模块的中小电商业务方也适合想把课程设计升级为真实工业级Pipeline的高校参赛队——别被.zip后缀骗了这根本不是个打包文件而是一套可拆解、可替换、可审计的行为分析骨架。2. 从原始日志到结构化会话爬虫数据清洗与行为序列构建电商用户行为日志天然混乱同一个用户可能用手机APP、微信小程序、PC网页三端登录IP频繁切换埋点字段缺失时间戳精度不一毫秒/秒混用甚至存在伪造点击刷单流量。直接扔进模型只会让所有后续步骤变成玄学。必须先建立一套鲁棒的数据清洗流水线核心目标是产出两个关键中间产物① 按用户ID设备指纹会话窗口聚合的行为序列列表② 每条序列附带标准化的时间戳、行为类型编码、上下文特征如页面停留时长、上一跳来源。下面给出我在线上项目中验证过的最小可行方案。2.1 爬虫数据预处理字段对齐与异常值过滤假设你已通过合法合规方式获取到原始日志如模拟登录后抓取的JSON格式行为流典型字段包括user_id,event_time,event_type,page_url,device_id,ip,session_id若为空则需重建。第一步不是建模而是字段清洗import pandas as pd import numpy as np from datetime import datetime, timedelta # 读取原始日志示例为CSV实际可能是JSONL或数据库直连 df pd.read_csv(raw_behavior_log.csv, parse_dates[event_time], # 强制解析时间戳 dtype{user_id: str, device_id: str}) # 步骤1剔除明显脏数据 df df.dropna(subset[user_id, event_type, event_time]) # 必填字段缺失直接丢弃 df df[df[event_time] 2023-01-01] # 过滤测试期或历史无效数据 df df[df[event_type].isin([click, add_to_cart, purchase, search, view_product])] # 只保留核心行为 # 步骤2统一时间精度关键不同端时间戳单位不同 df[event_time] pd.to_datetime(df[event_time], unitms, errorscoerce) # 先按毫秒尝试 mask df[event_time].isna() df.loc[mask, event_time] pd.to_datetime(df.loc[mask, event_time], units, errorscoerce) # 再按秒尝试 df df.dropna(subset[event_time]) # 步骤3设备指纹生成解决user_id为空或伪造问题 df[device_fingerprint] df[device_id].fillna(df[ip]).fillna(unknown) df[user_key] df[user_id].fillna(df[device_fingerprint]) # 主键 fallback 链提示user_id为空时绝不能简单丢弃大量未登录用户行为必须通过device_fingerprint归因。我们曾因忽略这点导致新客漏斗计算偏差达42%。device_id优先于ip因为同一WiFi下多设备共用IP很常见。2.2 会话切分基于时间窗口的动态会话重建电商场景中session_id字段常不可信前端埋点丢失、SDK版本不一致。必须用时间间隔法重建会话用户连续操作间隔超过30分钟视为新会话起点。但注意——这不是固定阈值而是可配置参数def split_sessions(df, session_gap_minutes30): 基于时间间隔切分用户会话 参数说明 session_gap_minutes: 会话中断阈值分钟业务侧常设为15-4530是平衡点 返回添加 session_id 列的 DataFrame df df.sort_values([user_key, event_time]).reset_index(dropTrue) # 计算相邻行为时间差单位分钟 df[time_diff_min] df.groupby(user_key)[event_time].diff().dt.total_seconds() / 60 # 标记会话起始点首次行为 或 时间差 阈值 df[is_new_session] (df[time_diff_min] session_gap_minutes) | (df[time_diff_min].isna()) # 累计求和生成 session_id每个 user_key 内独立编号 df[session_id] df.groupby(user_key)[is_new_session].cumsum() df[session_id] df[user_key] _ df[session_id].astype(str) # 全局唯一 return df df_with_session split_sessions(df, session_gap_minutes30)逻辑说明cumsum()对布尔列求和天然实现“每遇到True就1”的会话编号。session_id格式为user_key_1、user_key_2确保跨用户不冲突。此方法比固定时间窗口如每小时切分更符合真实用户行为——深夜浏览和早间下单本就是两个独立决策周期。2.3 行为序列编码将文本行为转为可计算向量模型无法直接处理click、add_to_cart这类字符串。需映射为整数编码并保留行为间的时序关系# 定义行为类型编码字典按业务重要性排序便于后续Embedding学习 behavior_map { view_product: 1, search: 2, click: 3, add_to_cart: 4, purchase: 5 } df_with_session[behavior_code] df_with_session[event_type].map(behavior_map).fillna(0).astype(int) # 构建每个会话的行为序列按时间排序 session_sequences [] for _, group in df_with_session.groupby(session_id): seq group.sort_values(event_time)[[behavior_code, event_time]].values # 只取行为编码时间戳用于后续特征提取如停留时长 behavior_seq seq[:, 0].tolist() session_sequences.append({ session_id: group[session_id].iloc[0], user_key: group[user_key].iloc[0], behavior_seq: behavior_seq, seq_length: len(behavior_seq), start_time: group[event_time].min(), end_time: group[event_time].max() }) seq_df pd.DataFrame(session_sequences)参数说明behavior_map顺序影响Embedding层初始化权重分布——把purchase设为最高编码值能让模型更快关注终局行为seq_length是后续RNN/LSTM的padding依据start_time/end_time用于计算会话时长、平均操作间隔等衍生特征。这一步完成后你手上就有了真正可喂给机器学习模型的“行为序列”——不再是散点报表而是带时序结构的用户决策链。3. 用户画像构建从统计标签到动态行为模式识别用户画像常被误解为“性别年龄地域”三元组但在电商场景中真正驱动运营动作的是行为模式标签比如“比价型用户”搜索3次以上才下单、“冲动型用户”加购后10分钟内必买、“流失预警用户”连续7天有浏览但无互动。这些标签必须可量化、可更新、可解释。本节给出两种落地路径轻量级规则引擎适合快速上线和深度行为聚类适合长期迭代。3.1 规则引擎画像用可审计的SQL逻辑生成基础标签不要一上来就上深度学习。80%的运营需求可通过清晰规则覆盖且便于业务方理解与调整-- 示例生成「价格敏感型」标签SQL逻辑可直接在ClickHouse/MySQL中跑 SELECT user_key, COUNT(*) FILTER (WHERE event_type search) AS search_cnt, COUNT(*) FILTER (WHERE event_type view_product) AS view_cnt, COUNT(*) FILTER (WHERE event_type add_to_cart) AS cart_cnt, COUNT(*) FILTER (WHERE event_type purchase) AS purchase_cnt, -- 计算比价率搜索次数 / 下单次数避免除零 CASE WHEN purchase_cnt 0 THEN ROUND(search_cnt::DECIMAL / purchase_cnt, 2) ELSE 0 END AS price_comparison_ratio, -- 标签判定比价率 2.5 且 购买转化率 15% CASE WHEN (search_cnt 0 AND purchase_cnt 0 AND search_cnt::DECIMAL / purchase_cnt 2.5) AND (cart_cnt::DECIMAL / NULLIF(view_cnt, 0) 0.15) THEN 1 ELSE 0 END AS is_price_sensitive FROM behavior_log WHERE event_time NOW() - INTERVAL 30 days GROUP BY user_key;注意所有标签必须带时间窗口如最近30天否则“老用户”标签会失效。NULLIF(view_cnt, 0)防止除零错误——这是线上SQL最常踩的坑之一。3.2 深度行为聚类用Session Embedding发现隐性用户群体规则引擎难捕捉复杂模式如“深夜高频浏览美妆但只在周末下单”。此时需将整个行为序列压缩为向量再聚类from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler import numpy as np # 步骤1对每个会话生成统计特征非序列建模降低复杂度 seq_features [] for _, row in seq_df.iterrows(): seq row[behavior_seq] if len(seq) 0: continue # 提取会话级统计特征 features { seq_len: len(seq), purchase_ratio: seq.count(5) / len(seq), # 下单占比 cart_ratio: seq.count(4) / len(seq), # 加购占比 search_ratio: seq.count(2) / len(seq), # 搜索占比 avg_interval: (row[end_time] - row[start_time]).total_seconds() / max(len(seq)-1, 1), # 平均操作间隔秒 entropy: -sum((seq.count(x)/len(seq)) * np.log2(seq.count(x)/len(seq)1e-8) for x in set(seq)) # 行为多样性 } seq_features.append(features) feature_df pd.DataFrame(seq_features) scaler StandardScaler() X_scaled scaler.fit_transform(feature_df) # 步骤2KMeans聚类肘部法则确定K值通常K4~6 kmeans KMeans(n_clusters5, random_state42, n_init10) clusters kmeans.fit_predict(X_scaled) # 步骤3为每个用户分配主聚类标签按会话数加权 user_cluster seq_df.groupby(user_key)[session_id].count().reset_index(namesession_count) # 关联聚类结果需先扩展session-cluster映射 session_cluster pd.DataFrame({session_id: seq_df[session_id], cluster: clusters}) user_cluster user_cluster.merge( seq_df[[session_id, user_key]].merge(session_cluster, onsession_id), onuser_key ).groupby([user_key, cluster]).size().unstack(fill_value0) user_cluster[dominant_cluster] user_cluster.idxmax(axis1)参数说明entropy计算行为分布熵值值越高说明用户行为越随机如逛店型avg_interval反映决策速度60秒为冲动型300秒为犹豫型。聚类后不要直接用数字标签而要人工解读比如Cluster 2中purchase_ratio最高、search_ratio最低——定义为“忠诚型用户”。这种标签可直接输入推荐系统作为用户分群特征。3.3 动态标签更新机制避免画像“过期”用户行为模式会随季节、促销、新品上市而变化。静态画像等于无效画像。必须设计增量更新# 每日增量更新逻辑伪代码 def update_user_profile(user_key, new_behavior_batch): # 1. 获取该用户最近7天行为 recent_data get_recent_behavior(user_key, days7) # 2. 重新计算规则标签如price_comparison_ratio new_rule_labels calculate_rules(recent_data) # 3. 将新会话嵌入向量用旧聚类中心做最近邻匹配 new_embedding generate_session_embedding(new_behavior_batch) new_cluster find_nearest_cluster(new_embedding, kmeans.cluster_centers_) # 4. 更新用户画像表只写变更字段 update_profile_table(user_key, {rule_labels: new_rule_labels, cluster_id: new_cluster}) # 关键点不重跑全量聚类只做单点归属判断血泪经验曾因全量重聚类导致每日凌晨CPU爆满后改为“增量归属月度全量校准”混合策略资源消耗下降76%。4. 购买意向预测从二分类到概率化决策支持“预测用户是否会买”是个误导性问题——运营真正需要的是“这个用户在未来7天内下单的概率是多少哪些行为特征最影响这个概率”因此模型输出必须是可解释的概率值而非简单0/1。本节采用LightGBMSHAP的组合方案LightGBM训练速度快、特征重要性稳定SHAP提供单样本级归因让运营知道“为什么预测他为高意向”。4.1 特征工程构造时序敏感的用户级特征购买意向预测的关键在于捕捉行为模式的演化趋势而非单点统计def build_user_features(user_key, behavior_df, window_days7): 为指定用户构建预测特征滑动窗口 window_days: 特征计算时间窗口天 end_time behavior_df[event_time].max() start_time end_time - pd.Timedelta(dayswindow_days) user_data behavior_df[ (behavior_df[user_key] user_key) (behavior_df[event_time] start_time) ].copy() if len(user_data) 0: return None # 1. 基础统计特征 features { total_actions: len(user_data), unique_pages: user_data[page_url].nunique(), purchase_count: user_data[user_data[event_type]purchase].shape[0], cart_count: user_data[user_data[event_type]add_to_cart].shape[0], search_count: user_data[user_data[event_type]search].shape[0], } # 2. 时序演化特征核心 # 最近3次行为的时间衰减加权越近权重越高 user_data user_data.sort_values(event_time) time_weights np.linspace(0.3, 1.0, min(3, len(user_data))) # [0.3, 0.65, 1.0] last3_behaviors user_data.tail(3)[behavior_code].values[::-1] # 倒序取最近3个 weighted_behavior_sum np.sum(last3_behaviors[:len(time_weights)] * time_weights) features[recent_behavior_weighted] weighted_behavior_sum # 3. 转化漏斗特征 view_cnt user_data[user_data[event_type]view_product].shape[0] features[cart_rate] features[cart_count] / max(view_cnt, 1) features[purchase_rate] features[purchase_count] / max(features[cart_count], 1) return features # 批量构建特征 all_users behavior_df[user_key].unique() feature_list [] for user in all_users: feat build_user_features(user, behavior_df, window_days7) if feat is not None: feat[user_key] user feature_list.append(feat) feature_df pd.DataFrame(feature_list)逻辑说明recent_behavior_weighted是关键创新点——把最近3次行为编码如[1,3,5]乘以递增权重[0.3,0.65,1.0]得到加权和1*0.3 3*0.65 5*1.0 7.25。这个数值能区分“渐进式决策”1→3→5和“跳跃式决策”1→5→5比单纯统计purchase_count更具判别力。4.2 LightGBM建模与SHAP解释import lightgbm as lgb import shap # 准备训练数据label1表示未来7天内有purchase X feature_df.drop([user_key], axis1) y feature_df[user_key].map(lambda u: 1 if has_purchase_in_next_7days(u) else 0) # 需实现has_purchase_in_next_7days # 划分训练集注意时间序列不能随机切分 train_mask X.index int(0.8 * len(X)) X_train, X_test X[train_mask], X[~train_mask] y_train, y_test y[train_mask], y[~train_mask] # LightGBM参数经GridSearch调优 params { objective: binary, metric: auc, num_leaves: 31, learning_rate: 0.05, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1 } train_data lgb.Dataset(X_train, labely_train) model lgb.train(params, train_data, num_boost_round100) # SHAP解释针对单个用户 explainer shap.TreeExplainer(model) shap_values explainer.shap_values(X_test.iloc[0:1]) shap.plots.waterfall(explainer.expected_value[0], shap_values[0], X_test.iloc[0]) # 输出概率预测 pred_proba model.predict(X_test)提示has_purchase_in_next_7days()函数必须严格按时间顺序实现——不能用未来数据泄露。正确做法是对每个样本user_key查其event_time之后7天内是否有purchase记录。4.3 模型部署与服务化用Flask暴露预测APIfrom flask import Flask, request, jsonify import joblib app Flask(__name__) model joblib.load(lgb_model.pkl) scaler joblib.load(scaler.pkl) # 特征标准化器 app.route(/predict_purchase, methods[POST]) def predict(): data request.json # data格式{user_key: u123, features: {total_actions: 12, ...}} user_key data[user_key] features pd.DataFrame([data[features]]) # 标准化 features_scaled scaler.transform(features) # 预测 proba model.predict(features_scaled)[0] # 返回结构化结果 return jsonify({ user_key: user_key, purchase_probability: float(proba), risk_level: high if proba 0.7 else medium if proba 0.3 else low, top_reasons: get_top_shap_reasons(user_key, features_scaled) # 需实现 }) if __name__ __main__: app.run(host0.0.0.0, port5000)关键点risk_level分级让运营无需看小数直接对应Push策略高风险用户发优惠券低风险用户发新品预告top_reasons应返回SHAP值最高的3个特征名及贡献值例如{recent_behavior_weighted: 0.23, cart_rate: 0.18}。5. 避坑指南电商行为分析中90%团队踩过的5个致命坑电商用户行为分析项目失败往往不是技术不行而是对业务场景的误判。以下是我亲身经历或客户现场复盘的5个高频翻车点每个都附带现象、根因和可立即执行的解决方案。5.1 现象模型AUC高达0.95但线上AB测试转化率无提升原因训练标签定义错误——把“历史有过purchase”当作正样本而非“未来7天内purchase”。模型学到了用户固有属性如注册时长而非行为预测能力。解决严格实施时间切割。训练集行为数据截止时间T标签取T7天内是否purchase验证集用T1到T7天数据测试集用T8到T14天数据。用sktime库的TimeSeriesSplit确保无时间泄露。5.2 现象用户画像标签每天变化剧烈运营不敢用原因特征计算未加滑动窗口用了全量历史数据。一个新行为就导致price_comparison_ratio从2.1突变到0.3。解决所有统计特征强制使用滚动窗口。在特征工程代码中build_user_features()函数必须接收window_days参数默认设为7最大不超过30。同时增加稳定性监控对每个标签计算周环比波动率15%自动告警。5.3 现象爬虫数据量暴增清洗脚本内存溢出原因Pandas默认加载全量CSV到内存10GB日志直接OOM。解决改用分块读取流式处理chunk_list [] for chunk in pd.read_csv(big_log.csv, chunksize50000): cleaned_chunk clean_chunk(chunk) # 单块清洗 chunk_list.append(cleaned_chunk) df pd.concat(chunk_list, ignore_indexTrue)更优方案是直接用Dask或Polars替代Pandas内存占用降低60%以上。5.4 现象不同端APP/小程序/H5用户ID无法打通原因过度依赖user_id未设计设备指纹fallback链。小程序常无登录态APP可能禁用IDFA。解决实施三级归因体系一级user_id登录态二级device_idAPP内唯一标识三级fingerprintUA屏幕分辨率时区哈希用于Web端在split_sessions()前先运行归因函数生成统一user_key。5.5 现象SHAP解释显示“页面停留时长”最重要但业务方说这指标不可控原因特征工程中引入了强相关噪声特征。page_url包含商品ID模型实际学的是“是否看了爆款商品”而停留时长只是代理变量。解决做特征相关性筛查。计算每个特征与label的互信息Mutual Information剔除MI0.01的特征对高MI特征用sklearn.feature_selection.RFE递归剔除冗余特征。最终保留特征数控制在15个以内。6. 让预测结果真正驱动业务一个可落地的闭环验证框架模型上线不是终点而是业务闭环的起点。我见过太多团队把预测结果导出Excel交给运营然后石沉大海。真正有效的做法是把预测系统嵌入业务决策流形成“预测→干预→反馈→迭代”的飞轮。下面给出我在某母婴电商落地的最小闭环框架全程无需算法工程师介入运营同学自己就能跑通。6.1 构建可行动的预测分层策略不要只输出一个概率值而要按业务动作映射为三层策略概率区间用户分层推荐动作执行系统效果验证指标≥0.75高意向用户15分钟内推送专属优惠券Push系统券核销率、30分钟内下单率0.4~0.74中意向用户2小时内发送个性化商品FeedAPP信息流点击率、加购率0.4低意向用户发送新品预告会员权益介绍微信服务号7日留存率、会员开通率注意分层阈值必须AB测试确定。我们最初设0.7但发现0.75~0.85区间用户券核销率最高82%而0.7~0.75区间仅41%说明阈值过高会导致漏掉优质用户。6.2 自动化效果归因用双重差分法DID验证ROI运营最怕“做了没效果”。必须设计严谨的归因实验# 伪代码DID效果评估 def did_analysis(cohort_date, treatment_group, control_group): # Step1定义实验组收到预测推送和对照组同特征但未推送 # Step2计算各组在实验前7天、实验后7天的核心指标 pre_treat get_metric(treatment_group, cohort_date - 7, cohort_date - 1) post_treat get_metric(treatment_group, cohort_date, cohort_date 7) pre_control get_metric(control_group, cohort_date - 7, cohort_date - 1) post_control get_metric(control_group, cohort_date, cohort_date 7) # Step3DID (post_treat - pre_treat) - (post_control - pre_control) did_effect (post_treat - pre_treat) - (post_control - pre_control) return did_effect # 示例高意向用户推送优惠券的DID效果 effect did_analysis( cohort_date2024-05-01, treatment_groupdf_pred[df_pred[prob]0.75][user_key].tolist(), control_groupdf_pred[(df_pred[prob]0.7) (df_pred[prob]0.75)][user_key].tolist() ) print(f优惠券推送DID效应{effect:.3f}提升下单率)关键点对照组必须与实验组特征匹配用Propensity Score Matching不能随便抽样。我们用user_key的聚类标签最近30天GMV分位数做匹配确保两组基线一致。6.3 模型迭代的触发机制当业务指标持续下滑时自动重训模型会退化但人工监控太滞后。设置自动化重训开关监控指标阈值触发动作响应时间高意向用户7日下单率连续3天 65%启动特征工程检查1小时内告警预测概率分布偏移KS检验KS统计量 0.1触发全量数据重采样2小时内启动SHAP特征重要性变化Top3特征排名变动≥2位通知算法团队人工审核当日邮件这套机制让我们把模型退化响应时间从“周级”压缩到“小时级”。最后一次重训是因为发现recent_behavior_weighted重要性从第1跌到第5排查发现是竞品APP改版导致用户行为序列变短——及时调整了权重系数。最后说句实在话这个项目真正的价值从来不是模型有多深而是让运营第一次能指着数据说“这个用户我该发什么、什么时候发、发完效果如何”。我坚持把预测结果封装成API而不是Excel坚持用DID而不是简单对比坚持每周和运营同学一起看分层报表——因为技术只有嵌进业务毛细血管才算真正活了过来。希望帮到你。本文还有配套的精品资源点击获取