ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

B2C电商数据集实战:从数据清洗到模型验证的避坑指南

B2C电商数据集实战:从数据清洗到模型验证的避坑指南 简介这份资源是面向电商数据分析初学者与从业者的B2C平台数据集来源于国内某知名电商网站可用于用户行为研究、推荐系统实验与平台运营分析。压缩包共30个文件以csv、sql、xml三类数据文件为主分别对应结构化数据表、数据库导入脚本与半结构化记录另附dmp数据泵文件、pdf说明文档和xls样例表整体约14.83MB便于快速导入本地环境复现分析流程。数据集覆盖用户浏览与购买记录、商品分类与价格库存、订单交易与支付方式、客户人口统计特征、评价反馈及流量来源等维度可支撑关联规则、聚类、分类与预测建模等任务。目前已有1590人学习下载适合作为课程设计、毕业项目或数据挖掘练手的实战素材帮助读者从数据清洗、缺失值处理到业务洞察形成完整链路理解电商场景下的用户留存与市场趋势分析。1. 拿到一份 B2C 电商数据集先别急着往模型里灌一份命名混乱的国内某B2C电子商务网站的数据集.rar摆在面前解压后大概率是订单流水、用户行为日志、商品维表这几类 CSV 或 SQL 导出文件。很多人第一反应是pd.read_csv然后直接丢进推荐或风控模型结果跑出来的 AUC 惨不忍睹回头查半天才发现字段里混着脱敏占位符、时间戳单位不统一、还有大量爬虫产生的脏会话。B2C 电商数据集的核心价值不在“大”而在“行为链路完整”——从曝光、点击、加购、下单到支付每一环的转化都能被还原才谈得上做召回、排序、用户分层或流失预警。这篇文章面向的是手里已经拿到一份电商数据集、想把它真正用起来的算法和数据分析同学我会按“先摸清结构、再清洗对齐、然后跑通一个最小可用模型、最后讲清楚哪些坑会让整条链路翻车”的顺序讲。适合谁适合做推荐系统、用户增长、交易风控以及需要拿真实行为数据做特征工程的人。不适合只想找现成 Kaggle 竞赛 baseline 的人因为这类国内 B2C 数据集的结构往往比公开数据集更“野”。2. 拆开压缩包先做三件事字段测绘、粒度确认、时间轴对齐2.1 用脚本把每个表的字段类型和空值率一次性打出来拿到数据集最忌讳凭文件名猜内容。我一般会先写一个测绘脚本把目录下所有 CSV/Excel 的字段名、dtype、非空计数、唯一值数量、前 3 行样例全部输出到一份报告里。这样做的目的是快速判断哪些表是事实表订单、行为日志哪些是维表用户、商品、类目以及哪些字段是脱敏后不可用的。import pandas as pd import os from pathlib import Path def profile_dataset(root_dir): report [] for file in Path(root_dir).rglob(*): if file.suffix.lower() not in [.csv, .xlsx, .txt]: continue try: # 大文件先读前 5000 行做类型推断避免内存爆掉 df pd.read_csv(file, nrows5000, encodingutf-8, on_bad_linesskip) except UnicodeDecodeError: df pd.read_csv(file, nrows5000, encodinggbk, on_bad_linesskip) except Exception as e: report.append({file: str(file), error: str(e)}) continue for col in df.columns: report.append({ file: file.name, column: col, dtype: str(df[col].dtype), non_null: df[col].notna().sum(), null_rate: round(df[col].isna().mean(), 4), nunique: df[col].nunique(), sample: str(df[col].dropna().head(3).tolist())[:120] }) return pd.DataFrame(report) profile profile_dataset(./dataset) profile.to_csv(field_profile.csv, indexFalse, encodingutf-8-sig) print(profile.groupby(file)[column].count())这段脚本的关键参数有三个nrows5000控制内存占用on_bad_linesskip防止个别脏行中断整个读取encoding先试 utf-8 再回退 gbk 是国内数据集的常见编码现实。输出报告里重点看三列null_rate超过 0.6 的字段基本可以放弃nunique等于 1 的字段是常量列sample里出现***、null、-1这类占位符的要单独标记。做完这一步你手里应该有一张“哪些字段能用、哪些字段要修、哪些字段直接扔”的清单。2.2 确认行为表的粒度一行是一次曝光还是一次点击B2C 数据集最容易翻车的地方是粒度混淆。行为日志表里一行可能代表一次商品曝光impression也可能代表一次点击click还可能代表一次会话session聚合。如果不确认清楚后面算 CTR 时分子分母全错。判断方法很直接看时间戳字段的间隔分布以及同一用户同一商品在短时间窗口内是否重复出现。# 假设行为表有 user_id, item_id, event_type, ts 四列 behavior pd.read_csv(./dataset/behavior_log.csv, parse_dates[ts]) # 看 event_type 的分布 print(behavior[event_type].value_counts()) # 看同一 user-item 对在 1 秒内的重复次数 dup behavior.groupby([user_id, item_id, ts]).size() print(同一秒内重复记录占比:, (dup 1).mean()) # 看时间戳最小间隔 behavior behavior.sort_values(ts) gap behavior[ts].diff().dt.total_seconds().dropna() print(gap.describe())如果event_type只有click一种说明曝光数据缺失你无法算真实 CTR只能算点击量。如果同一秒内重复记录占比很高说明数据可能是曝光日志且未去重。如果时间戳最小间隔是 0 秒且大量集中说明是批量导入时打的时间戳不具备行为时序意义。这些结论直接决定你后面能不能做序列建模。2.3 把用户、商品、订单三张表的时间轴对齐到同一时区国内电商数据集常见的时间字段有order_time、create_time、pay_time、event_time格式可能是2023-01-01 12:00:00、1672531200秒级时间戳、1672531200000毫秒级时间戳混用。更隐蔽的坑是部分表用 UTC 存储部分表用北京时间直接 join 会导致订单和行为的先后关系错乱。def normalize_time(series, unitauto): if pd.api.types.is_numeric_dtype(series): # 判断秒级还是毫秒级大于 1e12 一般是毫秒 if series.max() 1e12: return pd.to_datetime(series, unitms) return pd.to_datetime(series, units) return pd.to_datetime(series) orders[order_time] normalize_time(orders[order_time]) behavior[event_time] normalize_time(behavior[event_time]) # 统一转成北京时间假设原始有 UTC orders[order_time] orders[order_time].dt.tz_localize(UTC).dt.tz_convert(Asia/Shanghai)参数说明unitauto的判断逻辑是先看数值量级秒级时间戳约 1.6e9毫秒级约 1.6e12用 1e12 做分界是经验值。tz_localize和tz_convert的顺序不能反否则会报错。对齐之后用orders.merge(behavior, onuser_id)再过滤event_time order_time才能得到“下单前行为”这个正确的前置窗口。3. 清洗与特征化把原始日志变成模型能吃的宽表3.1 处理脱敏字段和缺失值的四条规则国内 B2C 数据集为了合规用户 ID、商品 ID 常被哈希或替换成自增编号手机号、地址等字段直接置空。这不影响协同过滤但影响基于内容的特征。我一般按四条规则处理第一纯 ID 类字段保留原值不做任何编码转换因为哈希后的 ID 本身已经是离散标识第二数值型缺失用-1填充并额外加一个is_missing标志列比填均值更安全第三类别型缺失统一填__MISSING__作为一个独立类别第四文本型字段如果缺失率超过 80%直接整列删除不要试图用模型补全。def clean_features(df): for col in df.columns: if df[col].dtype object: df[col] df[col].fillna(__MISSING__) elif pd.api.types.is_numeric_dtype(df[col]): df[f{col}_is_missing] df[col].isna().astype(int) df[col] df[col].fillna(-1) return df这段逻辑的核心是“缺失本身也是信息”。在风控场景里一个用户没有填写年龄可能比填了 25 岁更有区分度。加is_missing列的成本很低但经常能带来几个千分点的 AUC 提升。3.2 构造 RFM 和行为序列两类基础特征RFMRecency、Frequency、Monetary是电商数据集最通用的用户特征但很多人算错 Recency 的基准点。正确做法是以“数据集内最大时间”为基准而不是当前系统时间否则离线训练和线上推理的基准不一致。snapshot_date orders[order_time].max() rfm orders.groupby(user_id).agg( recency(order_time, lambda x: (snapshot_date - x.max()).days), frequency(order_id, nunique), monetary(amount, sum) ).reset_index() # 行为序列特征统计用户最近 7 天的点击、加购、下单次数 behavior[date] behavior[event_time].dt.date recent behavior[behavior[event_time] snapshot_date - pd.Timedelta(days7)] seq_feat recent.groupby([user_id, event_type]).size().unstack(fill_value0) seq_feat.columns [frecent7_{c} for c in seq_feat.columns]参数说明snapshot_date必须从数据里取不能写死。pd.Timedelta(days7)的窗口长度可以根据业务调整快消品用 7 天耐用品用 30 天。unstack(fill_value0)保证没有行为的用户也有 0 值而不是 NaN。3.3 用 LightGBM 跑一个最小可用版本验证数据质量特征宽表建好后不要急着上深度学习。先用 LightGBM 跑一个二分类任务比如预测用户未来 7 天是否下单如果 AUC 连 0.6 都不到说明数据清洗或特征构造有严重问题换模型也没用。import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score X wide_table.drop(columns[user_id, label]) y wide_table[label] X_train, X_val, y_train, y_val train_test_split(X, y, test_size0.2, random_state42) model lgb.LGBMClassifier( n_estimators500, learning_rate0.05, max_depth6, num_leaves31, subsample0.8, colsample_bytree0.8, random_state42 ) model.fit(X_train, y_train, eval_set[(X_val, y_val)], eval_metricauc, callbacks[lgb.early_stopping(50)]) pred model.predict_proba(X_val)[:, 1] print(Validation AUC:, roc_auc_score(y_val, pred))关键参数early_stopping(50)防止过拟合num_leaves31控制树复杂度subsample和colsample_bytree做行采样和列采样。如果 AUC 在 0.7 到 0.8 之间说明数据质量合格可以进入调参和特征迭代阶段。如果低于 0.65优先检查标签泄漏比如特征里混入了未来信息和时间对齐错误。4. 避坑与排查B2C 数据集最常见的五类翻车现场4.1 标签泄漏用下单后的行为预测下单现象离线 AUC 高达 0.95上线后效果暴跌。原因特征构造时把pay_time之后的行为也算进了窗口或者把订单金额直接作为特征去预测是否下单。解决所有特征的时间戳必须严格小于标签事件的时间戳用event_time label_time做硬过滤并在特征表里保留一列feature_cutoff_time用于审计。4.2 用户 ID 跨表不一致现象用户表有 10 万用户行为表只有 8 万订单表只有 5 万join 后大量丢失。原因不同表的用户 ID 生成规则不同有的是注册 ID有的是设备 ID有的是哈希后的 ID。解决先做 ID 映射表用手机号或邮箱等稳定标识做桥接如果没有稳定标识只能以行为表的 ID 为主键接受用户表覆盖不全的现实。4.3 时间戳单位混用导致序列错乱现象行为序列模型训练时 loss 不下降或者推荐结果明显违背时序。原因部分表用秒级时间戳部分用毫秒级直接比较大小会把 2023 年的记录排到 2021 年前面。解决统一用pd.to_datetime转换后检查min()和max()是否在合理范围内毫秒级时间戳转完后年份如果是 1970 年附近说明单位判断错了。4.4 爬虫流量污染行为数据现象某些商品的点击量异常高但转化率为零或者同一用户 ID 在 1 秒内产生上百次点击。原因数据集里混入了爬虫或压测流量。解决按用户维度统计点击频率超过阈值比如每秒 10 次的会话标记为异常并剔除同时检查user_agent字段如果有是否包含 bot 关键词。4.5 类别特征基数爆炸现象LightGBM 训练时内存溢出或者类别特征编码后维度上万。原因商品 ID、店铺 ID 这类高基数类别直接做 one-hot。解决高基数类别用目标编码target encoding或频率编码替代 one-hotLightGBM 原生支持categorical_feature参数但基数超过 1000 时建议先做频次过滤把出现次数少于 10 次的类别归为__RARE__。5. 从离线验证到线上可用一个可复现的评估习惯5.1 用时间切分代替随机切分做验证电商数据集的分布随时间漂移随机切分会让验证集里混入未来信息导致 AUC 虚高。我一般用“前 80% 时间做训练后 20% 时间做验证”的切分方式并且验证集和训练集之间留 1 天的 gap模拟线上“用历史预测未来”的真实场景。cutoff orders[order_time].quantile(0.8) train wide_table[wide_table[snapshot_time] cutoff] val wide_table[wide_table[snapshot_time] cutoff pd.Timedelta(days1)]参数说明quantile(0.8)按时间分位切分比固定日期更适应数据量变化。gap设为 1 天是为了避免特征窗口和标签窗口重叠。如果数据量足够可以做多轮滚动验证每轮往后推 7 天看 AUC 的方差是否稳定。5.2 监控特征分布漂移的三个指标上线后模型效果下降八成是特征分布变了。不需要复杂的监控系统先盯三个指标特征均值偏移PSI、空值率变化、类别特征的新增类别占比。PSI 超过 0.2 就告警空值率突然升高说明上游数据管道有问题新增类别占比超过 5% 说明业务侧有新品或新活动。指标计算方式告警阈值常见原因PSI分箱后计算分布差异 0.2促销活动、季节变化空值率当前空值数 / 总数环比上升 50%上游埋点故障新增类别占比新类别样本数 / 总样本数 5%新品上架、类目调整5.3 一个我踩过的坑别用全量数据做目标编码目标编码target encoding在电商数据集里很好用但必须只在训练集上拟合编码映射再应用到验证集和测试集。我有一次偷懒在全量数据上算了商品的平均转化率结果验证 AUC 比真实线上高出 8 个点上线后直接翻车。正确做法是用KFold在训练集内部做交叉编码或者用平滑后的贝叶斯编码。这个教训让我后来养成了一个习惯任何涉及标签的统计量先问一句“这个数字在预测时能不能拿到”拿不到就老老实实做交叉。希望帮到你。本文还有配套的精品资源点击获取
返回列表