3.电商订单数据清洗:从脏数据到准确反映业务事实

张开发
2026/6/8 6:34:24 15 分钟阅读
3.电商订单数据清洗:从脏数据到准确反映业务事实
#pandas数据清洗第1章 为什么电商订单数据需要清洗1.1 真实订单数据有多“脏”电商订单数据的常见“脏数据”问题缺失值订单金额为空、用户ID缺失重复值同一个订单号出现多次系统重复导出异常值金额为负数退款订单、金额超大测试订单格式错误日期是文本、金额带符号数据清洗的目标不是“删除所有有问题数据”而是“让数据准确反映业务事实”。1.2 学习前的准备工作步骤1生成一份包含脏数据的测试文件在Jupyter中运行以下代码生成dirty_orders.csvimport pandas as pd import numpy as np np.random.seed(42) n 1000 # 制造脏数据 df_dirty pd.DataFrame({ order_id: [ORD str(i).zfill(6) for i in range(1, n1)], user_id: np.random.randint(10000, 99999, n), amount: np.random.uniform(10, 2000, n).round(2), status: np.random.choice([已支付, 已取消, 已完成], n, p[0.7, 0.1, 0.2]), order_date: pd.date_range(2025-01-01, periodsn, freqH) }) # 人为制造脏数据 # 1. 缺失值10个订单的amount为空 df_dirty.loc[10:19, amount] np.nan # 2. 缺失值5个订单的order_id为空 df_dirty.loc[20:24, order_id] np.nan # 3. 重复值复制最后5行造成重复订单号 df_dirty pd.concat([df_dirty, df_dirty.tail(5)], ignore_indexTrue) # 4. 异常值金额为负数模拟退款单未过滤 df_dirty.loc[30:34, amount] -np.random.uniform(50, 500, 5) # 5. 异常值金额超大测试订单 df_dirty.loc[35:39, amount] np.random.uniform(10000, 50000, 5) # 6. 日期格式错误将部分日期转为文本 df_dirty.loc[40:49, order_date] df_dirty.loc[40:49, order_date].dt.strftime(%Y%m%d) df_dirty.to_csv(dirty_orders.csv, indexFalse) print(已生成 dirty_orders.csv共1005行含5行重复)⚠️实操避坑提醒数据清洗前一定要备份原始数据。我当初有一次直接在原DataFrame上操作删错了行想恢复已经来不及。建议先复制df_clean df.copy()所有清洗操作在副本上做。电商数据合规提示清洗过程中如果涉及用户手机号、地址等敏感字段不要直接打印或导出。只处理必要的分析字段。第2章 缺失值处理2.1 识别缺失值电商场景订单金额列有空白需要找出哪些行缺失。import pandas as pd df pd.read_csv(dirty_orders.csv) print(df.isnull().sum())输出order_id 5 user_id 0 amount 10 status 0 order_date 0 dtype: int64更多方法df.isnull().sum() / len(df)计算缺失比例df[df[amount].isnull()]查看缺失值的具体行2.2 删除缺失值dropna()电商场景订单号为空的行无法追踪直接删除。# 删除order_id为空的行 df_clean df.dropna(subset[order_id]) print(f删除后行数{len(df_clean)})参数说明subset指定检查哪些列不写则检查所有列howany只要有一个缺失就删除默认howall全部缺失才删除thresh至少有几个非空值才保留2.3 填充缺失值fillna()电商场景金额缺失但订单状态是“已取消”金额应为0。# 先筛选出已取消且金额缺失的行填充0 df.loc[(df[status] 已取消) (df[amount].isnull()), amount] 0 # 剩余金额缺失的行可以用平均值填充谨慎使用 mean_amount df[amount].mean() df[amount] df[amount].fillna(mean_amount)常用填充策略数值列中位数、均值、0文本列众数、‘未知’时间列前向填充methodffill或后向填充methodbfill⚠️实操避坑提醒不要盲目用均值填充。比如“已取消”的订单金额缺失应该填0而不是平均值否则会虚增GMV。要根据业务逻辑决定填充值。第3章 重复值处理3.1 识别重复值电商场景同一订单号出现多次需要去重。# 检查是否有重复订单号 print(df.duplicated(subset[order_id]).sum()) # 查看重复的具体行 df[df.duplicated(subset[order_id], keepFalse)].sort_values(order_id)3.2 删除重复值drop_duplicates()# 按订单号去重保留第一次出现 df_clean df.drop_duplicates(subset[order_id], keepfirst) print(f去重后行数{len(df_clean)})参数说明subset按哪些列判断重复keepfirst保留第一条last保留最后一次False删除所有重复电商场景如果有多列组合判断重复如订单号商品ID可以传入列表df.drop_duplicates(subset[order_id, product_id])3.3 我的踩坑经历有一次去重后订单数比预期少了100单。我以为是系统重复导出后来发现是Excel导出时同一个订单号在不同行后面多了空格“ORD001”和“ORD001 ”Pandas认为是不同的。解决方案先清理空格df[order_id] df[order_id].str.strip()第4章 异常值处理4.1 识别异常值电商场景订单金额为负数退款单或超大测试订单。# 查看金额的统计分布 print(df[amount].describe()) # 找出金额为负的行 negative df[df[amount] 0] print(f金额为负的订单数{len(negative)}) # 找出金额大于10000的行 too_high df[df[amount] 10000] print(f金额超大的订单数{len(too_high)})其他异常值识别方法箱线图IQR法则小于Q1-1.5IQR或大于Q31.5IQR业务规则如金额不能超过商品单价×合理数量4.2 处理异常值场景一金额为负数退款订单如果订单状态是“已取消”或“退款中”金额应改为0已支付的不计入GMV。# 假设退款订单状态为已取消将金额改为0 df.loc[(df[amount] 0) (df[status] 已取消), amount] 0场景二金额超大测试订单测试订单通常有特殊标识可以直接删除。# 删除金额大于10000且状态为异常的订单 df df[~((df[amount] 10000) (df[status] 测试))] # 或者直接删除所有金额10000的行根据业务判断 df df[df[amount] 10000]场景三金额超出合理范围用百分位法截断保留1%-99%分位数。lower df[amount].quantile(0.01) upper df[amount].quantile(0.99) df df[(df[amount] lower) (df[amount] upper)]⚠️实操避坑提醒不要一刀切删除所有异常值。比如双11大促订单金额超过10000可能是真实的需要单独确认。建议先标记异常值再逐类分析。第5章 数据类型转换5.1 转换数据类型astype()电商场景用户ID被读成了整数需要转为字符串。# 查看当前类型 print(df[user_id].dtype) # int64 # 转为字符串 df[user_id] df[user_id].astype(str) # 订单号确保是字符串 df[order_id] df[order_id].astype(str)5.2 日期转换to_datetime()电商场景订单日期列混用了日期格式和文本格式。# 尝试转换无法转换的会变成NaTNot a Time df[order_date] pd.to_datetime(df[order_date], errorscoerce) # 查看哪些行转换失败 df[df[order_date].isnull()]参数说明errorscoerce无效值转为NaT而不是报错format指定日期格式如format%Y%m%d可加快转换速度5.3 金额列清洗处理符号和文本电商场景金额列可能包含“¥”或“元”字。# 方法1用正则替换非数字字符 df[amount] df[amount].astype(str).str.replace(¥, ).str.replace(元, ) df[amount] pd.to_numeric(df[amount], errorscoerce)我的踩坑经历有一次用pd.to_datetime转换50万行日期没加errorscoerce结果遇到一个“2025-02-30”的脏数据整个脚本报错中断。加上errorscoerce后坏数据变成NaT不影响其他行后续再单独处理。第6章 综合实操案例完整清洗流程6.1 案例背景拿到dirty_orders.csv需要清洗成可用于分析的标准数据。清洗规则删除订单号为空的记录按订单号去重保留第一次出现金额为负数的如果是“已取消”订单改为0否则标记待查金额超过10000的订单先标记可能是测试订单将订单日期转为datetime格式保存清洗后的数据6.2 分步操作步骤1读取并备份import pandas as pd df pd.read_csv(dirty_orders.csv) df_original df.copy() # 备份 print(f原始行数{len(df)})步骤2删除订单号为空的记录df df.dropna(subset[order_id]) print(f删除空订单号后{len(df)})步骤3按订单号去重df df.drop_duplicates(subset[order_id], keepfirst) print(f去重后{len(df)})步骤4处理金额为负的异常值# 将金额为负且状态为已取消的改为0 df.loc[(df[amount] 0) (df[status] 已取消), amount] 0 # 检查还有没有其他负值 if (df[amount] 0).any(): print(警告仍有负金额订单请人工复核) df[df[amount] 0].to_csv(negative_amounts.csv, indexFalse)步骤5处理金额超大的异常值# 标记超大的订单 df[is_abnormal] df[amount] 10000 print(f超大订单数{df[is_abnormal].sum()})步骤6日期格式转换df[order_date] pd.to_datetime(df[order_date], errorscoerce) # 检查转换失败的行 if df[order_date].isnull().any(): print(部分日期转换失败已转为NaT)步骤7保存清洗后的数据df.to_csv(cleaned_orders.csv, indexFalse) print(清洗完成已保存到 cleaned_orders.csv)6.3 清洗结果验证# 验证各项指标 print(f最终行数{len(df)}) print(f金额范围{df[amount].min()} ~ {df[amount].max()}) print(f日期范围{df[order_date].min()} ~ {df[order_date].max()}) print(f缺失值情况\n{df.isnull().sum()})第7章 本章踩坑清单与合规总结7.1 新手常见踩坑场景错误操作正确做法缺失值直接删除所有含空值的行按列分析原因业务逻辑填充重复值去重前不检查keep参数确认保留第一次还是最后一次异常值一刀切删除先标记分类处理日期转换不设errors参数报错中断加errorscoerce数据类型转换前不确认原格式先用dtype查看再转换7.2 电商数据合规提示清洗过程中的数据保护不要在清洗日志中打印完整的用户ID、手机号如果必须保存异常数据供复核对敏感字段做脱敏处理原始数据保留清洗前必须备份原始数据清洗后保留清洗脚本代码方便追溯不要在原文件上直接修改异常值上报金额为负、超大等异常可能是系统问题或人为错误清洗后应向数据负责人反馈从源头解决问题第8章 结语数据清洗占电商数据分析60%以上的时间。掌握Pandas的缺失值、重复值、异常值处理和数据类型转换你就能高效地把脏数据变成干净的分析素材。下一章我会讲「Pandas数据转换与聚合」教你如何用groupby和pivot_table做多维度统计。有问题的评论区留言我看到会回复。

更多文章