news 2026/5/27 15:25:34

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
3.电商订单数据清洗:从脏数据到准确反映业务事实

#pandas数据清洗

第1章 为什么电商订单数据需要清洗

1.1 真实订单数据有多“脏”

电商订单数据的常见“脏数据”问题:

  • 缺失值:订单金额为空、用户ID缺失

  • 重复值:同一个订单号出现多次(系统重复导出)

  • 异常值:金额为负数(退款订单)、金额超大(测试订单)

  • 格式错误:日期是文本、金额带符号

数据清洗的目标不是“删除所有有问题数据”,而是“让数据准确反映业务事实”。

1.2 学习前的准备工作

步骤1:生成一份包含脏数据的测试文件

在Jupyter中运行以下代码,生成dirty_orders.csv

import 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, n+1)], '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', periods=n, freq='H') }) # 人为制造脏数据 # 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_index=True) # 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', index=False) 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:指定检查哪些列,不写则检查所有列

  • how='any':只要有一个缺失就删除(默认)

  • how='all':全部缺失才删除

  • 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

  • 文本列:众数、‘未知’

  • 时间列:前向填充method='ffill'或后向填充method='bfill'

⚠️实操避坑提醒:不要盲目用均值填充。比如“已取消”的订单,金额缺失应该填0而不是平均值,否则会虚增GMV。要根据业务逻辑决定填充值。

第3章 重复值处理

3.1 识别重复值

电商场景:同一订单号出现多次,需要去重。

# 检查是否有重复订单号 print(df.duplicated(subset=['order_id']).sum()) # 查看重复的具体行 df[df.duplicated(subset=['order_id'], keep=False)].sort_values('order_id')

3.2 删除重复值:drop_duplicates()

# 按订单号去重,保留第一次出现 df_clean = df.drop_duplicates(subset=['order_id'], keep='first') print(f"去重后行数:{len(df_clean)}")

参数说明

  • subset:按哪些列判断重复

  • keep='first':保留第一条;'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或大于Q3+1.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()

电商场景:订单日期列混用了日期格式和文本格式。

# 尝试转换,无法转换的会变成NaT(Not a Time) df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce') # 查看哪些行转换失败 df[df['order_date'].isnull()]

参数说明

  • errors='coerce':无效值转为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'], errors='coerce')

我的踩坑经历:有一次用pd.to_datetime转换50万行日期,没加errors='coerce',结果遇到一个“2025-02-30”的脏数据,整个脚本报错中断。加上errors='coerce'后,坏数据变成NaT,不影响其他行,后续再单独处理。

第6章 综合实操案例:完整清洗流程

6.1 案例背景

拿到dirty_orders.csv,需要清洗成可用于分析的标准数据。清洗规则:

  1. 删除订单号为空的记录

  2. 按订单号去重,保留第一次出现

  3. 金额为负数的,如果是“已取消”订单改为0,否则标记待查

  4. 金额超过10000的订单,先标记(可能是测试订单)

  5. 将订单日期转为datetime格式

  6. 保存清洗后的数据

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'], keep='first') 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', index=False)

步骤5:处理金额超大的异常值

# 标记超大的订单 df['is_abnormal'] = df['amount'] > 10000 print(f"超大订单数:{df['is_abnormal'].sum()}")

步骤6:日期格式转换

df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce') # 检查转换失败的行 if df['order_date'].isnull().any(): print("部分日期转换失败,已转为NaT")

步骤7:保存清洗后的数据

df.to_csv('cleaned_orders.csv', index=False) 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参数,报错中断errors='coerce'
数据类型转换前不确认原格式先用dtype查看,再转换

7.2 电商数据合规提示

清洗过程中的数据保护

  • 不要在清洗日志中打印完整的用户ID、手机号

  • 如果必须保存异常数据供复核,对敏感字段做脱敏处理

原始数据保留

  • 清洗前必须备份原始数据,清洗后保留清洗脚本(代码),方便追溯

  • 不要在原文件上直接修改

异常值上报

  • 金额为负、超大等异常,可能是系统问题或人为错误,清洗后应向数据负责人反馈,从源头解决问题

第8章 结语

数据清洗占电商数据分析60%以上的时间。掌握Pandas的缺失值、重复值、异常值处理和数据类型转换,你就能高效地把脏数据变成干净的分析素材。

下一章我会讲「Pandas数据转换与聚合」,教你如何用groupby和pivot_table做多维度统计。

有问题的评论区留言,我看到会回复。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 1:52:28

macOS下OpenClaw一键安装指南:对接千问3.5-35B-A3B-FP8实现本地自动化

macOS下OpenClaw一键安装指南&#xff1a;对接千问3.5-35B-A3B-FP8实现本地自动化 1. 为什么选择OpenClaw千问3.5组合&#xff1f; 去年我在整理个人知识库时&#xff0c;每天要重复执行几十次"截图→OCR识别→分类归档"的操作。直到发现OpenClaw这个能直接操控鼠标…

作者头像 李华
网站建设 2026/5/23 1:52:38

Ubuntu 18.04用户必看:如何彻底清理snapd及其残留的/dev/loop设备

Ubuntu 18.04系统瘦身指南&#xff1a;深度清理snapd与loop设备全攻略 每次打开终端输入df -h&#xff0c;那一长串/dev/loop设备列表是否让你感到不适&#xff1f;作为Ubuntu 18.04用户&#xff0c;你可能已经注意到这些神秘设备正在悄悄吞噬你的系统资源。今天我们就来彻底解…

作者头像 李华
网站建设 2026/5/27 0:12:42

本源量子开发工具链全解析:从QPanda到VQNet,构建量子计算生态

&#x1f527;掌握QPanda、pyQPanda、VQNet、Qurator&#xff0c;一站式量子软件开发体验量子计算的硬件发展日新月异&#xff0c;但要让算法真正落地&#xff0c;离不开易用、高效、功能完备的软件开发工具。本源量子作为国内量子计算领域的先行者&#xff0c;打造了一套完整的…

作者头像 李华
网站建设 2026/5/23 1:52:38

OpenClaw跨平台方案:Qwen3-14B在Windows与Mac双端部署

OpenClaw跨平台方案&#xff1a;Qwen3-14B在Windows与Mac双端部署 1. 为什么需要跨平台方案 去年我接手了一个跨团队协作项目&#xff0c;团队成员分别使用Windows和macOS系统。当时我们尝试用传统自动化工具实现文档同步和数据处理&#xff0c;结果发现不同系统下的路径分隔…

作者头像 李华
网站建设 2026/5/22 19:55:12

Second-Me:一款助力多领域协作的开源软件探索

Second-Me&#xff1a;一款助力多领域协作的开源软件探索 在当今数字化快速发展的时代&#xff0c;开源软件已成为推动技术创新与协作的重要力量。它们不仅促进了知识的共享&#xff0c;还为开发者、研究者及各行各业的专业人士提供了灵活、可定制的解决方案。在众多开源项目中…

作者头像 李华