资讯动态

Pandas数据处理实战:从零散方法到组合流水线的训练指南

发布时间:2026/10/27 12:44:35 来源:尧图企业网站定制
很多刚接触数据分析的朋友跟我聊过一个困惑Pandas 的 API 文档翻来覆去看了好几遍groupby、merge、pivot_table这些方法单独拎出来都能说出个大概可一旦拿到一份真实的、脏兮兮的数据表脑子就空了不知道第一步该干什么。这个现象特别普遍我自己当年也是这样过来的——看教程觉得全会动手就卡壳。问题不在于你记性差而在于缺少“把零散知识点串成动作序列”的训练。Pandas 训练题的价值就在这里它逼着你在一个具体的数据场景里连续调用多个方法处理真实存在的缺失值、类型错乱、重复行和逻辑分组最终产出一个能看的结果。这一章我整理了十几道初级训练题的完整解法覆盖数据读取、清洗、筛选、分组聚合、表连接和透视这几块最常用的能力每道题都写清楚“为什么这么写”以及“我实际踩过什么坑”。不管你是刚学完 Pandas 基础语法想找题练手还是工作里偶尔要处理表格数据但总写不利索这些内容都能直接拿去用。1. 训练题到底练的是什么能力1.1 从“会方法”到“会组合”的跨越单独看df.dropna()这个调用任何人五分钟就能记住。但真实场景里你面对的往往是这样一条链路先读进来一个 CSV发现某列数字被识别成了字符串接着要处理几行关键字段缺失的记录然后按某个维度分组算指标最后把结果和另一张表拼起来输出。这条链路上每一步的输入输出类型都在变中间任何一步写错后面的结果就全歪了。训练题的核心目的就是让你反复走这条链路形成肌肉记忆。我个人的经验是初级训练题不需要追求算法复杂度重点在于覆盖高频操作的组合模式。比如“筛选出某条件下某几列再按某列排序取前 N 行”这种模式在实际工作里出现的频率极高几乎每份数据报告都会用到。你把这种模式练到不用查文档就能写出来效率提升是立竿见影的。1.2 初级题该覆盖的六个动作类别我把这一章的训练题按动作类别做了划分方便你有针对性地补短板动作类别典型方法训练目标数据读取与概览read_csv、info、describe快速判断数据质量和字段类型缺失与重复处理isna、dropna、fillna、duplicated建立清洗的标准流程条件筛选与索引布尔索引、loc、iloc、query精准定位目标数据分组聚合groupby、agg、transform从明细到汇总的思维转换表连接merge、concat多源数据的整合逻辑重塑与透视pivot_table、melt行列视角的灵活切换这六类动作基本覆盖了日常数据处理的八成场景。初级阶段的训练目标不是把每个方法的所有参数都背下来而是知道遇到什么问题该调用哪一类方法以及调用之后结果长什么样。1.3 为什么建议用“造数据”的方式练手网上很多训练题直接给你一个现成的数据集这当然方便但我更推荐你自己用pd.DataFrame手动构造小规模数据来练。原因很实在手造数据的时候你清楚每一行每一列是什么含义结果对不对一眼就能看出来。用真实数据集反而容易被各种意外情况干扰分不清是方法用错了还是数据本身有问题。构造数据也很简单字典套列表就行import pandas as pd import numpy as np df pd.DataFrame({ 订单号: [A001, A002, A003, A004, A005], 城市: [北京, 上海, 北京, 广州, 上海], 品类: [数码, 服饰, 数码, 食品, 服饰], 金额: [1200, 350, 800, 90, 420], 数量: [1, 2, 1, 3, 2] })这种五到十行的小表足够你把绝大多数初级操作练一遍而且每次改一个条件就能验证一个新知识点比啃大数据集高效得多。2. 数据读取与第一眼体检的固定套路2.1 读进来之后先做三件事很多人拿到数据第一反应是直接开始筛选或者画图这是大忌。我养成的习惯是任何数据读进来先做三件事看形状、看类型、看缺失。这三步加起来不超过十秒但能帮你避开后面百分之八十的报错。df pd.read_csv(sales.csv) print(df.shape) # 多少行多少列 print(df.dtypes) # 每列什么类型 print(df.isna().sum()) # 每列缺多少shape告诉你数据规模dtypes告诉你有没有数字列被读成了object这是最常见的坑往往是因为某列里混了空格或者单位符号isna().sum()告诉你哪些列需要重点处理。这三行输出看完你对这份数据的“健康状况”就有了基本判断。2.2 类型错乱是初级选手最大的拦路虎我见过太多人卡在“明明这列是数字为什么算平均值报错”上面。根源就是dtype是object。常见诱因有三个数字里带了千分位逗号、带了货币符号、或者混进了空字符串。处理方式分两步先看看到底是什么值在捣乱# 找出无法转成数字的行 mask pd.to_numeric(df[金额], errorscoerce).isna() df[金额].notna() print(df.loc[mask, 金额])这段代码的逻辑是用errorscoerce强行转换转不动的会变成NaN再和原本非空的行做交集就精确定位到了那些“看着像数字其实不是”的脏值。定位到之后要么用str.replace清掉符号再转要么直接按缺失值处理。这个排查思路我用了很多年比盲目astype然后被报错打断要高效得多。提示pd.to_numeric的errors参数有三个取值raise直接报错、coerce转不动变 NaN、ignore原样返回。排查阶段用coerce确定数据干净后再用astype做最终转换。2.3 用 describe 快速摸清数值分布describe()默认只统计数值列输出计数、均值、标准差、四分位数和极值。别小看这几行它能帮你发现很多异常比如最小值是负数金额不该为负、最大值大得离谱可能是录入错误、均值和中位数差很远分布严重偏斜。我一般会配合includeall再看一遍类别列的统计print(df.describe(includeall))类别列会输出count、unique、top、freq其中unique告诉你这列有多少个不同取值top和freq告诉你出现最多的值及其频次。如果某列unique数量异常多比如“城市”列出现了两百个不同值那大概率是数据录入不规范需要进一步清洗。3. 缺失值与重复行的处理决策3.1 缺失处理没有标准答案只有场景判断新手最爱问的问题是“缺失值到底该删还是该填”。这个问题没有统一答案取决于缺失比例和字段重要性。我的判断逻辑是这样的先算缺失比例超过一半的列直接考虑整列丢弃比例在百分之五以内的关键字段可以删行比例中等的数值列用中位数填充比均值稳妥均值容易被极端值带偏类别列用众数或者单独标记为“未知”。# 计算每列缺失比例 missing_ratio df.isna().mean().sort_values(ascendingFalse) print(missing_ratio[missing_ratio 0])这里用isna().mean()而不是isna().sum()是因为比例比绝对数量更有决策价值。一个一千行的表缺五十行和缺五行处理策略完全不同。填充的时候有个细节要注意fillna默认返回新对象如果你希望原地修改得加inplaceTrue或者重新赋值。我个人的习惯是永远重新赋值因为原地修改在链式调用里容易出问题而且不利于回溯。df[金额] df[金额].fillna(df[金额].median()) df[城市] df[城市].fillna(未知)3.2 重复行要先定义“什么叫重复”duplicated()默认把所有列都相同视为重复但实际业务里“重复”的定义往往更窄。比如订单表里同一个订单号出现两次才算重复金额相同不算。这时候就要用subset参数指定判断依据# 按订单号判断重复保留第一条 df df.drop_duplicates(subset[订单号], keepfirst)keep参数有三个取值first保留首次出现、last保留最后出现、False把所有重复项全删掉。选哪个取决于业务含义——如果是数据重复录入保留任意一条都行如果是状态更新记录可能得保留最后一条。我踩过的一个坑是没指定subset就删重结果把两笔金额相同但订单号不同的正常交易给误删了这种错误在财务数据里是致命的。注意删重之前务必先df[df.duplicated(subset[...], keepFalse)]把重复项全部打印出来看一眼确认这些行确实该删再动手。这一步多花十秒能避免不可逆的数据损失。3.3 缺失和重复的处理顺序有讲究先处理哪个我的经验是先删重再填缺失。原因很简单如果先填缺失那些本来重复的行可能因为填充值不同而变得“不重复”了导致删重失效。反过来先删重剩下的行再统一填充逻辑更干净。这个顺序在数据量大、缺失多的情况下尤其重要我见过因为顺序搞反导致重复数据残留、最终汇总指标翻倍的案例。4. 条件筛选与索引的精准定位4.1 布尔索引是基本功中的基本功Pandas 筛选数据的核心就是布尔索引构造一个和行数等长的 True/False 序列用它去“罩”住目标行。多个条件组合时表示与、|表示或、~表示非每个条件必须用括号包起来这是运算符优先级决定的不加括号会报错。# 北京或上海且金额大于 500 result df[(df[城市].isin([北京, 上海])) (df[金额] 500)]isin比写一串加|要清爽得多尤其是候选值超过三个的时候。这个技巧在筛选特定几个品类、几个区域时特别顺手。4.2 loc 和 iloc 的分工要记牢loc按标签索引iloc按位置索引这两个别混。日常筛选我更常用loc因为它的语义是“我要哪些行、哪些列”读起来接近自然语言# 取金额大于 500 的行的城市和金额两列 result df.loc[df[金额] 500, [城市, 金额]]注意loc的行条件在前、列选择在后中间用逗号隔开。这个写法比先筛选再选列要高效因为它一步到位。iloc则适合按位置取比如取前五行df.iloc[:5]或者取第三到第五行、前两列df.iloc[2:5, :2]。初学者容易在iloc的切片上犯迷糊——它和 Python 列表切片一样左闭右开。4.3 query 方法让复杂条件更可读条件一多布尔索引那串括号和就看不清了。这时候query是个好选择它用字符串表达式写条件可读性高很多result df.query(城市 in [北京, 上海] and 金额 500)query里可以直接引用列名列名是中文也没问题用and、or、not代替符号还支持变量名引用外部 Python 变量。不过它有个限制列名里如果有空格或者特殊字符得用反引号包起来。我一般在中文字段名或者条件超过三个的时候用query简单条件还是布尔索引更直接。4.4 筛选后修改数据的警告陷阱这里必须提一个几乎人人都会遇到的坑对筛选结果赋值时出现SettingWithCopyWarning。比如subset df[df[金额] 500] subset[金额] subset[金额] * 0.9 # 可能触发警告这个警告的本质是 Pandas 不确定subset是原表的视图还是副本修改它可能影响也可能不影响原表。正确做法是用.copy()明确创建一个独立副本subset df[df[金额] 500].copy() subset[金额] subset[金额] * 0.9加上.copy()之后警告消失而且你心里清楚改的是副本不会污染原数据。这个习惯我从学 Pandas 第一周就养成了至今没再被这个警告烦过。5. 分组聚合从明细到汇总的思维转换5.1 groupby 的本质是“拆分-应用-合并”groupby是 Pandas 里最核心也最容易用错的方法。它的工作模式分三步按某个键把数据拆成若干组对每组独立应用一个函数最后把结果合并回来。理解了这个三段式很多看似复杂的聚合需求就变得清晰了。# 按城市分组算金额总和与均值 result df.groupby(城市)[金额].agg([sum, mean])这里agg接收一个函数名列表一次性算出多个指标结果是一个以城市为索引、以指标为列的 DataFrame。比分别调用两次sum()和mean()再拼接要简洁得多。5.2 agg 的三种传参方式各有用途agg的灵活性体现在它接受多种传参形式我按使用频率排一下第一种是传函数名字符串列表就是上面那种适合对同一列做多种聚合。第二种是传字典指定不同列用不同聚合方式result df.groupby(城市).agg({ 金额: sum, 数量: mean, 订单号: count })这种写法在需要“金额求和、数量求平均、订单计数”这种混合需求时特别清晰。第三种是传自定义函数适合内置方法覆盖不到的场景比如算极差result df.groupby(城市)[金额].agg(lambda x: x.max() - x.min())自定义函数虽然灵活但性能不如内置方法数据量大的时候要慎用。初级训练阶段先把前两种练熟覆盖九成需求没问题。5.3 transform 和 agg 的区别必须搞清楚这是初学者最容易混淆的一对方法。agg返回的是每组一行的汇总结果行数等于组数transform返回的是和原表等长的结果把每组的聚合值“广播”回每一行。举个具体例子就明白了# agg每个城市一行 city_total df.groupby(城市)[金额].agg(sum) # transform每行都带上所属城市的总金额 df[城市总额] df.groupby(城市)[金额].transform(sum)transform的典型用途是算“占比”——用每行金额除以所属城市总额就能得到该笔订单在城市内的占比。这种“组内相对值”的计算用agg是做不到的必须用transform。我当初学的时候就是靠“占比计算”这个场景彻底分清了这两个方法。5.4 多列分组与结果索引的处理按多个列分组时传一个列表就行result df.groupby([城市, 品类])[金额].sum()这样得到的结果是多层索引MultiIndex城市在外层、品类在内层。多层索引在后续操作里有时不太方便可以用reset_index()把它拍平成普通列result df.groupby([城市, 品类])[金额].sum().reset_index()拍平之后就是标准的三列表格方便导出或者继续和其他表连接。我个人的习惯是只要分组结果要进入下一步处理就先reset_index()避免多层索引带来的各种对齐问题。提示groupby默认会把分组键作为索引加as_indexFalse参数可以在分组时就直接保留为普通列效果等同于分组后reset_index()少写一步。6. 表连接与数据整合的实战逻辑6.1 merge 的四种连接方式对应四种业务场景merge是整合多张表的核心工具它的how参数决定连接方式每种对应不同的业务含义连接方式保留哪些行典型场景inner两表键都存在的行只要匹配上的记录left左表全部行以主表为准补充右表信息right右表全部行以右表为准outer两表所有键全量对照找差异日常用得最多的是left因为业务上通常是“以订单主表为准去关联商品信息表或用户信息表”。inner适合做交集分析outer则常用于核对两张表的数据是否一致。orders pd.DataFrame({ 订单号: [A001, A002, A003], 商品编码: [P01, P02, P01] }) products pd.DataFrame({ 商品编码: [P01, P02, P03], 商品名: [耳机, 外套, 巧克力] }) result orders.merge(products, on商品编码, howleft)这段代码把商品名补充到了订单表上P03因为订单表里没有对应记录不会出现在结果里如果订单表里有商品编码在商品表里找不到商品名会是NaN。6.2 连接键不一致时的处理办法实际工作中两张表的连接键经常不同名比如一张叫“订单号”、另一张叫“order_id”。这时候用left_on和right_on分别指定result orders.merge(other, left_on订单号, right_onorder_id, howleft)连接完之后结果里会同时保留两列键如果不需要可以drop掉其中一列。另一个常见问题是键的数据类型不一致——一边是字符串一边是整数merge会静默地匹配不上结果全是NaN。这种情况排查起来很隐蔽我的做法是连接前先print两边的dtypes确认一下养成习惯能省很多调试时间。6.3 concat 做的是“堆叠”不是“连接”concat和merge经常被混为一谈其实它们解决的是完全不同的问题。merge是横向按列拼接需要键匹配concat是纵向按行堆叠结构相同直接摞起来。比如把一月的销售数据和二月的销售数据合到一起用concatall_sales pd.concat([jan_sales, feb_sales], ignore_indexTrue)ignore_indexTrue的作用是重新生成连续的行索引否则两段数据的索引会重复。这个参数在堆叠多段数据时几乎必加我一开始总忘结果后面按索引取值时各种错乱。横向拼接也可以用concat加axis1参数但要求两边的行索引对齐否则会出现大量NaN。这种场景其实用merge更稳妥concat(axis1)我只在明确知道两边索引一致时才用。7. 透视表与行列视角切换7.1 pivot_table 是 groupby 的“可视化版本”pivot_table做的事情本质上和groupby一样都是分组聚合区别在于它把结果排成二维表格——一个维度做行、一个维度做列交叉处是聚合值。这种布局在汇报和阅读时直观得多。result df.pivot_table( index城市, columns品类, values金额, aggfuncsum, fill_value0 )这段代码生成一张“城市 × 品类”的金额汇总表行是城市、列是品类、格子里是金额总和。fill_value0把没有数据的格子填成零避免出现NaN影响后续计算。这个参数在生成报表时几乎必加因为空单元格在展示时很难看。7.2 多级聚合与边际汇总pivot_table支持传多个aggfunc也支持加边际汇总就是表格最后一行一列的“总计”result df.pivot_table( index城市, columns品类, values金额, aggfunc[sum, mean], marginsTrue, margins_name合计 )marginsTrue会自动加上行列汇总margins_name指定汇总行的标签。这个功能在做经营分析时特别省事不用手动算总计。不过要注意多指标加边际汇总会让表结构变复杂列变成多层索引导出前可能需要处理一下列名。7.3 melt 把宽表拉回长表pivot_table是长转宽melt是宽转长两者互为逆操作。什么时候需要melt典型场景是数据可视化——很多绘图库要求数据是“长格式”即每一行是一个观测、每一列是一个变量。比如你有一张“城市 × 各月销售额”的宽表要画折线图就得先melt成长表long_df wide_df.melt( id_vars城市, value_vars[一月, 二月, 三月], var_name月份, value_name销售额 )id_vars是不参与转换的标识列value_vars是要拉长的列var_name和value_name分别给新生成的“变量名列”和“数值列”命名。这个转换在数据整理阶段用得很多尤其是从 Excel 里导出来的交叉表几乎都要先melt才能做后续分析。8. 训练中反复踩到的几个坑8.1 链式赋值与视图副本的纠缠前面提过SettingWithCopyWarning这里再补充一个更隐蔽的变体链式索引赋值。像df[df[金额] 500][金额] 0这种写法Pandas 可能改的是临时副本原表纹丝不动而且不一定报错。这种“静默失败”最坑人你以为改了其实没改。正确写法永远是先筛选出副本再改或者用loc一步到位df.loc[df[金额] 500, 金额] 0loc的“行条件 列名”写法是官方推荐的修改方式既不会触发警告也保证改的是原表。这个写法我建议你从一开始就养成习惯别用链式索引。8.2 中文字段名带来的隐形麻烦用中文列名本身没问题Pandas 完全支持但在两个地方容易出状况一是query里列名含空格或特殊符号要用反引号二是某些老版本在eval和query里对中文支持不完善。我的建议是如果数据最终要进代码流程列名尽量用英文或拼音如果只是临时分析、要出中文报表那用中文列名更直观但记得在query里加反引号兜底。8.3 数据类型在聚合后的悄然变化一个容易被忽略的点整数列一旦出现NaN整列会自动变成浮点型。这在填充缺失值之后特别常见——原本是整数的“数量”列填了个NaN再参与计算结果变成了1200.0这种带小数点的形式。如果最终要输出整数记得在合适的时候用astype(int)转回来但要确保此时已经没有缺失值否则转换会报错。df[数量] df[数量].fillna(0).astype(int)先填充再转换顺序不能反。这个细节在生成报表时很重要带小数点的“件数”看着就不专业。8.4 排序时缺失值的默认位置sort_values默认把NaN放在最后na_positionlast升序降序都一样。如果你希望缺失值排在最前面加na_positionfirst。这个参数平时不太用得上但在做“缺失数据排查”时很有用——把缺失值排到最前面一眼就能看到哪些行需要处理。df.sort_values(金额, ascendingFalse, na_positionfirst)另外多列排序传列表就行sort_values([城市, 金额], ascending[True, False])表示城市升序、金额降序。这种“先按组排、组内再按值排”的需求在生成分组报告时很常见。9. 把训练题串成一条完整流水线9.1 从原始数据到分析结果的端到端示例单题训练的目的是熟悉单个动作但真实工作要的是把动作串起来。我给一个完整的流水线示例把前面讲的能力都用上读数据、体检、清洗、筛选、分组、连接、透视一气呵成。import pandas as pd # 1. 读取 df pd.read_csv(sales.csv) # 2. 体检 print(df.shape, df.dtypes.to_dict()) # 3. 类型修正 df[金额] pd.to_numeric(df[金额], errorscoerce) # 4. 清洗 df df.drop_duplicates(subset[订单号], keepfirst) df[金额] df[金额].fillna(df[金额].median()) df[城市] df[城市].fillna(未知) # 5. 筛选 valid df[df[金额] 0].copy() # 6. 分组聚合 city_summary valid.groupby(城市).agg( 订单数(订单号, count), 总金额(金额, sum), 客单价(金额, mean) ).reset_index() # 7. 连接维表 city_info pd.DataFrame({ 城市: [北京, 上海, 广州], 大区: [华北, 华东, 华南] }) city_summary city_summary.merge(city_info, on城市, howleft) # 8. 透视 pivot valid.pivot_table( index城市, columns品类, values金额, aggfuncsum, fill_value0 )这条流水线跑通说明你对初级操作已经形成了完整的认知。每一步的输出都可以print出来检查确认无误再进入下一步。我强烈建议你在自己的环境里把这段代码跑一遍然后故意制造一些脏数据比如把某列改成字符串、插入重复行看每一步会怎么反应——这种“破坏性测试”比按部就班跑通更能加深理解。9.2 每一步都留检查点流水线写长了出错时很难定位是哪一步的问题。我的做法是每个关键步骤后加一个轻量检查比如打印行数变化print(清洗前:, len(df)) df df.drop_duplicates(subset[订单号]) print(删重后:, len(df))行数变化是最直观的健康指标。如果删重后行数没变说明要么本来就没重复要么subset写错了如果删重后行数骤降那得回头看看是不是误删。这种“打点”习惯在调试长流程时价值极高比事后用断点调试快得多。9.3 初级训练之后的进阶方向把这一章的题目练熟你处理日常表格数据基本不会卡壳了。接下来可以往三个方向深入一是时间序列处理to_datetime、resample、滚动窗口这些在业务分析里用得极多二是性能优化比如用category类型压缩内存、避免apply改用向量化操作三是多表复杂关联包括多键连接、连接后的去重和校验。这三个方向我在后续章节会分别展开但前提是初级的这些动作你已经能闭着眼睛写出来。我个人在实际操作中的体会是Pandas 的熟练度不取决于你记住了多少方法而取决于你面对一份新数据时脑子里有没有一条清晰的“先干什么、再干什么”的路径。训练题的意义就是帮你把这条路径刻进肌肉记忆里。刚开始可能每道题都要查文档练到二十道左右你会发现很多操作已经不用想了手自己就敲出来了——那个时刻就是真正入门的标志。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑