1. Pandas到底解决什么问题先聊清楚再学如果只让我选一个Python库来介绍给做数据分析的人我会第一个说Pandas。它可以说是Python数据分析生态里的“基础设施”不管是清洗脏数据、透视汇总、时间序列分析还是给老板做自动化报表几乎每一步都会用到它。Pandas库的名字源自“panel data”但你现在不用死记这个来源只需要记住一点它把Excel里那套表格操作用代码的方式重新实现了一遍而且能力更通用、更适合自动化。适合什么人呢我觉得有三类人最容易从中受益第一类是刚入门Python想做数据分析但不知道从哪儿开始的第二类是长期用Excel处理数据动作娴熟但一遇到几万行、几十个文件就卡到想摔键盘的第三类是已经会写一点Python但代码总被同事吐槽“慢”希望理解Pandas到底该怎么用才能更高效的。这篇Pandas库详细介绍不会把每一个函数都念一遍而是按我平时做项目的实际顺序把最常用的主干讲清楚附带大量踩坑经验看完就能直接在真实任务里用起来。很多人学Pandas前喜欢问它不是就是个“Python里的Excel”吗这种类比能帮新手建立感觉但很容易误导。Excel的优势在于即时、可见、交互感强Pandas的优势在于可编程、可复现、能处理更大规模的数据。你不是用Pandas代替Excel而是用Pandas把重复劳动变成代码把不可记录的人工操作变成可留痕的脚本。想明白这个你后面学起来会顺很多。1.1 它和Excel表格的本质区别我反复跟身边的人强调不要把DataFrame想成“电子表格”它更像“带有行索引和列标签的数据容器”。在Excel里单元格位置是靠A1、B2这种坐标定位位置一变公式引用全乱掉而在Pandas里定位数据主要靠标签。标签不会因为行顺序变化而失效所以你可以在不破坏原数据的前提下随意排序、筛选、拼接标签会一路跟着走。这个设计理念是整个Pandas用法的地基。再说规模。Excel处理几千行没问题但到了几十万行很多操作就肉眼可见地变慢一个xlsx文件也可能在电脑上卡死。Pandas在单机上处理几百万行数据还是有余力的尤其是内存足够的时候。当然它不是大数据引擎海量数据还是要靠其他方案但在入门阶段你几乎遇不到明显的瓶颈。1.2 Pandas在整个数据分析流程里的位置你可以把数据分析拆成四步采集数据、清洗和准备、分析和建模、展示结果。Pandas的核心作用集中在第二步和第三步之间。它负责把各种乱七八糟的原始数据读进来整理成干净规整的表格然后做筛选、分组、聚合、关联这些统计分析前的准备工作分析完之后它又能把结果整理成一张适合人看的表。所以你会发现Pandas像是一个“中转站”前接文件、数据库、网页接口后接可视化库和机器学习库。换个角度说Pandas并不适合用来做特别复杂的统计建模也不用来画特别精美的报告图。这些任务有更专业的工具去完成。它更像厨师手里的“案板”和“菜刀”把不同食材切好、配好后面煎炒烹炸才顺手。理解了这个定位你就不会拿它跟机器学习框架硬比也不会嫌弃它的画图能力不够好看。2. 先从两个核心数据结构说起Series和DataFramePandas的API非常多如果你把所有函数都背下来那基本是不现实的。我学Pandas的时候走了很长一段弯路一开始就拿着参考手册硬啃结果发现函数几百个几天就忘了。后来我才明白Pandas的核心其实只有两个数据结构把Series和DataFrame搞明白了很多操作自然就通了。因为Pandas几乎所有的操作都是在这两种结构上进行输入、变换、输出。2.1 从一列数据开始认识SeriesSeries可以理解为“带标签的一维数组”。它和Python内置列表最大的区别在于每个元素除了位置还有一个索引标签。举个例子import pandas as pd s pd.Series([10, 20, 30], index[一月, 二月, 三月]) print(s)输出结果里左边是索引右边是数值。你可以通过标签访问比如s[二月]会得到20也可以像数组一样用位置访问比如s.iloc[1]。索引的好处是即使行的顺序被打乱只要标签还在数据就不会认错。如果你的数据本身没有特别含义也可以不传indexPandas会自动生成从0开始的整数索引。我发现很多初学者对Series的存在感不强因为实际项目里更常接触DataFrame。但请记住DataFrame里的每一列本质上都是一个Series。你单独取一列时得到的就是Series你可以对它做各种运算算完再放回DataFrame里。这个理解后续非常有用。2.2 DataFrame表格数据的核心容器DataFrame可以看作若干Series共享同一个索引的集合也就是一张二维表行有行索引列有列名。最直观的创建方式是用字典键是列名值是列表或Seriesdf pd.DataFrame({ 姓名: [A同学, B同学, C同学], 成绩: [88, 92, 76] }) print(df)DataFrame会自动生成整数索引也可以事后用set_index自己指定。刚拿到一张表我建议先做三个动作head()看前几行、info()看列类型和缺失值、describe()看数值列的统计概览。这三个方法能帮你在几分钟内对数据形成整体感觉而不是直接埋头处理。很多文档会强调shape、columns这些属性我觉得你只要记住“DataFrame是二维的、行列都能命名、每列有类型”这三个特点就够了。后续所有操作本质上都是在这张表上做行选择、列选择、变换、合并和聚合。2.3 索引为什么是Pandas的“灵魂”索引是Pandas区别于普通二维数组的关键。两个DataFrame之间做运算时Pandas会优先按索引对齐而不是按位置。这个“自动对齐”特性一开始容易让人困惑但理解了之后特别省事。df1 pd.DataFrame({a: [1, 2]}, index[x, y]) df2 pd.DataFrame({a: [10, 20]}, index[y, x]) print(df1 df2)这里两个DataFrame的索引顺序不同但相加结果会自动把“x行对x行、y行对y行”匹配起来而不是左表第一行加右表第一行。如果你希望严格按位置相加反而要额外用reset_index或者.values把索引抹掉。这一点如果不提前说很多人第一次看到结果都会愣住。索引也分普通索引和唯一索引。普通索引允许重复比如多行可能是同一个日期但如果做行定位重复索引会导致一次选择返回多行初学者容易踩坑。我的习惯是能保持唯一索引的列就尽量保持唯一例如订单号、编号等普通分析表则用reset_index把原始索引转成一列避免后续合并时误用。3. 数据读取、清洗与准备日常耗时最多的环节最近经常有人问我“项目里的脏数据怎么处理”说句实话数据分析里真正耗时间的往往不是核心模型而是数据清洗。大多数人以为Pandas的精华是统计函数其实它的看家本领是快速地把不规整的数据收拾干净。这一章我按实际数据流的顺序来讲从读取开始到缺失值、重复值、类型转换、文本和时间处理每一步都带上实操参数你可以直接抄作业。3.1 文件读写里的几个关键参数read_csv是最常用的函数。很多人只写pd.read_csv(路径)就完事结果中文乱码或数据类型不对。实操上建议至少关注几个参数encoding经常用到utf-8或gbk这直接决定中文乱不乱。如果解析报错或出现乱码先试encodinggbk。parse_dates把时间列解析为datetime类型比如parse_dates[日期]省得手动转。dtype指定每列类型例如dtype{金额: float64}能避免长数字被读成科学计数法。usecols只读需要的列文件很大时能明显减少解析时间。nrows先读前几行快速预览大文件确认格式后再生读。df pd.read_csv( 销售明细.csv, encodingutf-8, parse_dates[日期], dtype{数量: int32}, usecols[日期, 商品, 数量, 金额] )写入文件时to_csv默认会带上行索引但很多场景我们不想把索引存进去所以习惯写df.to_csv(输出.csv, indexFalse)。如果是写入Excelto_excel需要额外依赖openpyxl而且可以用ExcelWriter把多张表写入同一个文件的不同sheet非常实用。这些细节文档里有但没有人提醒你你很容易卡在“为什么报错找不到engine”这种小问题上。3.2 缺失值与重复数据怎么处理Pandas用NaN表示缺失值。但要注意Python里的None会被转成NaN而空字符串不是NaN它是。所以清洗前先统一判断否则你用isnull查不到空字符串数据里其实还有一堆“看起来空”的值。常用方法有三个角度检测缺失值用isnull和notnull删除缺失值用dropna填充缺失值用fillna。删除时可以用subset指定检查哪些列比如df.dropna(subset[订单号])只删除订单号缺失的行而不是整行出现缺失就删。填充时有时候用固定值有时候用均值、中位数时间序列里则经常用前向填充ffill或后向填充bfill。df df.dropna(subset[订单号]) df[金额] df[金额].fillna(df[金额].median()) df df.drop_duplicates(subset[订单号, 商品], keeplast)为什么我建议df df.dropna(...)而不是df.dropna(..., inplaceTrue)因为原地修改容易造成代码执行顺序上的误会尤其在函数链上调试时很难说清数据是哪一步被改的。返回新对象的写法更清晰也有利于链式操作。3.3 类型转换与字符串、时间处理类型转换是清洗里的基本功。astype可以把整列转换成指定类型但如果一个“数字列”里混入了几个文本字符astype(float)会直接报错这在实际报表里太常见了。更稳妥的是用pd.to_numeric设置errorscoerce把无法转换的值变成NaN然后你再决定是填充还是删除。df[金额] pd.to_numeric(df[金额], errorscoerce) df[金额] df[金额].fillna(0)字符串处理依靠.str访问器。比如df[姓名].str.strip()去掉首尾空格df[手机号].str.contains(138, naFalse)做模糊匹配。时间处理也是重点pd.to_datetime可以解析各种格式的日期解析成时间类型后你可以通过dt.year、dt.month快速取出年月也可以把它设为索引然后用切片df[2024-01-01:2024-01-31]直接选出时间段。这个能力在写月度报表时非常节省时间。4. 筛选、分组和聚合数据分析的主战场讲完清洗就进入最好用的筛选和数据聚合。我曾经让一个只学过Excel的人试Pandas的groupby他第一反应是“原来汇总还可以这么自由”。确实透视表和分组聚合在Excel里要拖来拖去在Pandas里就是一行式子。这个章节讲的是日常分析中最常用的筛选、分组、聚合逻辑也是从“会读表”跨向“会分析”的关键阶段。4.1 用loc和iloc做筛选别一上来就喜欢链式写法loc按标签选择iloc按位置选择。比如# 取前5行、第1到第3列 df.iloc[:5, 1:4] # 取“成绩”列大于90的所有行只看姓名和成绩列 df.loc[df[成绩] 90, [姓名, 成绩]]注意df[成绩] 90得到的是一个布尔Series把这个布尔列表放在.loc[]的第一个参数位置上就能筛出行。复杂条件用与和|或但必须给每个条件加括号否则会报语法错误。这也是新手最常犯的问题写成df[成绩] 90 df[年龄] 20会被Python解释器误解。如果条件特别长你可以用query方法比如df.query(成绩 90 and 年龄 20)用起来像SQL可读性也好。但query在复杂表达式上性能不一定比布尔索引好所以小数据无所谓数据量大了还是建议.loc。4.2 groupby的底层逻辑与常用组合groupby本质上就是“拆分-应用-合并”三步先把数据按某个列拆成若干组然后对每一组做聚合计算或变换最后把结果合并成一张新表。最简单的例子df.groupby(类别)[金额].agg([sum, mean, count])一行代码就得到了每个类别的总金额、平均金额和订单数量。你可以按多列分组比如df.groupby([地区, 年份])[金额].sum()得到分地区、分年份的汇总。需要注意的是分组列默认会成为结果的索引如果希望保持普通列的形式加上as_indexFalse或事后reset_index()。分组后除了agg还有一个容易被忽略的transform。agg返回的是“每组一行”的结果等于压缩了行数而transform会保留原来的行数把统计值广播回每一行。比如你想算“每个订单金额占该客户总金额的比例”用groupby(...)[金额].transform(sum)先拿到每个客户的总金额然后直接除这样就能避免循环逐行计算的痛苦。4.3 pivot_table与apply方法的使用边界如果你熟悉Excel透视表看到pivot_table会非常亲切。它的作用是把某一列的值展开成新的列另一列的值作为行中间放聚合结果。例如pd.pivot_table(df, index地区, columns季度, values金额, aggfuncsum, fill_value0)这样你能快速看到每个地区在不同季度的销售金额比手工写多个筛选条件高效得多。使用的时候多注意aggfunc默认是mean但业务上更常用sum或count等。如果不希望结果里有缺失值设置fill_value0会更干净。apply是Pandas里的“瑞士军刀”可以对行或列做自定义函数处理比如df[调整后金额] df.apply(lambda row: row[金额] * row[折扣] if row[会员] 是 else row[金额], axis1)。但它的缺点是慢而且如果你能用向量化操作实现就不要用apply。所谓向量化就是直接对整列做运算例如df[调整后金额] df[金额] * df[折扣]。大多数情况下系统内置方法比你写的逐行函数快得多。这个“能向量化就向量化”的习惯到了大数据量时会让你受益很多。5. 性能优化和代码习惯不要等数据大了才后悔很多人开始学Pandas只关心功能不关心代码跑得有多快。一旦真实数据变成几十万行同样的逻辑写法不同运行时间能差十几倍。我见过一个模拟项目的脚本用for循环逐行更新字段结果跑五分钟还没结束改成向量化操作后几秒就完成了。所以这一章我专门讲性能优化和代码习惯越早建立这个意识越好。5.1 向量化操作速度差在哪里Pandas底层是数组化的大多数内置操作会对整列做C级循环比Python级的for循环快得多。使用Python循环时每处理一行都要解释一次语句加上类型检查开销很大而向量化操作能一次性批量处理所有数据。举例来说如果你要给每一行计算销售额正确写法是df[销售额] df[单价] * df[数量]而不是for i in range(len(df)): df.loc[i, 销售额] df.loc[i, 单价] * df.loc[i, 数量]这不是说循环完全不能用而是说在Pandas里你应该优先思考“如何用整列运算表达逻辑”。比如比较大小、字符串拼接、时间提取都可以用.str、.dt这些访问器完成。如果你在内置方法里找不到合适函数再考虑apply只有确实无法用Numpy或Pandas表达时才退回去写逐行逻辑。5.2 为什么总有人碰到SettingWithCopyWarning很多初学者写代码时总会看到一段警告SettingWithCopyWarning它的意思是“你正在尝试修改一个副本而不是原始DataFrame”。典型错误写法df[df[成绩] 90][及格] 1这条语句先做筛选得到一个副本然后试图给这个副本赋值结果原表根本没变化。正确写法是df.loc[df[成绩] 90, 及格] 1它会在原始DataFrame上直接定位行和列再赋值既不会产生警告也不会出现“改了半天数据没变”的问题。遇到这个警告时不要简单地用pd.options.mode.chained_assignment None关掉它而是要认真检查代码哪里产生了链式操作。另一个相关的好习惯是当你切分出一个子表要单独修改时明确用.copy()复制一份这样修改副本不会污染原始数据也不会触发警告。比如sub_df df[df[地区] 华东].copy()。我看项目代码时特别喜欢检查.copy()的位置因为它反映了作者有没有意识到“视图和副本”的区别。5.3 大数据场景下的几点降级思路Pandas单机运行数据量大了难免内存吃紧。我建议在读取阶段就做优化而不是等到内存溢出才补救。第一降低列的类型精度。把float64改成float32整数列根据范围用int32甚至int16内存能省不少。类别型字符串可以转成category类型比如地区、商品分类等能显著压缩内存。第二分块读取。read_csv支持chunksize参数让你每次只读一部分行处理完再合并。数据特别大时可以用迭代器方式边读边清洗最后pd.concat汇总。第三写代码时多用pd.concat而不是过时的append多用merge处理关联少用嵌套循环。如果你有复杂的表关联需求也优先考虑merge而不是apply去逐行匹配。如果你的数据已经大到Pandas单机跑不动可以考虑Dask或Polars这类兼容Pandas风格的工具。不过这不是当前重点对绝大多数人来说用上述方式优化后Pandas能处理的数据量已经足够了。6. 常见报错与排查速查表我遇到过的Pandas报错已经数不清了。很多错误看起来千奇百怪但根源往往集中在几个地方索引没对齐、类型不对、视图和副本混淆、文件编码问题。这里我把高频问题整理成一张速查表方便你对号入座。报错或警告常见原因解决办法KeyError: xxx用不存在的列名访问数据先检查columns确认别名是否有多余空格SettingWithCopyWarning链式赋值导致修改了副本改用.loc[row_cond, col] value必要时.copy()ValueError: cannot convert float NaN to integer整数列存在缺失值先用fillna填充或选择可空整数类型Int64ParserError或中文乱码编码不匹配尝试encodinggbk、encodingutf-8TypeError: unsupported operand两列类型不兼容先pd.to_numeric或pd.to_datetime统一类型MemoryError数据量超出内存减少读取列数、降低dtype精度或分块处理PerformanceWarning操作涉及非唯一索引排序索引或用reset_index重新整理6.1 高频报错对照表上面这张表适合放在手边报错时先对号入座。比如KeyError十次里有八次是列名的问题。Excel里的列头可能带着不可见空格读进来后你看到的“姓名 ”和实际列名“姓名”不是同一个东西。遇到这种情况先执行一次df.columns df.columns.str.strip()能把很多莫名问题一次性解决。再比如cannot convert float NaN to integer是因为Pandas里的整数列不支持缺失值而你的数据里刚好有NaN。传统解决方案是用浮点型存有缺失的数据如果你必须保留整数类型可以使用Pandas的可空整数类型Int64注意大写I它能同时支持整数和缺失值。这个细节能救你很多次。6.2 解决问题前的三个习惯排查Pandas问题我建议养成三个习惯。第一报错后先看最后几行错误信息。Python的报错栈很长初学者容易被最上面的异常吓住其实最后一行往往写了具体原因比如“列名不存在”或“类型不匹配”。先把最后一行看懂再往上翻代码位置。第二小范围复现。不要直接在几十万行的数据上调试先用df.head(100)或df.sample(1000)切一个小样本在样本上复现问题。这样不仅能更快定位逻辑问题还能直接测试解决方案跑起来也快得多。第三用info()和dtypes检查类型。很多“奇怪”报错的根源是列类型不满足预期。比如你以为某列是数字其实是object字符串一做加减法就报错。先看一眼类型再决定是否转换能省下大量排查时间。另外还有一个很实用的习惯写代码时给关键步骤加中间输出。比如每处理完一个清洗步骤就print一下shape或head()确认行数、列名变化是否符合预期。虽然看起来多打了几行字但真的遇到数据异常时你能很快判断是哪一步引入的问题。最后想分享一个个人体会Pandas是一个工具不是考试知识点。你不用把每个函数都背下来遇到不确定的写法随手查文档、查帮助甚至直接试试报错都是很正常的学习方式。我用了很久Pandas之后依然会经常翻文档这并不丢人。真正宝贵的不是背了多少函数名而是遇到脏数据时能快速想出解决路径并在代码里做出清晰、安全、可维护的选择。这个能力是在一次一次实际数据任务中练出来的。
阅读完成 · 觉得有帮助?