尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Pandas数据分析全流程:从数据清洗到可视化的完整实战

Pandas数据分析全流程:从数据清洗到可视化的完整实战 最近在带几个朋友入门数据分析发现大家拿到一份数据之后最常见的状态就是愣住不知道从哪下手。清洗数据嫌麻烦画图又画不明白最后折腾半天还在print(df.head())打转。其实这个流程完全不神秘Pandas就是那把最顺手的刀从数据清洗到可视化一条链路走通之后你会觉得所谓“数据分析”说到底就是三件事把数据弄干净、把数据问清楚、把结论画出来。这篇博客我就拿一个常见的实战场景——白酒销售数据分析——来完整走一遍这条链路。如果你正在学 Python 数据分析或者手里正好有一份乱七八糟的 Excel 报表不知道该怎么处理那这篇内容应该能直接帮你省掉两三天的摸索时间。我会把每一步的为什么讲清楚也会把我在实际项目里踩过的坑、趟过的雷都交代出来尽量让你看完就能照着做。1. 项目整体设计与思路拆解1.1 为什么要用 Pandas 搭这条链路很多初学者纠结一个问题数据清洗用 Excel 不就行了可视化为啥不直接拖到 BI 工具里我的回答很简单——当你的数据量超过十万行或者你需要在每周、每天重复跑同一套分析逻辑的时候Excel 和 BI 的“手动操作”属性就成了致命伤。Pandas 的优势不在于它比 Excel 多厉害而在于它把整个过程脚本化了今天跑一遍明天数据更新了再跑一遍结果可复现、逻辑可审查、错误可追溯。另外Pandas 在数据清洗环节的生态实在太成熟了。dropna、fillna、duplicated、astype这些方法几乎覆盖了 90% 的脏数据场景配合groupby、merge、pivot_table又能把清洗完的数据直接加工成适合可视化的长表或宽表。后续接Matplotlib、Seaborn或者直接输出成CSV/Excel给领导看都非常顺。这套链路最核心的价值不是单个功能而是数据在每一步的形状变化都有迹可循。1.2 数据分析流程应该怎么排我自己的习惯是把整个分析拆成四个阶段这也是我推荐给入门者的标准姿势数据读取与概览先info()、describe()搞清楚数据规模、字段类型、缺失情况心里有数再动手。数据清洗缺失值、重复值、异常值、类型错误、字符串脏数据这个阶段通常占整个项目 60% 的时间别嫌烦。数据加工与聚合按业务维度分组、求和、求均值、算占比把明细数据凝练成可读的统计结果。可视化输出根据结论需要选图表类型把关键指标画出来配合必要的标注和配色输出成报告或者大屏数据。这个顺序不是拍脑袋定的。清洗不干净就去做聚合结果必然是错的聚合口径没想清楚就画图画出来也是自欺欺人。每一步都在为下一步打基础这也是为什么我一直强调“数据分析不是画图比赛是逻辑链路的工程”。2. 数据清洗最花时间也最见功力的环节2.1 缺失值处理先弄清楚缺的是什么类型的缺失拿到数据先跑一遍df.info()你会看到每个字段的非空数量。如果某个字段的空值比例超过 30%你就得想一想这个字段到底是“真的没采集到”还是“业务上根本不适用”。这两种情况处理方式完全不同。import pandas as pd import numpy as np # 模拟一份白酒销售数据 df pd.DataFrame({ 日期: [2024-01-05, 2024-01-06, 2024-01-07, None, 2024-01-09], 品牌: [泸州老窖, 茅台, None, 五粮液, 泸州老窖], 渠道: [经销商, 电商, 电商, 经销商, None], 销量箱数: [120.0, 85.0, np.nan, 200.0, 95.0], 销售额万元: [14.4, 17.0, np.nan, 30.0, 11.4] }) print(df.info())缺失值处理我一般按这个优先级判断直接删除缺失值占比很小比如千分之几且该行其他字段都完整直接dropna(subset[关键字段])。填充默认值对于类别字段缺失可以单独标记为“未知”对于数值字段业务上允许填 0 的才填 0比如销量为 0 表示没卖出去。用统计量填充数值型连续数据考虑用中位数填充因为中位数对异常值不敏感比均值稳。不处理有些算法比如树模型本身就支持缺失值但做常规统计分析时还是建议先处理干净。注意fillna(0)用起来很顺手但它会把缺失和真实的“零”混为一谈。如果 0 在业务上有特殊含义比如“退货清零”那你就等于篡改了数据语义。我踩过这个坑处理销售数据时把缺失的销量全填了 0结果月均销量被拉低了一大截汇报时才被发现。2.2 重复值与数据类型两个最容易被忽视的坑重复值处理相对简单但有个细节值得说判断重复不能只靠肉眼得明确“重复”的定义。是整行完全重复还是某个关键列比如订单号、日期品牌组合重复这俩的结果天差地别。# 整行完全重复 df.drop_duplicates(inplaceTrue) # 按指定列去重保留最后一条 df.drop_duplicates(subset[品牌, 日期], keeplast, inplaceTrue)数据类型是另一个容易翻车的地方。Pandas 里最经典的坑就是明明看着是数字实际类型是 object字符串。这种情况在读取 Excel 或 CSV 时特别常见比如金额列里混了“-”或者“待定”这类文本整列都会被读成字符串。# 强制转换数值遇到无法转换的变成 NaN df[销售额万元] pd.to_numeric(df[销售额万元], errorscoerce) # 日期列统一转成 datetime df[日期] pd.to_datetime(df[日期], errorscoerce) # 类别列转成 category 类型省内存且排序语义更清晰 df[渠道] df[渠道].astype(category)为啥要较真数据类型第一字符串没法直接求和、求平均第二日期不转成 datetime你没法做按月、按季度聚合第三category 类型在数据量大时能显著降低内存占用。这三个理由随便哪一个都值得你多花两分钟做类型检查。我的习惯是每次读取完数据后立刻写个循环df.dtypes扫一遍看到不对劲的 object 列马上处理。2.3 字符串清洗正则表达式是必须掌握的技能真实业务数据里的字符串脏得超乎想象。品牌名一会儿叫“贵州茅台”一会儿叫“茅台”一会儿又冒出个“茅台(飞天)”如果不做归一化groupby出来的结果就会碎成一片。这时候str访问器和正则表达式就是你的武器。# 去除首尾空格和特殊字符 df[品牌] df[品牌].str.strip().str.replace(r[\s\(].*$, , regexTrue) # 统一大小写适用于英文品牌 df[品牌] df[品牌].str.lower()正则表达式在 Pandas 里有两个高频场景用str.contains做条件筛选以及用str.replace做字段归一化。比如筛查异常记录# 找出品牌字段里包含非中文字符的异常数据 mask df[品牌].str.contains(r[^\u4e00-\u9fa5], naFalse) print(df[mask])刚开始学的时候正则语法确实劝退但我的建议是别强背先掌握三件套就够了.*匹配任意字符、^和$锚定开头结尾、[...]字符集合。剩下的遇到了再查用多了自然熟练。3. 数据转换与聚合把明细数据变成能回答问题的结果3.1 groupby数据分析最核心的操作没有之一清洗干净之后真正开始“干活”的就是分组聚合了。groupby的语法逻辑非常直白按照什么维度分组对每一组做什么计算。# 拿一份按日的销售数据按品牌统计销量和销售额 daily pd.DataFrame({ 日期: pd.date_range(2024-01-01, periods90, freqD), 品牌: [茅台, 五粮液, 泸州老窖] * 30, 销量箱数: np.random.randint(50, 300, 90), 销售额万元: np.random.uniform(5, 50, 90).round(2) }) # 按品牌聚合 brand_summary daily.groupby(品牌).agg( 总销量(销量箱数, sum), 平均日销(销量箱数, mean), 销售额合计(销售额万元, sum), 记录天数(日期, count) ).reset_index() print(brand_summary)agg的写法是agg(新列名(原列名, 聚合函数))这种写法可读性高而且一次能算好几个指标比groupby之后分别sum()、mean()再合并高效得多。我个人建议能用agg就别分开写不仅代码短执行效率也更好。3.2 merge 与 concat多表数据怎么拼才不出错实际项目中很少有“一张表打天下”的情况。白酒销售数据往往拆在好几个系统里销售系统出销售明细财务系统出回款数据库存系统出库存快照。要把它们拼起来就得掌握merge。# 另一份品牌价目表 price pd.DataFrame({ 品牌: [茅台, 五粮液, 泸州老窖], 出厂价万元/箱: [0.2, 0.15, 0.1] }) merged pd.merge(daily, price, on品牌, howleft)merge里how参数的选择是重灾区。inner只保留两边都有的键left以左表为准、右表缺失的填 NaNouter两边的都保留。我一般遵循一个原则主表是哪张就用它做左表howleft这样不会因为关联键缺失而丢掉主表的记录。每次 merge 完一定要检查行数有没有变多如果变多了说明关联键不是唯一的出现了笛卡尔积这是最常见的合并事故。# 检查合并后行数是否异常 if len(merged) ! len(daily): print(警告merge 后行数变化请检查关联键是否有重复)concat则是纵向拼接的场景比如把 1 月、2 月、3 月的表按行叠起来。用的时候注意ignore_indexTrue否则索引会重复后续操作容易踩坑。3.3 pivot_table二维透视比 Excel 透视表更可控Excel 的透视表大家都很熟Pandas 里对应的是pivot_table。它的好处是可以一次性完成“行维度×列维度×数值聚合”的三维统计这在对比各品牌在各渠道的表现时特别好用。# 模拟品牌×渠道的销售明细 sales pd.DataFrame({ 品牌: [茅台, 茅台, 五粮液, 五粮液, 泸州老窖, 泸州老窖] * 20, 渠道: [经销商, 电商, 经销商, 电商, 经销商, 电商] * 20, 销售额万元: np.random.uniform(5, 50, 120).round(2) }) pivot sales.pivot_table( index品牌, # 行 columns渠道, # 列 values销售额万元, # 要聚合的值 aggfuncsum, # 聚合方式 fill_value0 # 缺失填0避免 NaN 干扰后续计算 ) print(pivot)这里的fill_value0是个小技巧。透视表里出现 NaN 通常意味着“该组合下没有数据”业务上等价于 0。如果你不填 0后面一旦对这列求百分比或者做热力图NaN 会一路传染下去很麻烦。4. 可视化实现把结论画成别人一眼能看懂的样子4.1 Matplotlib 打底先把图画出来再谈好看Pandas 内置了基于 Matplotlib 的.plot()方法这是最快捷的入口。对于折线图、柱状图这种常规图表一行代码就能出图import matplotlib.pyplot as plt # 让中文正常显示默认字体不含中文必须配置 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 各品牌每月销量趋势 monthly daily.set_index(日期).resample(M)[销量箱数].sum() monthly.plot(kindline, figsize(10, 5), title白酒月度销售总量趋势) plt.xlabel(月份) plt.ylabel(销量箱数) plt.grid(alpha0.3) plt.tight_layout() plt.show()有个细节必须提醒中文乱码是每个入门者必踩的坑。Matplotlib 默认字体不支持中文不设置上面那两行所有图表标题和坐标轴中文都会变成方块。只需要在脚本开头统一设置一次全局字体即可。如果你用的是 Linux 服务器可能还得先安装中文字体包这个我后面在常见问题里会细说。4.2 Seaborn 进阶统计图表的颜值担当Seaborn是建立在 Matplotlib 之上的高级接口最大的优势是用一行代码画出带有统计含义的图表。比如盒须图看分布、热力图看相关性这些用 Matplotlib 要写半天用 Seaborn 就一句import seaborn as sns # 各渠道销售额分布对比 sns.boxplot(datasales, x渠道, y销售额万元) plt.title(各渠道销售额分布对比) plt.show() # 品牌与渠道的透视热力图 sns.heatmap(pivot, annotTrue, fmt.1f, cmapYlOrRd) plt.title(品牌×渠道销售额热力图) plt.show()我个人的感受是业务分析报告里Seaborn 的图比 Matplotlib 默认样式更容易“让领导满意”。它的默认配色和网格风格比较现代不需要额外调参就很能打。但 Seaborn 不能完全替代 Matplotlib比如复杂的多子图布局、自定义坐标轴刻度最终还是得回到 Matplotlib 层面手动操作。4.3 可视化大屏场景数据导出比画图更关键很多朋友看到热词里出现“可视化大屏”以为必须学什么炫酷的 BI 工具。实际上在做大屏之前你首先得把数据整理成大屏所需要的宽表结构。比如看板要展示“各区域各品牌月度销售额”你就得先用 Pandas 把明细数据聚合出这张表再导出成 CSV 或 JSON 交给前端或者 BI 工具去渲染。# 导出清洗并聚合好的数据供大屏或BI使用 pivot.to_csv(brand_channel_summary.csv, encodingutf-8-sig)utf-8-sig这个编码我特意强调一下。如果你直接输出utf-8在 Windows 上用 Excel 打开 CSV 会出现中文乱码因为 Excel 默认用 ANSI 编码去解析。utf-8-sig会在文件开头加一个 BOM 标记Excel 就能正确识别了。这类“小问题”在真实交付场景里特别毁人提前注意能省很多沟通成本。5. 常见问题与排查技巧实录5.1 读不进数据、编码乱码、中文显示方块这三个问题几乎是新手必遇三连。我整理了一个排查顺序按照这个顺序走绝大多数情况五分钟内能解决问题常见原因解决思路FileNotFoundError路径错误或文件名中文编码问题用os.path.exists()先检查路径文件路径建议用英文命名UnicodeDecodeError文件编码不是 UTF-8读取时指定encodinggbk或encodinglatin1先读出再转码Excel 打开 CSV 中文乱码缺少 BOM 标记导出时用encodingutf-8-sig图中中文显示为方块Matplotlib 默认字体不含中文设置plt.rcParams[font.sans-serif] [SimHei]并确认系统已安装该字体read_csv遇到编码问题我的土办法是先试utf-8报错就换gbk再不行就latin1。虽然粗暴但覆盖率极高。如果你想更精准可以用 Python 的chardet库检测文件编码后再读取不过那是后话。5.2 大数据量卡顿与内存爆炸Pandas 是跑在内存里的数据量一旦上到几千万行内存占用会非常吓人。这时候有几个优化技巧非常实用读取时只选需要的列pd.read_csv(..., usecols[日期, 品牌, 销量])从源头减少内存。把字符串列转成 category如果某列只有少数几种取值比如渠道只有“经销商、电商、餐饮”转 category 后内存能降一大截。分块读取pd.read_csv(..., chunksize100000)配合循环分批处理再合并结果。# 分块处理大文件示例 chunk_iter pd.read_csv(huge_sales.csv, chunksize500000) results [] for chunk in chunk_iter: # 每块清洗和聚合 results.append(chunk.groupby(品牌)[销售额万元].sum()) final pd.concat(results).groupby(level0).sum() print(final)这里有个细节很多人不知道分块处理时先聚合再合并比合并再聚合省内存得多。因为聚合后的结果远小于原始 chunk后续的concat和二次聚合都轻量很多。5.3 类型转换与链式赋值的隐藏坑SettingWithCopyWarning是 Pandas 最经典的警告之一。它不是在报错而是在提醒你“你可能在修改一个副本原数据没变”。这个警告出现多了很多人直接忽略但忽略的后果就是代码跑完结果没保存数据没更新白忙活一场。# 错误示范可能触发 SettingWithCopyWarning filtered df[df[品牌] 茅台] filtered[新列] 100 # 修改的是副本 # 正确做法使用 .loc 或 copy() 明确意图 filtered df[df[品牌] 茅台].copy() filtered[新列] 100 # 修改的是独立副本我的建议是只要你想对筛选后的子集做修改先.copy()再说。虽然多占一点内存但语义清晰不会留定时炸弹。至于类型转换时遇到“无法转换”的值errorscoerce参数会把它们变成 NaN插入dropna或fillna后再处理链路就完整了。5.4 可视化“画出来不好看”怎么办图丑一般是两个原因一是没选对图表类型二是没做信息减法。折线图适合展示趋势柱状图适合对比大小饼图只适合展示占比且类别最好少于 5 个散点图适合看两个变量关系。很多人一股脑全用柱状图结果当然不好看。另外一个常见问题是标签拥挤。如果你的 x 轴有几十个品牌名直接画出来就是一片黑块。解决办法是旋转刻度、或者只展示 Top 10 品牌、或者把坐标轴改成“其他”合并项。可视化不是把数据全部堆上去而是把最重要的一两个信息突出出来剩下的该舍弃就舍弃。写在最后的实际操作体会这套 Pandas 流程我前前后后用了很多年从一开始天天翻文档到现在拿到任何一张乱表都能半小时内出结果最大的经验就一句话清洗步骤一定要写成函数封装起来别每次手动敲。把clean_sales_data(df)、aggregate_by_brand(df)这种操作固化成脚本下次换一份数据改一下文件路径就能直接跑。数据分析和写代码一样最怕的是重复劳动和不可复现。另外一个小技巧送给大家每做完一个步骤就df.to_csv(中间结果.csv)存一次盘。别看这个动作简单真能救命。有时候你清洗了十步最后一步报错如果没有中间存档就得从头再来。存中间结果既方便排查也方便对比每一步处理前后的差异。数据分析也好数据工程也罢稳扎稳打才是最快的路径。
返回列表