尧图精选

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

🕒 发布时间:2026/9/7 15:31:08 📁 来源:尧图网络
说实话拿到作业3.7这个编号的时候我第一反应是“课程又留常规练习了”结果把题目完整读了三遍才发现这道题表面上是Python数据处理练习实际把数据清洗、字段理解、分组聚合、可视化分析全揉在了一起。更关键的是它要求的不是“跑通代码”而是“讲清楚每一步为什么这么做”这就完全不是抄一段Pandas代码能交差的了。这道作业的场景非常典型一份电商订单CSV约一万行字段包含订单号、用户ID、下单时间、订单金额、商品类别、支付状态等。任务分四问按类别统计销售额排行、分析月度销售趋势、计算支付状态占比、找出金额异常的订单并给出排查结论。看起来每问都很基础但真正动手做的时候你会发现坑全藏在数据本身的脏乱差里。这篇文章我就完整复盘一下我当时是怎么拆解任务、怎么写代码、在哪几个地方差点翻车以及最后从作业里沉淀下来的通用处理套路。不管你是在上网课、做训练营作业还是工作中第一次接手数据清洗的活儿这套思路应该都能直接拿来用。1. 作业任务拆解与整体设计思路1.1 先把“表面需求”翻译成“技术动作”我拿到题的第一件事不是打开IDE而是拿笔在纸上把这四问翻译成具体的技术操作。这个过程非常关键因为题目里的“分析销售额排行”到了代码层面其实是“先做类别分组再对金额字段聚合求和最后排序”这么一串动作。如果一上来就写代码很容易写着写着发现漏掉了某个隐含条件。我当时拆出来的技术动作是这样的统计各品类销售额与订单量排行对应groupby(类别)后分别对订单金额做sum、对订单号做count再排序。分析月度销售趋势对应先把下单时间转成标准日期格式再提取year-month按月分组求和。计算支付状态占比对应value_counts(normalizeTrue)。找金额异常的订单对应要做描述性统计结合quantile或z-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.7Jupyter 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 MS或WenQuanYi 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(元, , regexFalse) # 2. 将空字符串转为缺失值便于统一处理 df[订单金额] df[订单金额].replace(, pd.NA) # 3. 转成数值类型无法转换的变成NaN df[订单金额] pd.to_numeric(df[订单金额], errorscoerce)pd.to_numeric这个函数的errorscoerce参数是这段代码的灵魂它会把无法转换的内容统一置为NaN而不是直接让程序崩溃。转换结束后我又检查了一次缺失情况发现多了14个NaN这些就是原本带“元”后缀和真正空白的记录加在一起的量。清理掉脏字符之后还要再做一个合理性边界检查订单金额理论上应该是正数。我扫了一遍发现最低值是-9.9这种负数订单通常是退款或者测试订单但作业题意是分析销售数据这类负值会严重干扰聚合结果。我的处理是先把边界情况都打印出来看一下确认是退款单后在本次分析中先过滤掉。3.2 清洗第二关时间字段解析与业务特征提取时间字段的清洗是这道作业里最容易出错的地方因为原始数据里的时间格式杂乱到让人头大。我扫了一遍发现至少存在三种格式2024/01/15 09:23:11、2024-01-15 09:23、2024年1月15日。如果不统一成一种格式之后做月份提取就只能得到一堆垃圾值。我的做法是直接用pd.to_datetime让Pandas自动推断格式df[下单时间] pd.to_datetime(df[下单时间], formatmixed)Pandas从2.0版本开始支持formatmixed参数可以自动识别混合格式。如果是老版本就得用errorscoerce配合手动格式列表去试。解析完之后我单独检查了解析失败的行把这些行标记成缺失值然后根据订单号的连续性做了少量补录其余实在无法确认的就直接删除。时间格式洗好之后特征工程就水到渠成了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(销售额, ascendingFalse)这里我用agg函数显式指定每个聚合列的计算方式老手一眼就能看懂你的统计口径。特别注意我用的是nunique而不是count这是因为我检查发现同一订单会有多个商品行记录的场景即一个订单包含多件商品如果按count统计订单量会被虚高放大。用nunique对订单号去重计数才能得到真实的订单数量。关于sum和size的区别也值得一提。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(normalizeTrue)一步到位乘以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(kindbar, y销售额, figsize(10, 5)) plt.title(各品类销售额对比) plt.xticks(rotation45) plt.tight_layout() plt.show()这里有个小技巧plt.xticks(rotation45)给横轴标签加了45度旋转否则品类名称稍微长一点就会互相重叠图会显得非常业余。tight_layout()会自动调整留白避免标题和坐标轴标签被切掉。4.2 从图表中反推业务结论图表画出来不是用来看热闹的而是用来支撑结论的。我当时从两张图里读出了几个关键信息第一家居品类销售额占据了接近30%的份额遥遥领先其他品类第二全年销售额在9月出现了一个明显的低谷随后10月开始快速反弹到12月达到全年峰值。这两个结论如果只看数字也能得到但看图会更直观尤其是9月低谷这个问题我在纯表格里根本不会注意到因为排名前十的月份数值差距不大。可视化最大的价值在于“让你注意到你没有主动去找的问题”。所以我建议做完每个统计之后都画一张图不只是为了给作业加页数更是给自己多一双眼睛。4.3 中文乱码、坐标轴溢出与颜色失真的排查可视化环节最容易翻车的三个问题我全踩了一遍。中文乱码在前面已经提过不再赘述。坐标轴溢出主要出现在异常值筛查时把8999的订单和平均两三百的其他订单画在同一张图里Y轴被极端值拉长其他柱子全部变成贴地的“矮桩”。这个问题的解决思路很简单画图前先做一个局部过滤只看金额低于1000元的订单分布作为主图异常值单列一个子图展示。颜色失真其实是导出图片时遇到的一个小坑Matplotlib默认的保存格式是PNG在Jupyter里显示时颜色正常但导出到Word里有时会偏灰这是因为默认的dpi太低。保存时手动指定dpi150就能解决。这个细节不致命但交作业时图的清晰度确实会影响老师的观感。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沉淀下来的通用处理模板做完这道作业我把整个流程总结成了一个“四步走”模板。之后再接任何数据处理任务我基本都会按这个顺序推进。第一步是结构探查用shape、dtypes、head、describe把数据的规模、类型和分布摸清楚第二步是质量清洗把重复值、缺失值、格式问题全部列出来逐一处理第三步是口径明确针对每个分析问题明确分组字段、聚合字段与聚合方式并用agg显式表达第四步是结果验证用图表和逐条抽样检查来确认结果符合业务直觉。这个模板听上去简单但真正执行到位需要耐心。尤其是第二步表面上是体力活实际上每一处清洗都对应着一个数据质量问题而数据质量直接决定分析结论的可靠性。以后你遇到任何新数据集都可以按这个模板走一遍基本不会出大差错。最后再分享一个心态上的体会作业3.7这种题目真正的价值不在那四个问题的答案本身而在于完整走了一遍“拿到原始数据→发现问题→定义口径→输出结论”的链路。这个链路你走得越熟以后面对真实工作中更乱、更大、更缺文档的数据时心里就越有底。数据清洗这类脏活累活干一次是折磨干十次就会变成肌肉记忆之后再碰到任何“看似不可能完成”的数据任务你就知道自己一定能啃下来。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →