Pandas实战:从数据读取清洗到分组聚合与时间序列分析

发布时间:2026/10/7 2:37:21
Pandas实战:从数据读取清洗到分组聚合与时间序列分析 简介《Pandas数据分析实战》是Pandas Cookbook英文原版的PDF版电子书聚焦Pandas在科学计算、时间序列分析和数据可视化中的高级应用。资源包内共1个PDF文件大小约53.38MB便于下载后随时研读。全书以近百个源自真实项目的实战案例为主线系统讲解数据清洗、分组聚合、时间序列处理、布尔索引与多重索引等核心操作并演示如何通过与Matplotlib、Seaborn的集成快速创建兼具信息密度与美观度的图表。各专题按应用需求组织既可通读也适合按需查阅每个案例均配有详细解决方案与可复现代码强调高效地道的编程实践帮助读者掌握从数据子集隔离、分组转换、多源数据合并到数据集重整理的全流程技能为机器学习任务准备整洁数据并应用于金融、科学等真实场景。已有271人浏览学习适合具备一定Python基础、希望从实战案例入手掌握Pandas高级应用的数据分析人群。1. Pandas数据分析实战从一行代码读数据到能独立交付分析结论Pandas在Python数据分析里的地位不需要多讲但凡用Python处理过表格、CSV、Excel的人几乎都绕不开它。可现实里真正用Pandas接手过一个脏乱差的实际数据的人都知道这东西入门容易想稳定跑通一套完整分析链路并不轻松光是一个编码问题就能挡住半天进度一个不显眼的数据类型错位会在聚合后悄悄淹没你的结果。这篇笔记要拆的就是Pandas实战中最常见、也最值得提前避开的那些环节数据读取与清洗、分组聚合与透视表、多表合并与时间序列处理最后用一个能跑完的小案例把这些动作串起来。适合刚学完Pandas基础语法、准备拿真实数据练手的开发者也适合已经在用Pandas但总在数据清洗和类型转换上翻车的分析岗从业者。2. 数据读取与清洗read_csv参数没吃透后面全是坑2.1 read_csv核心参数与编码处理Pandas读取数据的入口基本就是read_csv但这个函数参数非常多实际中用得最勤的其实是下面这一组文件路径、编码、指定列数据类型、解析时间列、只读取需要的列。import pandas as pd df pd.read_csv( order_data.csv, encodingutf-8, dtype{phone: str, order_id: str}, parse_dates[order_time], usecols[order_id, user_id, phone, amount, order_time, city], )逻辑说明大多数情况下直接pd.read_csv(file.csv)也能读但一旦文件里有中文、电话号码带前导零、日期是字符串格式默认读取方式就会把phone读成float、把order_time读成object。这段代码用dtype参数强制把phone和order_id按字符串读避免前导零丢失parse_dates让pandas在读取时直接完成日期解析之后就能用dt访问器做月份、星期等维度分析usecols只加载需要的列文件很大时能明显减少内存占用。参数说明方面encoding这个参数值得多说一句。utf-8不是万能解药很多业务系统导出的CSV是GBK或GB18030编码读的时候直接报UnicodeDecodeError。我一般会准备一个小函数做编码兜底先尝试utf-8失败后用gbk再失败就尝试gb18030这三种能覆盖绝大多数国内业务系统的导出文件。另外一个高频参数是sep默认逗号分隔但有些系统导出的是制表符或竖线分隔不指定sep会把整行读成一列。遇到这种情况先打开原始文件看一眼分隔符别急着改代码。2.2 类型错乱、缺失值与重复行的清洗思路数据读进来之后第一步不是急着分析而是先摸清数据的“脾气”。常见做法是先跑df.info()看每一列的非空数量和dtype再跑df.describe()看数值列的分布。如果某列应该是数值型但dtype显示object说明里面有非数字的脏值常见来源是千分位逗号、单位文字、全角数字混入。df[amount] ( df[amount] .astype(str) .str.replace(,, , regexFalse) .str.replace(元, , regexFalse) .astype(float) ) df df.drop_duplicates(subset[order_id], keeplast) df df.dropna(subset[order_id, amount])逻辑说明第一段把amount先强制转成字符串去掉千分位逗号和“元”字再转成float。这一串操作看起来绕但比直接用pd.to_numeric(errorscoerce)更可控因为coerce会把脏值静默变成NaN你根本不知道哪些行被吞掉了。第二段按order_id去重keeplast表示保留最后一条这适合订单有修正记录的导出场景第三段dropna只针对关键字段不做全表删除。参数说明drop_duplicates的subset参数很关键去重前先想清楚业务上“重复”的定义是什么。订单表按订单号去重没问题用户表按user_id去重合理但如果直接对整个DataFrame调用drop_duplicates不带subset只有整行完全一致才被删除这在实战里往往不是你要的效果。dropna同理全表空值删除会误伤很多本来可以保留的行一定要用subset限定范围。3. 分组聚合与透视表groupby写不好分析结论就是空中楼阁3.1 groupby底层逻辑与agg组合用法分组聚合是Pandas分析里最核心的动作。很多初学者习惯groupby之后接一个mean或sum但真正到实战里一个分析维度往往同时需要多个统计量而且还要跨多列计算。用agg一次搞定比分开写好几行要清晰得多。city_stats ( df.groupby(city) .agg( 订单量(order_id, count), 销售额(amount, sum), 客单价(amount, mean), 最大单笔(amount, max), ) .reset_index() .sort_values(销售额, ascendingFalse) )逻辑说明groupby(city)按城市分组agg里用命名元组的写法每个统计量是一个(列名, 函数)二元组输出列名就是指定的中文名做出来的结果可读性很高。reset_index把city从索引里释放成普通列这样后面如果要跟其他表合并可以直接用city做连接键。sort_values按销售额降序排这样结果一眼就能看到头部城市。参数说明agg里的函数可以是字符串方法名如sum、mean、count、max、min、median也可以是numpy函数或自定义lambda。count和size的区别很多人搞不清count只统计非空值个数size统计分组内的总行数如果一列有缺失值这两者结果不同。做订单量统计建议用count配合具体列名别用size因为size容易掩盖异常缺失。另外还有一个高频需求是分组后计算某个值占组内总量的比例。这个用transform比用merge更高效df[城市销售额占比] df.groupby(city)[amount].transform(sum) df[单笔占比] df[amount] / df[城市销售额占比]逻辑说明transform返回的是和原DataFrame等长的Series不会把分组维度折叠掉。先算出每笔订单所属城市的总销售额再用每一笔金额除以这个城市总额得到单笔订单在所属城市销售额中的占比。这种做法省去了合并步骤代码也更好读是pandas实战里很实用的技巧。3.2 透视表与交叉表的选型groupby能做80%的分组需求但有些场景用pivot_table表达更符合直觉尤其是需要行列两个维度交叉展示的时候比如看“不同城市在不同月份”的销售额表现。pivot pd.pivot_table( df, valuesamount, indexcity, columnsdf[order_time].dt.month, aggfuncsum, fill_value0, )逻辑说明pivot_table的index是行维度columns是列维度values是要聚合的数值列aggfunc默认是mean但这里显式指定为sumfill_value0让没有数据的交叉格填0而不是NaN。这种表非常适合直接丢进Excel或做热力图。groupby做同样的交叉聚合也能实现但pivot_table的结构更直观尤其是最后输出给非技术同事看的时候行列分明比长表好读得多。选型上我的习惯是只要最终展示需要“行×列”的矩阵结构就直接用pivot_table如果后续还要做进一步的长表变换或合并就用groupby加reset_index。两者的底层都是分组聚合只是输出形态不同不需要纠结性能差异数据量在百万行以下时没有明显差别。3.3 数值分箱与标签编码连续数值转分类标签是分析里的高频操作比如把客单价分成低中高三档。pandas的cut做这件事很顺手。bins [0, 50, 200, float(inf)] labels [低客单, 中客单, 高客单] df[客单档位] pd.cut(df[amount], binsbins, labelslabels, rightFalse)逻辑说明pd.cut把amount按bins切分成三段rightFalse表示区间左闭右开即[0, 50)为低客单[50, 200)为中客单[200, ∞)为高客单。labels参数给每个区间起名字生成的结果是Categorical类型排序时会按区间顺序而不是字典序这对后续聚合很有用。参数说明bins可以传整数表示等分成几段也可以传列表自定义断点。如果数据里有负数或不希望出现无穷大断点可以用数据里的实际最大最小值来做边界但要注意万一新数据超出范围会变成NaN。cut和qcut的区别也要拎清楚cut按数值区间切qcut按分位数切保证每段样本量大致相等。做用户分群或城市分层时qcut更常用做业务口径明确的区间划分时cut更合适。4. 多表合并与时间序列merge语义不清数据翻倍了都不知道4.1 merge连接语义与常见翻车点真实数据分析极少只靠一张表订单表要关联用户表拿城市要关联商品表拿品类这就要用merge。merge最核心的是搞清how参数四种连接的含义以及on参数指定的连接键在两表里是否唯一。users pd.read_csv(users.csv, encodingutf-8, dtype{user_id: str}) products pd.read_csv(products.csv, encodingutf-8, dtype{product_id: str}) df_merged pd.merge( df_orders, users, onuser_id, howleft, suffixes(_订单, _用户), validatemany_to_one, )逻辑说明这是订单表左连接用户表的标准写法订单表的每一行都会保留users表里匹配不到的用户信息用NaN填充。suffixes参数解决两张表重名列的问题比如两边都有create_time加了后缀后就能区分是订单创建时间还是用户创建时间。validatemany_to_one是做校验如果users表里user_id有重复pandas会直接报错而不是默默返回翻倍的结果。参数说明validate是很多人忽略的参数但它能救命。它接受one_to_one、one_to_many、many_to_one、many_to_many四种取值。在写merge之前先想清楚业务逻辑订单表对用户表是many_to_one用户表对用户画像表是one_to_one。加上validate之后一旦源数据里有脏数据导致唯一性被破坏代码立即报错你就能提前发现底层数据问题而不是带病分析。还有一个高频问题是连接键的类型不一致左边是str右边是intmerge时匹配不上但又不报错结果全是NaN。解决办法是在merge之前检查两边连接键的dtype确保一致这点在data类型转换上的坑后面细说。4.2 时间序列处理与重采样时间列解析成datetime类型之后能做的事就多了。最常见的是按月、按周汇总以及计算同比环比。resample是处理这类需求的入口但它的前提是索引必须是datetime类型。df_time df.set_index(order_time) monthly ( df_time.resample(M)[amount] .agg([sum, count, mean]) .reset_index() ) monthly[环比增长] monthly[sum].pct_change()逻辑说明set_index把order_time设为索引resample(M)按自然月聚合对amount列同时计算月度销售额总和、订单数和客单价。reset_index把月份索引还原成普通列方便后续和其他表合并或输出Excel。pct_change计算环比增长率第一行自然是NaN表示没有上期数据。参数说明resample的规则字符串里M是自然月末MS是月初W是每周日截止D是自然日Q是季度Y是年末。如果业务按周一到周日计算可以写成resample(W-MON)表示每周一截止统计上一周。这里有个坑值得单独强调resample默认是分组键右闭区间M会落在每个月最后一天如果原始数据未来有新增记录重采样结果不会包含这些新增直到你再跑一次。做报表的时候要注意这个时间边界别让统计口径出差错。时间字段的解析还有一个容易忽略的点就是dt访问器的用法。order_time解析成datetime后df[order_time].dt.month拿月份dt.year拿年份dt.dayofweek拿星期几dt.hour拿小时。这些导出的维度可以直接作为分组字段使用是时间分析最顺手的一组工具。5. Pandas实战常见问题排查五条血泪经验5.1 带链式赋值的SettingWithCopyWarning告警现象执行df[df[city] 上海][amount] 0时出现SettingWithCopyWarning有时修改还不生效。原因方括号连续操作返回的是原DataFrame的视图或副本pandas无法确定你是在改副本还是原数据于是发警告。解决不要用链式赋值先loc定位再赋值。df.loc[df[city] 上海, amount] 0这段代码用loc把行条件和列条件一次传进去pandas明确知道要修改原DataFrame不再触发告警。从那以后我每次写赋值逻辑都强制走loc哪怕多写一行也值。5.2 读取CSV时数字列被读成object现象read_csv读完后df.info()显示amount列是object后续astype(float)报错。原因该列里有非数字脏值或空值。解决先用pd.to_numeric逐个排查。df[amount] pd.to_numeric(df[amount], errorscoerce)errorscoerce让无法转换的值变成NaN然后配合isna()查出脏值位置再决定是修正原数据还是删除。整个过程是先定位脏值来源再决定处理策略绝不能硬转。5.3 日期字符串解析后的时区与格式陷阱现象parse_dates之后数据能读但dt.month取出来比实际少一个月。原因原始时间字符串带时区偏移或格式不标准pandas解析时按UTC处理。解决读取时指定dayfirst或统一转成无时区的datetime。df[order_time] pd.to_datetime(df[order_time], format%Y/%m/%d %H:%M:%S)format参数显式声明了原始格式后pandas不再猜测解析速度和准确性都提升明显。国内导出文件常见“2024/01/05 14:30:00”这种格式不指定format时pandas可能按US格式解析成1月5日但某些场景下会按5月1日解读这属于翻车风险最高的那类坑。5.4 merge后行数暴增或大量NaN现象merge前订单10万行merge后变成30万行且很多城市列是NaN。原因连接键在右表不唯一merge产生笛卡尔积。解决merge前先检查连接键的唯一性。assert users[user_id].is_uniqueis_unique是Series的一个属性返回布尔值。这一行代码如果用户表有重复user_id会直接抛AssertionError把问题暴露在merge之前。我现在只要写merge前面必然带这条校验已经成了肌肉记忆。5.5 内存占用过高导致程序卡死现象读取2GB的CSV后程序内存占用超过10GB后续操作极慢。原因pandas默认把读入的数字列按int64/float64存储内存占用远大于实际需要。解决读取时指定dtype和仅加载需要的列。df pd.read_csv( big_data.csv, usecols[order_id, amount, order_time, city], dtype{order_id: int32, amount: float32}, )int32和float32在精度满足需求时可以减半内存配合usecols只留必要列大数据量的读取体验完全不一样。这个方法在数据量几十万行时感觉不明显到几百万行以上差距立竿见影。6. 完整小案例电商订单分组对比分析的一次完整跑通把前面的操作串成一个能从头跑到尾的小案例目标是分析某电商平台不同城市、不同客单档位下的订单表现。数据是两张表order_data.csv和users.csv订单表有订单号、用户ID、金额、下单时间用户表有用户ID、城市、注册时间。import pandas as pd # 1. 读取数据并修正类型 orders pd.read_csv( order_data.csv, encodingutf-8, dtype{order_id: str, user_id: str}, parse_dates[order_time], ) users pd.read_csv( users.csv, encodingutf-8, dtype{user_id: str}, parse_dates[reg_time], ) # 2. 清洗订单表 orders orders.drop_duplicates(subset[order_id], keeplast) orders[amount] pd.to_numeric(orders[amount], errorscoerce) orders orders.dropna(subset[amount]) # 3. 合并用户维度 assert users[user_id].is_unique df pd.merge(orders, users, onuser_id, howleft, validatemany_to_one) # 4. 构造客单档位 bins [0, 100, 500, float(inf)] labels [低客单, 中客单, 高客单] df[客单档位] pd.cut(df[amount], binsbins, labelslabels, rightFalse) # 5. 城市档位透视 result pd.pivot_table( df, valuesamount, indexcity, columns客单档位, aggfunc[sum, count], fill_value0, ) # 6. 输出结果 result.to_excel(city_segment_report.xlsx)逻辑说明读取阶段就把订单号、用户ID设为字符串避免后续merge时类型不一致。清洗阶段先去重然后转换金额类型用to_numeric配合errorscoerce把脏值变成NaN再删除这一步能保证后续合并和聚合的真实性。merge前用is_unique断言确保用户表主键唯一避免数据翻倍。cut分档位后pivot_table按城市和档位交叉聚合sum和count同时输出表格结构适合直接给业务看。最后to_excel导出。验证结果的方法是抽查几个城市的订单量加总和原始订单表count做对比。如果对不上优先检查merge时是否有重复匹配其次看dropna是否删掉了过多行。这套流程每次跑完我都会强制走一遍核对逻辑确保输出数据经得起追问。做数据分析最怕的不是结果不对而是结果不对却不知道哪里不对。希望这些沉淀下来的操作习惯能帮你在Pandas实战里少走几次弯路。本文还有配套的精品资源点击获取