
简介《Pandas数据分析实战》完整PDF文档面向已具备Python基础、希望深入掌握数据处理与可视化的学习者和从业者。全书以近百个源于真实项目的案例为主线系统讲解数据清洗、分组聚合、布尔索引与多重索引、SQL风格数据合并以及时间序列分析和与Matplotlib、Seaborn的集成可视化覆盖数据分析、金融与科研等典型应用场景。资源为单个PDF文件包体大小53.38MB排版清晰代码与解析对照呈现适合按需查阅与系统精读。该资源已有271人学习内容实用性经过用户检验。读者通过书中iPython笔记本风格的案例可掌握隔离任意数据子集、分组后应用聚合与转换、整理现实世界脏数据等核心技能并学会为机器学习任务准备数据集。完成学习后可独立实现从数据清洗、统计计算到可视化输出的完整分析流程。1. Pandas数据分析实战先看清三件最常被高估的事做数据分析的人几乎都绕不开 Pandas但真正上手才知道import pandas as pd 只是开幕式。很多人带着数据库或 Excel 的习惯进来第一周就把 DataFrame 当成大号表格操作结果数据量大一点、格式脏一点代码直接卡死或悄悄算错。Pandas数据分析实战要解决的问题不是「会调用几个 API」而是三件被高估的事一是读数据被高估以为 read_csv 之后就能开始分析二是类型转换被高估object 列先转成日期和数值才谈得上聚合三是性能被高估循环遍历 DataFrame 的写法在真实数据面前不是慢是灾难。这篇按我自己做电商账单、网约车订单和农产品价格数据的路径来讲从装包、读数、洗数据到分组聚合每一步都给能直接跑的命令和参数。新手跟着走能跑通第一条分析链路熟手重点看第 5 章的踩坑和第 6 章的提速技巧那些都是让我返工过不止一次的教训。2. 从零跑通 Pandas环境、读数、数据类型三件事2.1 环境准备装 Pandas 的三种方式和最终检查先解决能不能跑的问题。最常见的安装方式是 pip但在国内裸装经常遇到超时或找不到版本。这里优先用清华源下载命令是pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple如果同时要装 jupyter、matplotlib、openpyxl 这些分析常客建议一次装齐全pip install pandas matplotlib seaborn openpyxl jupyter -i https://pypi.tuna.tsinghua.edu.cn/simple有人问 PyCharm 怎么安装 pandas 包其实不用折腾界面里的 Settings直接在 PyCharm 底部 Terminal 跑上面这条命令比图形面板快且不易出错。手机端装 Python 的话Android 上用 Termux、iOS 上用 Pythonista装包命令同样是 pip 走清华源只是注意手机内存小不要装数据分析全家桶pandas 加 openpyxl 就够。装完必须做一次检查确认版本和底层依赖都正常import pandas as pd print(pd.__version__)预期输出类似 2.x.x。如果版本后面出现 Warning 提示 numpy 版本不兼容先升级 numpy 再回来用 pandas不要继续往下跑。pandas 2.x 对 numpy 版本有硬要求低于 1.23 会直接触发 error: could not find a version 类似的依赖报错。2.2 读入数据read_csv 的参数比你想的更重要Pandas 的数据分析实战第一步不是打印前五行而是读成「可分析的 DataFrame」。以一份电商日订单表为例列包含订单号、成交时间、买家备注、实付金额、优惠金额、商品类目。常见读取写法只有一行参数踩坑就在这import pandas as pd df pd.read_csv( orders.csv, encodingutf-8, dtype{订单号: str, 实付金额: float}, parse_dates[成交时间], low_memoryFalse, ) print(df.shape) print(df.dtypes)这里我一般会逐项说明参数。encoding 用 utf-8如果是 Windows 导出的 Excel 另存的 CSV经常是 gbk 编码读的时候会报 UnicodeDecodeError把 encoding 换成 gbk 就好。dtype 用来锁定列类型订单号必须保持字符串否则会被读成 int64首位的 0 全丢parse_dates 把成交时间直接转成 datetime64比事后 to_datetime 更省事。low_memoryFalse 让 pandas 一次性判断所有列的类型不是分行推断避免文件大时某一列前半是 int 后半是 str 导致类型错乱。print(df.dtypes) 是为了读完立刻看到每列的真实 dtype这一步能避免后续大量被隐藏的类型问题。读进来之后别急着写聚合先用三行做快速体检print(df.info()) print(df.isna().sum()) print(df[:3])df.info() 告诉你有多少行、每列的非空数量和内存占用df.isna().sum() 让你知道每一列缺多少值df[:3] 是快速肉眼看前三条。这三步加起来不到十秒但能让你决定「这份数据能不能直接练手」。常见的真实情况是金额列有负值、成交时间有 NaT、类目列混着空字符串和空格这些在聚合时全部是雷。2.3 dtype、object 列与内存占用pandas 类型转换的底层逻辑很多自学者卡在「pandas 数据类型转换」这一步因为 DataFrame 里最常见的字符列 dtype 显示为 object而 object 不是一种类型是「一坨什么东西」。object 列进行字符串运算时速度极慢而且无法直接排序分组。先看下面这段排查print(df.dtypes) df[下单日期] df[成交时间].dt.date df[用户等级] df[用户等级].astype(category) df[实付金额] pd.to_numeric(df[实付金额], errorscoerce)把 datetime 列用 .dt.date 拆出日期列方便按天分组用户等级这种取值有限的文本列转成 category内存直接降一半以上groupby 也更快金额列用 to_numeric 加 errorscoerce意思是遇到无法解析的值置为 NaN而不是直接抛异常中断后面再统一处理 NaN。内存优化还有更狠的招尤其处理几百万行订单数据时df[实付金额] df[实付金额].astype(float32) df[订单量] df[订单量].astype(int32)浮点列从 float64 降到 float32整型从 int64 降到 int32三百万行数据能省下几百 MB。代价是精度和范围变小金额一般两位小数用 float32 够用但如果实付金额超过千万级或者要求分毫不差保留 float64。pandas 2.0 之后引入了 PyArrow 后端不过日常项目里 float32 加 category 已经解决大部分内存焦虑不必为了新特性折腾依赖。到这里环境、读数和类型三条线就通了。下一步进入占比最重的数据清洗环节。3. 数据清洗是重头戏从「能读」到「敢分析」3.1 缺失值处理的选型dropna、fillna 还是留空真实数据没有不缺值的。关键在于缺的是哪种值以及后续分析要不要用它。处理策略分为三类按场景选。# 行内所有关键字段缺一不可直接删行 df_clean df.dropna(subset[订单号, 实付金额]) # 数值列用均值或中位数填充 df[实付金额] df[实付金额].fillna(df[实付金额].median()) # 文本列用占位值填充保留原始行信息 df[买家备注] df[买家备注].fillna(无备注) # 时间列用前值填充适合序列数据 df[成交时间] df[成交时间].fillna(methodffill)几条逻辑分别展开说。dropna 的 subset 参数特别关键它只检查你指定的关键列不是所有列一行数据备注是空的但订单号金额都在删掉就亏了。fillna 用中位数而不是均值因为电商订单金额右上角拖尾严重均值会被大单拉偏中位数更稳。时间列 ffill 前值填充只在数据排序正确时有效如果原始表格乱序用了 ffill 等于编织谎言——时间会串行到无关记录上。有个容易翻车的点缺失值不一定是 NaN可能是空字符串、空格、NULL、-fillna 一概不认。真实数据里这些伪缺失才是大头。df[买家备注] df[买家备注].replace([, , NULL, -], pd.NA) df[买家备注] df[买家备注].fillna(无备注)replace 先把各种伪缺失统一替换成 pd.NA然后再走 fillna。这一步顺序不能反反了 fillna 只对真 NaN 生效。3.2 类型转换的三板斧dt、astype、to_numeric、to_datetime数据分析实战里类型转换是最能看出一个人熟不熟 pandas 的地方。新手的通病是盯着 dtype 硬转用 astype 转日期列失败之后就开始怀疑人生。日期转换的正规姿势是 to_datetime它多一个 format 参数用于提速和消歧。# 统一转时间遇到不合理日期转成 NaT df[成交时间] pd.to_datetime( df[成交时间], format%Y-%m-%d %H:%M:%S, errorscoerce, ) # 从时间列拆出新维度 df[分钟] df[成交时间].dt.minute df[星期] df[成交时间].dt.dayofweek # 0周一, 6周日 # 数字列也可能是伪数字带货币符号或千分位 df[实付金额] df[实付金额].astype(str).str.replace(¥, ) df[实付金额] df[实付金额].str.replace(,, ) df[实付金额] pd.to_numeric(df[实付金额], errorscoerce)format 参数的好处是做两件事一是解析速度比 pandas 自动推断快一倍以上二是格式不对会立刻变 NaT不会出现「2023-13-45」这种日期被悄悄解析成人名的情况。errorscoerce 是后悔药脏数据转不了就留 NaN最后统一清洗而不是中断整个流程。带货币符号的金额列是经典场景直接 to_numeric 会返回 NaN必须先清理字符串。astype 的用途要缩小到确实安全的转换上整数转浮点、category 化、bool 转 int。凡是遇到「看起来是数字但不是数字」的列一律 from pandas 的解析函数这是实践里最重要的一个准则。3.3 列操作与去重drop_duplicates 的三个细节清洗里还有一类高发疏漏是重复行。重复数据影响的是聚合结果的偏差一张订单表里同样的订单号出现两次groupby 之后销量直接翻倍。# 根据订单号去重保留第一次出现 df_unique df.drop_duplicates(subset[订单号], keepfirst) # 全字段去重用于日志类数据 df_log df.drop_duplicates() # 查看各列重复情况占比 dupe_rate df.duplicated(subset[订单号], keepFalse).mean() print(f重复订单占比: {dupe_rate:.2%})subset 选择唯一键电商订单用订单号日志数据用全字段keepfirst 保留第一条是默认行为matched with 业务逻辑要留最后一条成交记录的再用 keeplast。duplicated 配合 mean 算重复率是清洗阶段快速评估数据质量的手段这个数值高过 5% 就要回头找源头问题不要闷头去重。去重之后顺手做一次「列存活检查」assert df_unique[订单号].is_unique, 订单号仍有重复 assert (df_unique[实付金额] 0).all(), 存在负金额两个 assert 断言把清洗结果锁住之后再做任何聚合底层数据都是可信的。断言失败就说明前面的清洗逻辑有漏洞赶紧回去查不要带着脏数据继续往下跑。这是我在网约车订单项目里学到的一课清洗的目标不是数据变得干净而是「你能证明它干净」。4. 分组聚合的硬功夫groupby、agg、apply 的正确分工4.1 groupby 最小闭环分组、聚合、重置索引分组聚合是数据分析实战的高频动作。最基本的 pattern 是groupby 指定分组列agg 指定聚合函数reset_index 把分组键从索引里释放回普通列。daily_summary ( df.groupby(下单日期) .agg( 订单量(订单号, count), 实付总额(实付金额, sum), 客单价(实付金额, mean), ) .reset_index() ) print(daily_summary.head())这里的写法值得细看。groupby(下单日期) 按天分组agg 传入一个字典键是结果列名值是 (源列, 聚合函数) 的元组这种命名聚合方式从 pandas 0.25 开始支持可读性比 sum 字符串好太多——它把「计算什么、对哪一列算」放在一起代码评审阶段一眼就能看明白业务含义。订单量用 count 不是 nunique因为每一行就是一张订单count 统计行数nunique 会错误地去重。客单价是实付金额的均值。reset_index 几乎是必调的因为很多人在这一步翻车分组后的 DataFrame 索引还是多层的 MultiIndex直接 .to_excel 导出Excel 里会出现莫名其妙的索引层级后续合并时也会报错。reset_index 就是把这些组键放回列里让结果变回一个扁平的普通表。4.2 agg 与 transform一个约等于聚合一个约等于广播初学者最容易卡死的地方是想给每一行算「自己的值占当天总额的比例」却不知道用 transform。两者的差别是行为层面看代码更清楚。# agg返回按组压缩后的结果 daily_total df.groupby(下单日期)[实付金额].agg(sum) print(len(daily_total)) # 输出天数比如 31 # transform保持行数不变结果广播回原行 df[每日总额] df.groupby(下单日期)[实付金额].transform(sum) df[金额占比] df[实付金额] / df[每日总额] print(df.head())transform 的输出长度和原始 DataFrame 完全一致它把每天的汇总值广播到每一天的每一行上然后你就可以做逐行计算。金额占比就是这么算出来的。agg 则用于生成报表transform 用于「扩展列做逐行运算」这是 pandas 里最容易理解也最常用错的一对。注意 transform 的聚合函数必须是逐组内置或可向量化的不要把 lambda x: x.sum() 写复杂——复杂度高了 transform 的性能会崩。还有一类需求是「每个类目里金额前 N 的订单」这属于分组内 TopN推荐用 rank 而不是 applydf[类目排名] df.groupby(类目)[实付金额].rank(methoddense, ascendingFalse) top3 df[df[类目排名] 3]rank(methoddense) 遇到并列时不会跳过排名比如两个并列第一后面顺延是第二减少分组里的排名空洞。这一招比 groupby apply nlargest 快一个数量级数据量上百万时差距极其明显。4.3 透视表 pivot_table别再用 groupby 硬算二维报表有时候业务方要的是「行日期列类目值销售额」的二维报表。groupby 也能做但得 unstack代码绕且注定不直观。pandas 自带的 pivot_table 才是这个场景的正解。pivot pd.pivot_table( df, index下单日期, columns类目, values实付金额, aggfuncsum, fill_value0, marginsTrue, margins_name合计, )参数逐条说明。index 是透视表的行维度columns 是列维度values 是要聚合的数值列aggfunc 指定聚合方式sum、mean、count 都行fill_value0 把没有数据的单元格填成 0 而不是 NaNmarginsTrue 会在最右和最下方生成总计行与总计列margins_name 自定义这个总计的名字。这个函数几乎就是把 Excel 透视表迁移到了 pandas 里常用于电商运营日会报表。透视表和 groupby 的输出区别在于索引布局pivot_table 天然是二维交叉表格式groupby 加 unstack 可能产生 NaN 空洞需要再次填充。实际项目里凡是遇到「行一个维度、列一个维度、值做聚合」这种需求我一般会直接用 pivot_table不折腾 groupby。做报表时还有个细节透视表如果列很多导出 Excel 后不好看可以用 to_excel 配合 openpyxl 引擎输出但列宽需要手动设置pandas 不会管排版。5. Pandas 实战避坑指南5 条血泪经验5.1 切片后的链式赋值与 SettingWithCopyWarning现象对 df[df[金额] 100][备注] 做赋值时pandas 弹出 SettingWithCopyWarning甚至赋值没生效。原因链式索引返回的是原数据的副本或视图pandas 自己也分不清对副本赋值等于修改了一份临时数据原 DataFrame 不会变。早期 pandas 版本是静默吞下去后来版本用警告逼你改。解决永远用 .loc 做过滤和赋值的组合。df.loc[df[金额] 100, 高价值标记] 是这一行既完成了行过滤又完成了列赋值是安全的视图操作不会触发警告。牢记一条铁律凡是「既要选行又要改列」统一用 .loc。5.2 read_csv 读大文件时类型推断导致的内存翻倍现象读取 2GB 的 CSV内存直接飙到 8GB机器接近卡死df.info() 显示一堆 object 列。原因read_csv 默认先抽样推断类型数字列如果没识别出来会落入 object字符串在 pandas 里按 Python 对象存储内存开销是原生数值类型的数倍同时为保留原始数据读入时会额外持有副本。解决读之前明确规定类型一次性压低内存。df pd.read_csv( big.csv, dtype{订单号: string, 类目: category}, usecols[订单号, 类目, 实付金额, 成交时间], parse_dates[成交时间], )usecols 只读需要的列直接砍掉一半内存。dtype 锁定 string 和 category 之后object 列消失内存跳跃式下降。别以为 parse_dates 会拖慢读取在大文件场景下它比事后 to_datetime 再释放字符串列更快。5.3 groupby 之后不 reset_index 直接入库现象分组聚合结果写回数据库或 CSV 时出现一行行索引混乱CSV 第一列多出无名索引数据库表里多出一个 index 字段。原因groupby 的分组键默认变成索引虽然打印好看但业务系统不认索引。解决聚合后立即 reset_index(dropFalse)并显式控制导出列。result df.groupby(类目).agg(...).reset_index() result.to_csv(class_report.csv, indexFalse, encodingutf-8-sig)utf-8-sig 编码专治 Excel 打开 CSV 中文乱码问题这是国内环境下几乎必踩的坑。indexFalse 告诉 pandas 别把行索引写进 CSV否则每次都要手动删掉第一列。5.4 apply 里的 lambda 过多百万行数据越跑越慢现象一段 apply 遍历每行做字符串拼接数据量一万行还好一百万行跑了十几分钟没出结果甚至内存涨到爆。原因apply 本质是逐行调用 Python 函数每一行都有函数调用开销一万次无所谓百万次就是灾难而且 lambda 里如果访问全局变量还得反复做名字解析。解决优先向量化操作实在需要逐行计算时也别盲目 apply先看有没有内置函数。# 慢逐行 lambda df[年] df[成交时间].apply(lambda x: x.year) # 快dt 访问器直接取 df[年] df[成交时间].dt.yeardt.year 底层是向量化操作C 语言执行速度超过 apply 百倍。字符串处理的场景类似能用 str.replace 就不用 apply replace。真正无法向量化的场景再考虑 apply但一定要先测 1 万行耗时按行数推算下总耗时是否可接受。我自己的阈值是百万行超过 30 秒就回头改方案。5.5 把 df.iterrows() 当遍历神器结果慢到怀疑人生现象用 for idx, row in df.iterrows() 做逐行业务计算遍历 50 万行花了十几分钟代码看着挺直观。原因iterrows 每一行都要构建一个 Series同时保持行号和 dtype 对齐开销极其夸张而且它还会把 datetime 等类型重新识别导致速度再降一档。解决优先用 apply 配合向量化操作熟悉之后直接用 NumPy 数组或者换成 dict 结构能快两个数量级。# 避免iterrows 最大值时代 # for idx, row in df.iterrows(): # calc(row) # 推荐能向量化就向量化 df[折扣率] df[优惠金额] / df[实付金额]这个案例不是让你完全不遍历而是认识到 pandas 的强项在于「按列做整数组操作」一旦进入逐行 Python 循环优势和 Excel 公式拉开不多。需要逐行复杂计算的正确路径是先写出向量化表达式再考虑 apply最后才考虑 iterrows。顺序不要反。6. 让 Pandas 快起来的三个实用进阶技巧第一个技巧是延迟类型转换。读入 CSV 后不要急着把所有列一次处理完先保持原始 dtype然后按「使用到的字段」逐个转换。这样做的意义在于你永远不会为没参与分析的列付出计算和内存成本。比如分析订单金额就不需要关心收货地址列的解析等真正做地域分析时再转也不迟。第二个技巧是善用 query 代替布尔索引。df[df[金额] 100 df[类目] 数码] 这种写法一旦条件增多优先级错误或可读性崩塌我自己的做法是换成 querysub df.query(实付金额 100 and 类目 数码 and 下单日期 2024-01-01)query 关于条件的字符串写法一致还支持 变量 引用外部值性能在读大表时比布尔索引快不少代码也更像 SQL新人看得懂。第三个技巧是验证结果而不是验证过程。写完分组聚合后务必对照手工算一条再和聚合结果比对manual_total df[df[下单日期] 2024-01-05][实付金额].sum() reported_total daily_summary.loc[daily_summary[下单日期] 2024-01-05, 实付总额].iloc[0] assert abs(manual_total - reported_total) 0.01, 聚合结果不一致这个断言看着笨却是数据分析实战里最有价值的习惯。它能抓住我踩过的几乎所有坑重复行没处理干净、金额列混入字符串、transform 和 agg 用反、reset_index 把键弄丢。数据量大了以后肉眼检查早已失效断言就是最后的质检员。我的个人习惯是每个分析任务结束前花十分钟做这一轮验证数据对不上就回到清洗阶段查。其实真正阻碍你完成 Pandas数据分析实战项目的不是 API 不够熟而是你敢不敢在关键结果上较真。每次我偷懒跳过验证最后基本都要回头补课这是用返工换来的教训。希望帮到你记得让断言和向量化成为你做数据分析的本能。本文还有配套的精品资源点击获取