news 2026/9/7 15:30:59

Pandas数据清洗与分组聚合实战:从电商订单看分析全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pandas数据清洗与分组聚合实战:从电商订单看分析全流程

说实话,拿到作业3.7这个编号的时候,我第一反应是“课程又留常规练习了”,结果把题目完整读了三遍才发现,这道题表面上是Python数据处理练习,实际把数据清洗、字段理解、分组聚合、可视化分析全揉在了一起。更关键的是,它要求的不是“跑通代码”,而是“讲清楚每一步为什么这么做”,这就完全不是抄一段Pandas代码能交差的了。

这道作业的场景非常典型:一份电商订单CSV,约一万行,字段包含订单号、用户ID、下单时间、订单金额、商品类别、支付状态等。任务分四问:按类别统计销售额排行、分析月度销售趋势、计算支付状态占比、找出金额异常的订单并给出排查结论。看起来每问都很基础,但真正动手做的时候,你会发现坑全藏在数据本身的脏乱差里。

这篇文章我就完整复盘一下我当时是怎么拆解任务、怎么写代码、在哪几个地方差点翻车,以及最后从作业里沉淀下来的通用处理套路。不管你是在上网课、做训练营作业,还是工作中第一次接手数据清洗的活儿,这套思路应该都能直接拿来用。

1. 作业任务拆解与整体设计思路

1.1 先把“表面需求”翻译成“技术动作”

我拿到题的第一件事不是打开IDE,而是拿笔在纸上把这四问翻译成具体的技术操作。这个过程非常关键,因为题目里的“分析销售额排行”到了代码层面其实是“先做类别分组,再对金额字段聚合求和,最后排序”这么一串动作。如果一上来就写代码,很容易写着写着发现漏掉了某个隐含条件。

我当时拆出来的技术动作是这样的:

  • 统计各品类销售额与订单量排行,对应groupby('类别')后分别对订单金额sum、对订单号count,再排序。
  • 分析月度销售趋势,对应先把下单时间转成标准日期格式,再提取year-month,按月分组求和。
  • 计算支付状态占比,对应value_counts(normalize=True)
  • 找金额异常的订单,对应要做描述性统计,结合quantilez-score来判断离群点。

这么一拆就发现,整道题其实在训练三个核心能力:字段理解、数据清洗、聚合口径的选择。多数人卡住的地方根本不在分组聚合本身,而是前面数据没洗干净,或者时间字段解析出了错,导致后面所有结果全偏。

1.2 为什么选Pandas而不是Excel或SQL

有些同学习惯用Excel做这类分析,因为点几下鼠标就能出透视表,但作业的隐性要求是“写出可复现的处理流程”,这意味着每一步操作都得有记录。Excel在数据量小的时候确实方便,但一涉及时间格式清洗、异常值筛查这种需要写逻辑的操作,它就变得很笨拙,而且很难追溯过程。

SQL当然也能做分组聚合,但这类课程作业通常更希望你掌握DataFrame的处理方式,因为后续做机器学习特征工程时,Pandas是绕不开的。从我的习惯来说,这类“一撮数据、四问分析、需要反复看中间结果”的任务,Pandas的DataFrame对象是最顺手的工具,每一行代码都是一个可独立验证的步骤,中间结果随时能head()出来检查,出问题时定位非常快。

另外还有一个关键考量:Pandas处理一万行的数据几乎无延迟,可以高频地“写完一步、跑一步、看一眼结果”,这种交互感能让你及时发现数据质量问题。而SQL虽然也能做,但改一次口径就要重写一段查询,不如DataFrame里直接新增一列来得直观。

2. 数据读取与结构探查:动手写代码前必须干的三件事

2.1 环境准备与数据导入

我先说明一下我这边的运行环境:Python 3.10 + Pandas 2.0 + Matplotlib 3.7,Jupyter Notebook里跑的。之所以用Notebook而不是直接写.py脚本,是因为作业本身分四个问题,Notebook的单元格天然适合分段执行、分段验证结果。

导入阶段建议直接写这两行:

import pandas as pd import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文乱码 plt.rcParams['axes.unicode_minus'] = False # 解决负号显示异常

这两行配置是做中文可视化的标配,不写的话图表里的中文会全部变成方框。第一次运行时我就吃过这个亏,导出的图里“销售额”三个字全变成了乱码,白白浪费了十分钟。如果你用的不是Windows系统,SimHei这个字体可能不存在,那就得换成你系统里实际的中文字体,比如Arial Unicode MSWenQuanYi Zen Hei

接着读取数据:

df = pd.read_csv('orders.csv') print(df.shape) print(df.columns.tolist()) print(df.head(10).to_string())

读进来之后第一步永远先看形状和列名。我当时看到的是(10485, 7),这个行数比题目说的一万行略多,说明数据里很可能混进了脏数据。列名分别是:订单号用户ID下单时间订单金额商品类别支付状态收货城市。这个字段结构很典型,接下来的所有操作都会围绕这些列展开。

2.2 字段类型与数据质量初检:一切统计的口径前提

df.info()看字段类型这一步,看似普通,其实价值极高。我当时的运行结果里有两个非常关键的发现:

  • 下单时间object类型,这是正常的,因为原始CSV里的时间就是字符串,但后续做趋势分析必须转成datetime64
  • 订单金额虽然看起来是数值,但实际检查后发现里面混了少量带“元”字样的脏数据,比如"89元""120.5元"这种,这会让整列变成object类型。如果直接sum,结果报错或者得到一串诡异的字符串拼接结果。

所以我的建议是两步走:先用df.dtypes快速扫描,再用df.isnull().sum()查看缺失情况。这两个操作加起来不到五秒钟,但能避免后面所有计算踩坑。

还有一项初检是看看是否有完全重复的行:

print(df.duplicated().sum())

我查到有13行完全重复的数据,这属于典型的“数据录入两次”的情况,后面统一去重即可。这类细节如果不在探查阶段发现,做到后面某个统计数字怎么都对不上时,你会很难想起来根因是重复行。

3. 核心实现:数据清洗与特征工程全流程

3.1 清洗第一关:金额字段的“去脏”与类型转换

数据清洗是整个作业里耗时最长的环节,也是最能拉开分差的部分。以订单金额为例,我当时的原始数据里金额列既有纯数字,也有带“元”后缀的字符串,还有少数几条是空白。处理逻辑并不复杂,但要分好几步才能做得干净。

我的处理顺序是这样的:

# 1. 去掉金额字段中的"元"字样,统一转为字符串 df['订单金额'] = df['订单金额'].astype(str).str.replace('元', '', regex=False) # 2. 将空字符串转为缺失值,便于统一处理 df['订单金额'] = df['订单金额'].replace('', pd.NA) # 3. 转成数值类型,无法转换的变成NaN df['订单金额'] = pd.to_numeric(df['订单金额'], errors='coerce')

pd.to_numeric这个函数的errors='coerce'参数是这段代码的灵魂,它会把无法转换的内容统一置为NaN,而不是直接让程序崩溃。转换结束后,我又检查了一次缺失情况,发现多了14个NaN,这些就是原本带“元”后缀和真正空白的记录加在一起的量。

清理掉脏字符之后,还要再做一个合理性边界检查:订单金额理论上应该是正数。我扫了一遍发现最低值是-9.9,这种负数订单通常是退款或者测试订单,但作业题意是分析销售数据,这类负值会严重干扰聚合结果。我的处理是先把边界情况都打印出来看一下,确认是退款单后,在本次分析中先过滤掉。

3.2 清洗第二关:时间字段解析与业务特征提取

时间字段的清洗是这道作业里最容易出错的地方,因为原始数据里的时间格式杂乱到让人头大。我扫了一遍发现至少存在三种格式:2024/01/15 09:23:112024-01-15 09:232024年1月15日。如果不统一成一种格式,之后做月份提取就只能得到一堆垃圾值。

我的做法是直接用pd.to_datetime让Pandas自动推断格式:

df['下单时间'] = pd.to_datetime(df['下单时间'], format='mixed')

Pandas从2.0版本开始支持format='mixed'参数,可以自动识别混合格式。如果是老版本,就得用errors='coerce'配合手动格式列表去试。解析完之后,我单独检查了解析失败的行,把这些行标记成缺失值,然后根据订单号的连续性做了少量补录,其余实在无法确认的就直接删除。

时间格式洗好之后,特征工程就水到渠成了:

df['月份'] = df['下单时间'].dt.to_period('M')

这里用to_period('M')而不是strftime('%Y-%m'),原因在于to_period得到的是Pandas的Period对象,后面按月份分组时可以直接天然排序,不会出现“2024-10”排在“2024-9”前面的字典序问题。这个细节如果不注意,趋势图的横轴顺序就会完全错乱,图表看起来非常业余。

3.3 分组聚合的口径选择:sum、count还是size

分组聚合是作业的核心考法,但也是最容易搞混统计口径的地方。以“各品类订单量”为例,如果直接df.groupby('商品类别').count(),得到的是每列的非空值数量,默认会把订单号用户ID等所有列都统计一遍,结果看起来列很多,但每一列的数字一样,这就不够专业。

更规范的写法是这样:

category_stats = df.groupby('商品类别').agg( 销售额=('订单金额', 'sum'), 订单量=('订单号', 'nunique'), ) category_stats = category_stats.sort_values('销售额', ascending=False)

这里我用agg函数显式指定每个聚合列的计算方式,老手一眼就能看懂你的统计口径。特别注意我用的是nunique而不是count,这是因为我检查发现同一订单会有多个商品行记录的场景(即一个订单包含多件商品),如果按count统计,订单量会被虚高放大。用nunique对订单号去重计数,才能得到真实的订单数量。

关于sumsize的区别也值得一提。agg(销售额=('订单金额', 'sum'))处理的是金额合计,而size()统计的是分组后的行数,两者在概念上完全不同。如果题目问的是“各品类售出多少件商品”,那么size()是合适的;如果问的是“多少个订单”,就要用nunique。这里面的差异,恰恰是这道作业想要考察的“对数据含义的理解”。

3.4 月度销售趋势:当年最隐蔽的一个坑

月度趋势分析本身不难,难在确保月份顺序正确、且时间范围不能错。我遇到的问题是这样的:数据里混了少量2023年12月的记录,而作业想考察的其实是2024年全年的趋势。如果不加过滤直接按月分组,图表开头就会多出一个看起来很小的柱子,导致Y轴自动缩放,后面的真实趋势反而看不清。

最后我加了一道筛选:

df['年份'] = df['下单时间'].dt.year df_current = df[df['年份'] == 2024] monthly_sales = df_current.groupby('月份')['订单金额'].sum()

这样处理之后,趋势图的核心信息就非常清晰了。我在复盘时还发现,如果只写groupby('月份')而不sort,输出的月度顺序可能是乱序的。所以建议在分组后加一句.sort_index(),确保月份按时间顺序排列。这一点在可视化时尤其重要,不然画出来的折线图就是一条来回乱跳的线。

3.5 支付状态占比与异常订单筛查

支付状态占比相对简单,value_counts(normalize=True)一步到位,乘以100变成百分比即可。需要注意的是,这里也要先确认缺失值,我看到有17条记录的支付状态是空,占比不到千分之二,直接dropna()删掉不会影响整体结论。

异常值筛查这问比较有趣。我的思路是先看订单金额的整体分布:

desc = df['订单金额'].describe()

结果显示均值约为218元,但75分位数是268元,最大值达到了惊人的8999元。这种长尾分布非常典型,均值远大于中位数说明右侧存在极端大额订单。我再用quantile(0.99)找到99分位数的金额作为阈值,把所有超过阈值的订单列出来,逐条查看金额、品类和订单号,发现大部分是正常的企业采购单,但有两笔订单金额恰好是几千元的整数倍,疑似测试数据。

排查异常值没有银弹,核心思路是“先用量化手段圈定,再结合业务理解判断”。如果你要写成可复现的代码,可以用z-score方法:

from scipy import stats import numpy as np df['z_score'] = np.abs(stats.zscore(df['订单金额'])) outliers = df[df['z_score'] > 3]

这里z_score > 3表示偏离均值超过3个标准差,是统计学中常用的离群点判定标准。这两种方法结合使用,基本能把异常订单都找出来。

4. 可视化呈现与业务结论解读

4.1 为什么选柱状图与折线图的组合

作业里没有强制要求画图,但我在完成每一个统计结果后都补了一张图。原因很朴素:数字排在表格里时,读者很难一眼看出哪些品类差距大、趋势是在涨还是在跌,而图形能在半秒内传递结论。

各品类销售额对比我用了柱状图,因为品类是离散变量,柱状图可以直观展示排名差距。月度趋势我用了折线图,因为时间序列的核心信息是连续变化的方向和速度,折线比柱状更容易体现“从8月开始爬升”这样的判断。

画图的代码本身不复杂:

category_stats.plot(kind='bar', y='销售额', figsize=(10, 5)) plt.title('各品类销售额对比') plt.xticks(rotation=45) plt.tight_layout() plt.show()

这里有个小技巧:plt.xticks(rotation=45)给横轴标签加了45度旋转,否则品类名称稍微长一点就会互相重叠,图会显得非常业余。tight_layout()会自动调整留白,避免标题和坐标轴标签被切掉。

4.2 从图表中反推业务结论

图表画出来不是用来看热闹的,而是用来支撑结论的。我当时从两张图里读出了几个关键信息:

第一,家居品类销售额占据了接近30%的份额,遥遥领先其他品类;第二,全年销售额在9月出现了一个明显的低谷,随后10月开始快速反弹,到12月达到全年峰值。这两个结论如果只看数字,也能得到,但看图会更直观,尤其是9月低谷这个问题,我在纯表格里根本不会注意到,因为排名前十的月份数值差距不大。

可视化最大的价值在于“让你注意到你没有主动去找的问题”。所以我建议做完每个统计之后都画一张图,不只是为了给作业加页数,更是给自己多一双眼睛。

4.3 中文乱码、坐标轴溢出与颜色失真的排查

可视化环节最容易翻车的三个问题,我全踩了一遍。

中文乱码在前面已经提过,不再赘述。坐标轴溢出主要出现在异常值筛查时,把8999的订单和平均两三百的其他订单画在同一张图里,Y轴被极端值拉长,其他柱子全部变成贴地的“矮桩”。这个问题的解决思路很简单:画图前先做一个局部过滤,只看金额低于1000元的订单分布,作为主图;异常值单列一个子图展示。

颜色失真其实是导出图片时遇到的一个小坑,Matplotlib默认的保存格式是PNG,在Jupyter里显示时颜色正常,但导出到Word里有时会偏灰,这是因为默认的dpi太低。保存时手动指定dpi=150就能解决。这个细节不致命,但交作业时图的清晰度确实会影响老师的观感。

5. 作业里最隐蔽的四个雷区与排查思路

5.1 缺失值处理不当导致聚合结果凭空变大

作业数据中用户ID列有少量缺失,我当时第一反应是直接删掉这些行,但后来发现这会导致一个问题:这些行里的订单金额是有效值,删掉之后类别汇总的销售额会变小,而且你根本不知道小了多少。

更好的做法是分情况处理:如果缺失的列不是当前分析所必需的字段,就保留行,只把缺失字段标记为“未知”;如果确实需要用到这个字段做分组,例如做“按用户维度”的统计时,才考虑删除。单纯因为某一列缺失就删掉整行,是很多新人常犯的代价最高的错误。

5.2 groupby之后忘记reset_index导致后续操作报错

groupby默认会把分组字段变成索引,如果不加reset_index(),后面想把这个分组结果和另一个表做关联时,就会一直找不到列名。我在做“月度销售额与订单量双轴图”时就卡在这一步,折腾了好一会儿才发现是索引问题。

所以我的习惯是:在每次groupby().agg()之后,立刻加一句.reset_index(),让分组字段回到普通列。这个习惯虽然简单,但能让后续代码顺畅很多,也避免了很多新手在groupby结果上反复踩坑。

5.3 排序时字典序导致的月份错乱

这个坑在前面提过,我再详细说一下现象。如果时间字段是字符串格式,'2024-10'按字典序会排在'2024-9'前面,因为你比较的是字符而不是数值。当时我用的to_period('M')已经规避了这个问题,但如果你用的是strftime('%Y-%m'),那排序时就必须额外加sort_values,或者干脆用pd.Categorical指定顺序。

这件事给我的教训是:任何时间字段,能早转类型就早转类型,一旦你把它当做字符串处理,后面所有依赖顺序的操作都会变成定时炸弹。

5.4 只看汇总数字、不逐条检查导致的业务误判

写完代码后我也有一瞬间觉得“这不就完成了吗”,但多留了个心眼,把筛选出来的异常订单逐条打印出来看了一眼,结果发现有一条金额是负数的退款记录被算进了总销售额里。如果不做这个检查,最终结论会偏离真实情况差不多0.3%,单看数字影响不大,但在作业汇报时如果被老师问到“这个负数是哪来的”,回答不上来就很尴尬。

数据工作的核心素养就是“永远对结果保持怀疑”,尤其是面对那些看起来特别规整、特别漂亮的结果时,更要往回多查一步。

6. 从作业3.7沉淀下来的通用处理模板

做完这道作业,我把整个流程总结成了一个“四步走”模板。之后再接任何数据处理任务,我基本都会按这个顺序推进。

第一步是结构探查,用shapedtypesheaddescribe把数据的规模、类型和分布摸清楚;第二步是质量清洗,把重复值、缺失值、格式问题全部列出来逐一处理;第三步是口径明确,针对每个分析问题明确分组字段、聚合字段与聚合方式,并用agg显式表达;第四步是结果验证,用图表和逐条抽样检查来确认结果符合业务直觉。

这个模板听上去简单,但真正执行到位需要耐心。尤其是第二步,表面上是体力活,实际上每一处清洗都对应着一个数据质量问题,而数据质量直接决定分析结论的可靠性。以后你遇到任何新数据集,都可以按这个模板走一遍,基本不会出大差错。

最后再分享一个心态上的体会:作业3.7这种题目真正的价值,不在那四个问题的答案本身,而在于完整走了一遍“拿到原始数据→发现问题→定义口径→输出结论”的链路。这个链路你走得越熟,以后面对真实工作中更乱、更大、更缺文档的数据时,心里就越有底。数据清洗这类脏活累活,干一次是折磨,干十次就会变成肌肉记忆,之后再碰到任何“看似不可能完成”的数据任务,你就知道自己一定能啃下来。

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

MODIS MOD13Q1质量波段解析:Python掩膜生成与时序应用

做遥感时间序列分析的同学,基本都绕不开MODIS的MOD13Q1产品。这份数据是250米分辨率、16天合成的植被指数产品,里面有NDVI和EVI,直接拿来就能做长时序的植被变化分析,看起来特别“友好”。但用着用着你就会发现,产品里…

作者头像 李华
网站建设 2026/9/7 15:27:39

OpenClaw+Skills+星辰大模型:企业级AI代理安全落地实践

最近圈子里聊OpenClaw的人肉眼可见地多起来了,身边做运维和自动化的朋友也开始打听这玩意到底能不能在企业里正经用。我花了小半年时间,基于OpenClawSkills星辰大模型这条主线,从零搭了一套企业级AI代理能力平台,重点是围绕“安全…

作者头像 李华
网站建设 2026/9/7 15:26:05

GenOffice、Motrix Next、Qx:三款提升效率的开源项目实测

这次一次聊三个 GitHub 开源项目,方向完全不同,但都属于“装上就能提升效率”的类型:GenOffice、Motrix Next 和 Qx 效率启动器。三者的 Star 数分别是 54.9K、4.1K 和 15,量级差很远,但各自解决的问题都很明确——办公…

作者头像 李华
网站建设 2026/9/7 15:24:17

秋叶ComfyUI-V35整合包:AI绘画节点式工作流一键部署指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 15:24:11

并行处理与批量任务调度:从原理到稳定落地的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华