资讯详情

B2C电商数据集清洗与RFM用户分层实战指南

📅 2026/10/8 16:45:13 | 华诺云谱 👁 阅读
B2C电商数据集清洗与RFM用户分层实战指南
简介国内某知名B2C电商平台的数据集面向数据分析初学者、电商运营及算法研究人员可用于用户行为分析、商品销量预测、客户画像构建等典型场景。压缩包共包含30个文件大小约14.83MB涵盖XML、CSV、SQL、DMP、PDF、XLS等多种格式既有便于导入关系型数据库的SQL和DMP结构也有通用性更强的CSV和XML数据配合PDF说明文档与Excel样例方便快速上手与二次处理。资源已有1590人学习数据内容涉及用户浏览、购买记录、商品信息、订单流水、客户属性及评价反馈等维度可支撑从数据清洗、特征工程到关联规则、聚类预测的完整分析流程。通过这套数据读者既能理解B2C平台的核心数据组织方式也能在真实业务字段上开展电商运营分析与建模实践是学习电商数据挖掘的实用素材。1. 这份国内 B2C 电商数据集值得你花十分钟拆开看做数据分析的人手里最缺的往往不是算法而是一份真实、能折腾的业务数据。这份国内某 B2C 电子商务网站的数据集以 .rar 形式打包解压后就是一套典型的电商数据集覆盖订单、商品、用户这类 B2C 业务核心内容跟网上那些已经清洗干净的样例完全不同它更接近企业数据仓库导出的原始状态脏值、空值、字段对不齐全都有。对正在练 SQL、想夯实数据清洗能力或者准备做电商方向简历项目的人来说这份电商数据集可以当一整条练习链路来用解压、摸结构、清洗、关联、分层分析每一步都有足够的坑让你踩。我拆过不少同类资源这篇把完整流程和血泪经验过一遍照着做基本能复现。2. 解压与目录摸底先看清这份 B2C 数据集的真实结构拿到 .rar 的第一步不是急着写分析代码而是把文件安全解压出来看清里面到底有几张表、什么格式、数据量多大。很多人栽在第一步用系统自带工具解压 rar 报错或者解压出来一堆乱码文件名后面全乱。我一般固定走 7-Zip 和 Python rarfile 两条路分别对付 Windows 和 Linux 服务器场景下面把两种姿势和探查目录的方法都过一遍。2.1 解压 .rar7-Zip 命令行与 Python 两种姿势Windows 下最常见的是装 7-Zip 然后右键解压这个没什么好说的。但如果文件多、或者你后面要反复重新解压命令行更省事。7-Zip 安装后默认在C:\Program Files\7-Zip\把这个目录加进 PATH然后# Windows 下用 7-Zip 解压到指定目录 7z x 国内某B2C电商数据集.rar -oD:\b2c_data -y参数说明x表示解压并保留压缩包内的目录结构-o后面紧跟输出目录注意-o和目录路径之间没有空格-y表示遇到覆盖询问全部选是适合批量操作。如果压缩包设了密码追加-p你的密码即可没设密码就不用管。这个命令跑完先看返回信息里有没有ERROR字样很多解压问题在这一步就能发现。Linux 服务器上则用 unrar 或 7z。unrar 是 RAR 协议的解压工具Debian/Ubuntu 系直接装# Linux 下安装并解压保留目录结构 sudo apt install unrar unrar x 国内某B2C电商数据集.rar ./b2c_data/这里的x同样表示保留目录结构./b2c_data/是输出目录注意最后那个斜杠不能省否则 unrar 会把它当文件名处理。解压时如果压缩包有密码终端会交互式提示输入unrar e和unrar x的区别是e会把所有文件平铺到当前目录不保留子目录我一般不用e避免不同表同名文件互相覆盖。还有一种场景是想在 Python 脚本里直接读 rar 内部的文件不想先落盘解压。这时用 rarfile 库import rarfile # 打开 rar 并列出内部文件清单 rar rarfile.RarFile(国内某B2C电商数据集.rar) print(rar.namelist()) # 打印压缩包内所有文件名和路径 # 直接读取内部某个 csv 的前 5 行适合快速预览 with rar.open(orders.csv) as f: for i, line in enumerate(f): if i 5: break print(line.decode(utf-8, errorsignore))rarfile 本身是纯 Python 封装但底层依赖系统里有 unrar 或 7z 可执行文件否则会抛RarCannotExec错误这是新手最容易卡住的地方。rar.namelist()能让你不解压就看到内部结构比盲目全量解压更稳尤其适合判断这个 rar 里到底是不是只有一份 csv还是有多个月份文件夹。2.2 文件清单与表结构推断先画 ER 图再动手解压之后第一件事是摸清楚有哪些表。B2C 电商数据集通常逃不出这几张订单主表、订单明细表、商品表、用户表有的还带类目表和评价表。这里要特别提醒区分订单主表和明细表两者是一对多关系很多人后面 join 出行数暴增就是没搞清这个。常见表名典型字段记录粒度作用orders订单主表order_id, user_id, order_time, pay_amount, status一单一记录订单级金额、状态、支付时间order_items订单明细order_id, product_id, quantity, price一行一商品一个订单拆成多行products商品表product_id, category_id, title, price一商品一记录商品静态属性users用户表user_id, register_time, city, gender, age一用户一记录用户画像属性拿到文件列表后我一般会先画一个三到四张表的 ER 草图明确主键和外键订单表通过user_id关联用户表通过product_id关联商品表订单明细表通过order_id挂到订单主表下面。这个步骤的作用是给后面的 join 定方向——哪些是事实表、哪些是维度表心里有数再写 SQL 才不会乱。画图不用工具纸上画几条线就行关键是确认每个关联键在各自表里是不是唯一的这是后面所有分析的地基。2.3 抽样探查用 pandas 快速判断数据质量文件格式大概率是 csv 或 tsv。读取之前先抽样看几行比直接全量读要稳得多。用 pandas 读 csv 时给nrows限制行数避免文件巨大时读半天import pandas as pd # 先只读前 100 行探查结构encoding 先试 utf-8报错再换 gbk df_orders pd.read_csv(orders.csv, nrows100, encodingutf-8) print(df_orders.shape) print(df_orders.dtypes) print(df_orders.head())如果encodingutf-8直接抛UnicodeDecodeError基本可以判断文件是 GBK/GB2312 编码改成encodinggbk重试这个在国内导出的数据里非常常见。先看dtypes的目的是确认金额列是不是被读成了 object、时间列是不是 object这两类是清洗大头第三章会展开。head()则是看字段名是否规范有没有带 BOM 的\ufeff前缀有的话用encodingutf-8-sig读。抽样只能看字段有没有看不全脏数据。真正动手前我再习惯用 shell 数一下总行数跟文件大小做个对应判断数据量级是几万还是几十万行# 统计订单文件总行数减去表头行就是记录数 wc -l orders.csv这一步决定后面用 pandas 全量跑还是分批跑。几十万行以内 pandas 随便扛上了百万行就要考虑用 chunk 分块读或者直接转 SQLite 再查别等到内存爆了才后悔。提示解压完成后先用du -sh看总大小再逐文件确认没有 0 字节的残缺文件这一步能避开很多「解压看似成功但表是空的」的坑。3. 数据清洗与字段对齐把订单、商品、用户三张表串起来数据摸完就该动手清洗了。B2C 数据的清洗工作九成集中在三个地方订单表的金额和时间、用户表的 ID 与去重、以及跨表关联时的键对齐。这三步做完后面不管是做 RFM 还是做销售漏斗底子才稳。很多人一上来就写 join 查数结果口径对不上回头发现是脏数据没清返工成本极高所以我把清洗放在关联前面讲。3.1 订单表清洗金额、时间、状态三个高频坑先看金额。从业务系统导出的订单表金额列常见两种脏值一种是带了货币符号和千分位分隔符比如¥1,299.00pandas 读进来直接变成 object 字符串一种是空值或者 0表示订单取消或者测试订单混进来了。前者要归一化成浮点数后者要决定是填充还是剔除。import pandas as pd import re df pd.read_csv(orders.csv, encodinggbk) def clean_amount(s): if pd.isna(s): return None # 去掉货币符号、千分位逗号和多余空格 s re.sub(r[¥,\s], , str(s)) try: return float(s) except ValueError: return None # 解析失败统一返回 None df[pay_amount_clean] df[pay_amount].map(clean_amount) df df.dropna(subset[pay_amount_clean]) # 金额解析失败的整行剔除 print(df[pay_amount_clean].describe())clean_amount里正则把人民币符号、千分位逗号和空白全部剥掉再转 float解析不了的置 None 最后丢行。describe()会给出 min、max、均值如果 min 出现负数或 0要警惕退款单和测试单混进去了这类单子做销售汇总时通常要过滤掉。注意这里的清洗函数只处理金额字符串如果原字段本身就是数字但混了少量文本用pd.to_numeric(..., errorscoerce)更直接。时间列的处理类似常见格式有2023-01-15 12:33:45和2023/1/15两种甚至同一列里混着来。用pd.to_datetime统一# 常见格式一带时分秒 df[order_time_dt] pd.to_datetime( df[order_time], format%Y-%m-%d %H:%M:%S, errorscoerce ) # 常见格式二斜杠日期跑批时分开处理再合并 df[order_time_dt2] pd.to_datetime( df[order_time], format%Y/%m/%d, errorscoerce ) df[order_time_dt] df[order_time_dt].fillna(df[order_time_dt2])errorscoerce是关键参数解析失败变成NaT而不是抛异常这样你能统计到底多少行对不上df[order_time_dt].isna().sum()。如果这个数超过 5%建议回源头确认是不是导出时有脏行混入。时间字段一旦统一成 datetime64后面按月、按日聚合就非常顺。两份格式都解析失败的行大概率是乱码或空壳数据可以直接标记后剔除。状态字段是第三类高频脏点。订单状态常见值是数字编码比如 1 待支付、2 已支付、3 已发货、4 已完成、5 已取消有的表里却是中文「已完成」「已退款」。我的习惯是建一个映射字典把中文和数字统一成一套标准编码# 状态映射中文和数字统一成标准编码 status_map { 1: 1, 待支付: 1, 未支付: 1, 2: 2, 已支付: 2, 待发货: 2, 3: 3, 已发货: 3, 配送中: 3, 4: 4, 已完成: 4, 交易成功: 4, 5: 5, 已取消: 5, 退款: 5, 已退款: 5, } df[status_cd] df[status].astype(str).map(status_map).fillna(-1)分析时只保留status_cd在 2、3、4 的行否则会把未支付订单算进销售额这是电商口径里最常见的错误。状态映射后如果出现大量-1说明源数据里有你没见过的状态值先df[status].value_counts()看全量枚举再补映射不要硬删。3.2 用户表和商品表的 ID 对齐与去重用户表最常见的坑是重复注册行。同一user_id出现两行可能是因为数据来自全量快照叠加增量也可能是注册信息更新产生了多版本。处理原则是先看重复行的字段差异如果只有注册时间不同而画像字段一致大概率是快照冗余直接去重保留最早一条如果画像字段都不同说明是数据回填产生的新版本保留最新一条。users pd.read_csv(users.csv, encodinggbk) # 按 user_id 去重先按注册时间排序保留每条记录的最新版本 users_dedup ( users .sort_values(register_time) .drop_duplicates(subset[user_id], keeplast) ) print(f去重前 {len(users)} 行去重后 {len(users_dedup)} 行)drop_duplicates的keeplast配合先sort_values实现保留每个 user_id 最新一条的意图。排序字段用注册时间还是数据更新时间取决于哪个更能代表「新版本」这点要根据实际字段来定不要盲抄。去重后务必再校验一次唯一性users_dedup[user_id].is_unique应为 True否则说明排序没有真正区分先后换排序字段重跑。商品表的对齐主要是确认订单明细里的product_id都能在商品表找到。找不到的通常有三种原因商品下架被物理删除、ID 格式不一致比如有的带前缀P、或者明细表里存的是 SPU 而商品表是 SKU。检验方法# 检查订单明细里的 product_id 在商品表里的匹配率 items pd.read_csv(order_items.csv, encodinggbk) products pd.read_csv(products.csv, encodinggbk) valid_pids set(products[product_id].astype(str)) items[product_id_str] items[product_id].astype(str) match_rate items[product_id_str].isin(valid_pids).mean() print(f商品匹配率: {match_rate:.2%})匹配率低于 95% 就要警觉。常见解决方式是先把两边 ID 都转成字符串再匹配避免 int 和 str 类型不匹配造成假性缺失如果转完还匹配不上看未匹配的样本长什么样比如商品表里是100023明细里是P100023用str.replace去掉前缀再匹配。这种 ID 格式问题在 B2C 数据集里出现频率非常高属于典型的字段对齐坑千万别上来就 inner join。3.3 构建分析宽表SQL 写法与执行参数清洗完单表下一步就是 join 成宽表。宽表的意义在于把分析要用的字段一次性备齐避免后面每次分析都重复 join也方便统一口径。我一般把订单明细作为事实表基准左关联订单主表拿金额和状态再关联用户表和商品表拿画像和类目-- 构建 B2C 分析宽表按订单明细行粒度 SELECT oi.order_id, oi.product_id, oi.quantity, oi.price AS item_price, o.order_time_dt, o.pay_amount_clean AS pay_amount, o.status_cd AS order_status, u.user_id, u.city, u.gender, p.category_id FROM order_items_clean oi LEFT JOIN orders_clean o ON oi.order_id o.order_id LEFT JOIN users_dedup u ON o.user_id u.user_id LEFT JOIN products_clean p ON oi.product_id p.product_id WHERE o.status_cd IN (2, 3, 4) -- 只保留已支付、已发货、已完成这里用 LEFT JOIN 而不是 INNER JOIN是因为要保留明细表里所有行关联不上的维度字段留空后续方便统计缺失率。WHERE 过滤掉未支付和已取消的订单保证销售额口径干净。执行时注意order_items_clean、orders_clean这些表或视图要先建好可以分步执行验证每步行数不要一把梭堆在一个长 SQL 里出错了不好定位。宽表建好后行数应当等于订单明细清洗后的行数。如果 join 后行数翻倍优先怀疑订单主表有重复order_id回第四章 4.4 按那个方法排查。宽表字段命名建议统一带前缀pay_amount和item_price就能一眼看出是订单级还是明细级后面写分析 SQL 不容易混。注意宽表粒度想清楚再建。你要的是「订单明细行」粒度还是「订单」粒度决定了 join 基准表和 group by 的写法这一步定了后面所有分析都受它约束。4. 避坑指南RAR 解压与数据探查的五个翻车现场这章把我实际拆数据时反复踩过的坑整理成五条每条按「现象 → 原因 → 解决」的结构写照着一一排查能省下大量时间。这些坑单独看都不大但串起来足够让一个下午报废值得提前打预防针。4.1 现象rar 解压报错且反复中断解压到一半报 CRC 错误或者 7-Zip 直接弹「文件已损坏」并终止。原因通常是压缩包下载不完整尤其是通过浏览器或下载工具断点续传出问题本地文件的大小和发布页标注不一致。解决方式先对比压缩包大小不一致就重下如果大小一致仍报错用 unrar 的测试模式确认损坏范围# 只测试压缩包完整性不真正解压 unrar t 国内某B2C电商数据集.rarunrar t会逐个文件校验 CRC输出里会明确告诉你哪个文件坏了。如果只是个别文件损坏可以只解压完好的部分继续用但涉及坏文件的分析结论要慎重下数据不完整时宁可不分析那一块也别用残缺文件硬出结论。另外下载工具多线程也会导致 rar 包静默损坏我后来都强制单线程下载这一类关键资源。4.2 现象解压后文件名全是乱码Windows 下解压出来的 csv 文件名是「绠▼」这种乱码。原因是压缩包在 Linux 或 macOS 下打包时使用了 UTF-8 文件名编码而 Windows 下部分解压工具按本地 ANSI 码表GBK解析了文件名。解决方式用 7-Zip 打开压缩包时在「选项 → 编码」里手动指定文件名编码为 UTF-8或者解压后用批量改名工具修正。这个坑不影响文件内容但会影响脚本里的文件路径引用乱码文件名在 Python 里极容易写错建议尽早统一改成英文拼音文件名省得后面 path 各种踩坑。4.3 现象read_csv 直接报 UnicodeDecodeErrorpandas 读 csv 抛UnicodeDecodeError或者读出来全是乱码。原因大都是文件是 GBK/GB2312 编码而你默认用了 utf-8。解决方式是换encodinggbk这个在第 2.3 节提过。还有一个更稳的办法是先用二进制读文件自己判断# 先读文件头 200 字节肉眼判断编码特征 with open(orders.csv, rb) as f: raw f.read(200) print(raw[:50])如果输出里出现\xd6\xd0这类高位字节基本是 GBK 编码。也可以引入 chardet 自动检测但国内系统导出的数据九成在 GBK 和 UTF-8 二选一直接试这两个就够了别为了一个编码问题给环境塞一堆依赖。顺带一提读出乱码时先看是不是字段值乱码如果只有表头乱码多半是 BOM 问题用utf-8-sig读。4.4 现象用户表和订单表 join 之后行数暴增LEFT JOIN 后结果行数比订单明细多了两三倍甚至更多。原因多半是关联键有重复用户表去重没做干净或者订单主表里同一个order_id有多行两边的重复叠加形成笛卡尔放大。解决方式是先分别对两张表做分组计数找出重复键-- 查看订单主表是否存在重复 order_id SELECT order_id, COUNT(*) AS cnt FROM orders_clean GROUP BY order_id HAVING COUNT(*) 1 LIMIT 20;查出重复键后按第三章的去重策略处理再重新 join。这个坑的根因是没在 join 前做键的唯一性校验属于我见过最多的翻车现场。现在我每次写 join 前都会先跑一遍这个 HAVING 检查成为肌肉记忆了。另一个隐蔽情况是 JOIN 条件写错比如漏了oi.product_id p.product_id只留了order_id关联行数也会异常膨胀SQL 写好先 explain 看看行数预期。4.5 现象金额求和后数值大得离谱pay_amount加总之后比业务常识高几个数量级或者出现让人摸不着头脑的负数。原因往往是金额单位不统一有的行是元有的行是分或者导出时把 quantity 和 price 乘出来的明细混进了主表金额。解决方式# 看一下金额字段的极值和分位判断是否单位混用 print(df[pay_amount_clean].describe()) print(df[pay_amount_clean].min(), df[pay_amount_clean].max())如果 max 是亿级而中位数只有几百基本可以判定单位混用把明显异常的记录按分位过滤或除以 100 统一成分转元再聚合。如果 min 出现负数确认是不是退款记录。退款是真实业务的一部分做净销售额时要单独处理成负数参与汇总不要直接删行否则口径对不上财务数据。5. 把 B2C 数据盘活RFM 用户分层与复购率的落地脚本数据清洗干净、宽表建好之后这份 B2C 数据集的价值才开始显现。我建议从 RFM 用户分层做起它是电商数据分析里投入产出比最高的一件事也是面试时最能讲清楚的实战项目。RFM 三个字母分别对应最近一次下单时间Recency、下单频次Frequency和累计消费金额Monetary组合起来就能给用户打标签。5.1 RFM 打分脚本直接拿第三章的宽表wide聚合出每个用户的三个指标import pandas as pd # 按用户聚合得到 R/F/M 三个原始指标 rfm wide.groupby(user_id).agg( last_order(order_time_dt, max), freq(order_id, nunique), monetary(pay_amount, sum) ).reset_index() # R 指标最近下单日期距离观测日期的天数越小越活跃 obs_date pd.Timestamp(2023-06-30) # 取数据截止日期别用今天 rfm[recency] (obs_date - rfm[last_order]).dt.days # 用分位数打分R 越小分越高F/M 越大分越高 rfm[R_score] pd.qcut(rfm[recency], 4, labels[4, 3, 2, 1]) rfm[F_score] pd.qcut(rfm[freq].clip(lower1), 4, labels[1, 2, 3, 4]) rfm[M_score] pd.qcut(rfm[monetary], 4, labels[1, 2, 3, 4])注意两点obs_date一定要用业务截止日期而不是datetime.today()否则今天跑和明天跑结果不一样报告没法复现freq用nunique统计去重后的订单数避免一个订单多行明细被重复计数。pd.qcut遇到频次全为 1 时会报错所以先clip(lower1)兜底。5.2 用户分层与复购率打分之后把三个分数拼成组合按规则分层# 组合打分结果转成易于分层的整数 rfm[R] rfm[R_score].astype(int) rfm[F] rfm[F_score].astype(int) rfm[M] rfm[M_score].astype(int) # 经典分层规则重要价值客户 高二高二高 def rfm_label(row): if row[R] 3 and row[F] 3 and row[M] 3: return 重要价值客户 if row[R] 3 and row[F] 3: return 重要发展客户 if row[R] 3 and row[M] 3: return 重要唤回客户 if row[R] 3 and row[F] 3 and row[M] 3: return 一般挽留客户 return 普通客户 rfm[user_seg] rfm.apply(rfm_label, axis1) print(rfm[user_seg].value_counts())分层之后按用户段聚合看人数和金额占比哪类客户贡献了大部分销售额就一清二楚了。复购率是另一个很好算的指标宽表里直接统计下单次数大于等于 2 的用户占比# 复购率 下单次数 2 的用户数 / 总下单用户数 user_orders wide.groupby(user_id)[order_id].nunique() repeat_rate (user_orders 2).mean() print(f复购率: {repeat_rate:.2%})这组脚本跑完你就有了一个「用户分层 复购率」的完整分析闭环可以直接写进简历项目。从那以后我每次拿到陌生的电商数据集第一件事永远是先看 order_id 的唯一性和金额口径确认这两点没问题才开始做业务分析这个习惯帮我避掉了大量返工。把 RFM 这套跑通这份 B2C 数据集才算真正吃透了希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑