☰
《Python for Data Analysis》数据集实战:从read_csv到分组聚合全流程
2026/10/13 8:04:43 网站建设 项目流程

简介:《Python for Data Analysis, 2nd Edition》的配套数据集,面向需要动手验证Pandas、NumPy代码的初学者和进阶者;压缩包共209个文件,约40.36MB,其中包含136个txt说明、23个csv表格、14个ipynb示例笔记本,以及xml、json、pkl等常见数据格式,还附带shp、sbn等地理信息文件,可支撑书中大部分章节的练习。目前已有347人学习,常被用作自学辅助材料。这套数据集的便利之处在于:从Series与DataFrame的基础操作,到数据导入导出、缺失值和重复值清洗,再到merge、concat及groupby分组聚合,以及时间序列分析和Matplotlib、Seaborn可视化,每个主题都能找到对应样例数据与代码,直接对照运行即可。不用花时间四处寻找或重复造数据,节省了从环境准备到代码排错的精力,无论是想系统梳理数据处理流程,还是希望快速复现书中案例,这套文件都能提供清晰的练习路径。

1. 这本书自带的数据集,才是真正值得反复啃的东西

很多人学 Python 数据分析都会栽在同一个地方:书上的代码跟着敲完了,过两周全忘。原因不是记性差,而是练手的数据太假、太干净、太少。这本《Python for Data Analysis》第二版之所以在从业者圈子里口碑一直稳,除了把 pandas 讲得清楚之外,还有一个常被忽略的优点——标题末尾那个"(数据集)"。这意味着你不需要满世界找练习数据,解压之后就能拿到一批带着真实缺陷的 CSV 和 JSON:有缺失值、有脏格式、有类型混用,跟生产环境里导出来的数据一个德性。它能解决的核心问题是让你把从读取文件到分组聚合再到可视化的整条链路,在一套稳定数据上反复练到形成肌肉记忆。适合两类人:一类是有 Python 基础但 pandas 只会查手册的入门者,另一类是准备用数据分析做面试作品或日常报表的从业者。

2. 先搞清楚数据集长什么样,再谈环境:目录结构与运行准备

这套数据集不是单一文件,而是按章节组织的多个数据文件集合。很多人拿到手就直接pd.read_csv,结果路径写错、编码报错、类型不对,第一关就卡住。正确的顺序是先把数据集当成一个「待勘察的目录」来处理,搞清楚里面有哪些文件、各自是什么格式、什么编码,再决定用哪一组 pandas API 去加载。

2.1 数据集的形态:CSV、JSON 和时间序列混在一起

解压之后,数据集大体上分几类。第一类是记录型表格,典型如用户行为日志、评分记录,每一行是一次事件,列包含用户标识、时间、行为类型、金额等字段;第二类是截面型数据,比如乘客信息表,每一行是一个样本,特征是年龄、舱位、性别这些;第三类是时间序列,比如股价或天气数据,特点是索引本身就是时间;第四类是短文本数据,用于文本处理的练习。为什么要先分清楚形态?因为不同形态决定加载方式完全不同:CSV 用read_csv,JSON 用read_json,时间序列要额外处理日期解析,多表结构则要考虑后面怎么merge。我一般建议先把数据集目录完整列一遍,确认文件命名规律和数据规模,再开始写代码:

# 递归列出所有数据文件,head 只看前面部分,避免终端刷屏 ls -R data | head -50 # 用 file 命令检查真实编码,CSV 可能是 UTF-8 也可能是 GBK,提前知道能省不少事 file data/*.csv | head -10

记住一个原则:不要用 Excel 去打开这些 CSV 再另存。Excel 另存会改写编码、可能破坏日期格式、还会把前导零弄丢,这些都会让后面 pandas 的处理结果变得不可信。数据集里的文件保持原样,用命令行工具去勘察,这是第一个习惯。

2.2 复现环境:能跑通全部示例代码的安装组合

第二版写作时的 pandas 版本和今天的 API 已经有差异,但主流操作——read_csv、groupby、merge、pivot_table——在新版本里依然健在。所以环境搭建不必刻意去装一个老版本,直接用新版本即可,遇到个别函数报错时查一下新版替代写法就行。我建议用虚拟环境隔离,别把库装进系统 Python,后面做别的项目时版本冲突会让人头大:

# 创建独立虚拟环境,目录名叫>import pandas as pd df = pd.read_csv( "data/sample_data.csv", sep=",", # 明确分隔符,遇到制表符文件改成 sep="\t" encoding="utf-8", # 乱码时依次尝试 gbk、utf-8-sig、latin1 parse_dates=["date"], # 把日期列自动解析为 datetime64 类型 dtype={"user_id": "int32"}, # 指定列类型,避免 pandas 猜错或浪费内存 na_values=["NA", "null", ""] # 把这几种写法统一识别为缺失值 ) print(df.head())

每个参数都有它存在的理由。sep默认就是逗号,但明确写出来是一种好习惯——数据文件里偶尔会有分号或制表符分隔的版本,显式声明能让代码在文件格式变化时只改一行。encoding是这套数据集的高频坑,后面避坑章节会细说。parse_dates很重要:如果不指定,日期会被读成字符串,后面排序和画时间序列图时全部错位。dtype是用来指定列类型的,像user_id这种列读成 int64 没问题,但如果文件有几百万行,把 int64 缩窄成 int32 能省一半内存。na_values的作用是统一缺失值的表示方式——不同系统导出 CSV 时,空值可能写成空字符串、NA、null甚至N/A,不统一的话isna()统计出来的结果完全不同。

3.2 数据探查三板斧:info、describe 之外还要看什么

读取成功只是第一步。拿到 DataFrame 之后,花两分钟做数据探查,能避免后面分析写到一半被奇怪的结果带偏。我的固定动作是四件事:看结构、看统计、看分布、看缺失。示例代码:

# 第一板斧:info 输出行数、列名、每列类型和非空数量,10 秒摸清全貌 print(df.info()) # 第二板斧:describe 给出数值列的均值、标准差、分位数,快速发现异常范围 print(df.describe()) # 分类列要看分布:value_counts 能暴露脏数据,比如出现了预期外的取值 print(df["action"].value_counts()) # 缺失值按列统计:缺失的分布决定后续是删除还是填充 print(df.isna().sum()) # 重复行检查:数据集里常见的隐形脏数据来源 print(df.duplicated(subset=["user_id", "date"]).sum())

这四段代码的输出合在一起,基本能判断这份数据集「能不能直接用」以及「哪些列需要处理」。比如describe里某个数值列的 max 是平均值的几百倍,那多半有异常值;value_counts里出现 5 种取值但业务上只应该有 3 种,那就是数据录入有问题;isna().sum()显示某一列缺失比例超过 30%,这一列在后续分析里就要谨慎使用。这套探查流程对这套数据集里的每一份文件都适用,形成一个固定动作后,拿到任何陌生 CSV 都会先做这一步,而不是急着画图——很多翻车现场就是跳过探查直接进入分析导致的。

4. 从一份数据集跑通完整分析:分组聚合、透视表与可视化

数据集的价值不在于单点函数调用,而在于把多个操作串成一条流水线。这一章用一份典型的用户行为日志数据做例子,演示从明细数据到结论输出的完整链路。这类数据在配套数据集里很常见,结构一般包含用户标识、事件时间、行为类型、订单号、金额这几个字段。核心目标是把几万行明细汇总成一张能直接汇报的趋势表。

4.1 分组聚合与透视表:把明细变成结论

先读数据,再把时间切成天,然后按「日期 + 行为类型」做分组聚合,同时求订单数和金额合计:

import pandas as pd # 读取日志数据,event_time 直接解析成时间类型 df = pd.read_csv("data/user_log.csv", parse_dates=["event_time"]) # 从时间戳里提取日期,后续按天汇总 df["date"] = df["event_time"].dt.date # 按日期和行为类型分组,同时求两个指标:唯一订单数和金额总和 daily_action = ( df.groupby(["date", "action"], as_index=False) .agg( order_count=("order_id", "nunique"), total_amount=("amount", "sum"), ) ) # 按金额降序看头部,快速找到贡献最大的日期和渠道 print(daily_action.sort_values("total_amount", ascending=False).head(10))

这段代码里值得注意的有两点。第一,agg里用了命名聚合的写法——order_count=("order_id", "nunique")表示对order_id列做去重计数,比先groupby再单独nunique再merge回来要简洁得多。第二,as_index=False让分组字段保留为普通列而不是变成索引,这样后续做筛选和排序时操作更自然。

分组聚合结果的形态是长表,适合电脑继续处理,但人眼看起来不够直观。这时候用透视表把行为类型从行变成列,一行一个日期,一眼就能看出各行为之间的消长关系:

# 透视表:行是日期、列是行为类型、值是金额合计,空缺补 0 pivot = pd.pivot_table( df, values="amount", index="date", columns="action", aggfunc="sum", fill_value=0, ) print(pivot.tail(5))

透视表和分组聚合是同一份数据的两种看法,没有谁优谁劣——做进一步计算时用分组聚合的结果更方便,做汇报展示时透视表更直观。实际项目中我通常两个都生成,分组聚合结果用于后续统计检验,透视表用于出图。

4.2 结果可视化与输出:把 DataFrame 变成能交付的成果

有了透视表,画趋势图就是顺手的事。用 matplotlib 直接画,不引入 seaborn 也能出能看的图:

import matplotlib.pyplot as plt # 透视表本身就是宽表格式,plot 直接按列画多条线 ax = pivot.plot(kind="line", figsize=(10, 5)) plt.title("Daily Amount by Action") plt.xlabel("date") plt.ylabel("amount") plt.tight_layout() # 保存为 PNG,交付时不会被终端画布大小影响 plt.savefig("daily_trend.png", dpi=150)

出图之后,还有一步很多人会漏掉——把加工后的数据导出。分析结果如果不落盘,下次重新跑一遍脚本又要等半天。导出时两个参数是血泪经验积累出来的:

# 关联用户维度信息,补齐明细表里缺的列 users = pd.read_csv("data/users.csv") merged = pd.merge(df, users, on="user_id", how="left") # 导出时 index=False 避免写出多余行号;utf-8-sig 让 Excel 打开不乱码 merged.to_csv("data/merged_output.csv", index=False, encoding="utf-8-sig")

index=False这个参数几乎每次导出都要写,不然 CSV 里会多出一列无名索引,别人拿到文件后经常困惑这列是什么。encoding="utf-8-sig"是专门给 Windows 同事准备的——普通utf-8编码的文件用 Excel 打开时中文会乱码,utf-8-sig带 BOM 头,Excel 能正确识别。这套「读取 → 探查 → 分组聚合 → 透视表 → 可视化 → 导出」的流水线,就是这一章要传递的核心:数据集不是拿来单个函数试的,而是拿来串流程的。

5. 数据集复现中的五个高频坑:现象、原因、解决

这套数据集本身没毛病,但复现过程中常见的问题基本集中在读取环节和版本差异上。以下五条踩坑记录,每一条都是实际反复遇到过的,按「现象 → 原因 → 解决」的顺序写清楚。

5.1 路径问题:文件名带空格或中文导致读取失败

现象:pd.read_csv("data/我的 数据.csv")报FileNotFoundError,但文件明明就在那里。

原因:Windows 下路径里的反斜杠被转义,或者文件名里带空格、中文导致路径解析失败。有些导出工具还会在文件名里自动加空格,肉眼看不出来。

解决:统一用正斜杠路径,并借助pathlib构造路径,不要手写字符串拼接:

from pathlib import Path data_dir = Path("data") file_path = data_dir / "my_data.csv" # 正斜杠拼接,天然兼容 Windows 和 Linux df = pd.read_csv(file_path)

5.2 编码问题:UTF-8 文件读出来全是乱码

现象:read_csv正常执行,但打印 DataFrame 时中文全变成乱码,或者直接报UnicodeDecodeError: 'utf-8' codec can't decode byte。

原因:数据集里的 CSV 可能不是 UTF-8 编码,而是 GBK 或 GB2312。Windows 环境导出的文件尤其常见。

解决:用encoding参数依次尝试,直到不再报错:

for enc in ["utf-8", "gbk", "latin1", "utf-8-sig"]: try: df = pd.read_csv("data/sample_data.csv", encoding=enc) print(f"成功: {enc}") break except UnicodeDecodeError: continue

这个try循环是排查编码问题最快的方式,跑一遍就知道该用哪个编码,比盲猜高效得多。

5.3 版本 API 差异:书上的代码在新版 pandas 中报错

现象:按书里的代码执行df.sort("date")或df.append(...),新版 pandas 报AttributeError或直接移除。

原因:pandas 在 1.0 之后的版本移除了sort、append等旧接口,sort_values成为唯一排序入口,concat取代了append。书成稿早于这些变动。

解决:遇到报错先查官方文档的函数变更记录,用新 API 替换:

# 旧写法:df.sort("date") —— 已移除 # 新写法: df = df.sort_values("date")

5.4 日期列被读成字符串,时间排序完全错乱

现象:read_csv之后df["date"]的类型是object而不是datetime64[ns],按日期排序的结果看起来毫无规律。

原因:CSV 里日期格式多样,比如2023/1/5、2023-01-05、01/05/2023混在一起,pandas 默认不自动推断日期类型。

解决:读取时显式声明日期列,并指定格式:

df = pd.read_csv( "data/sample_data.csv", parse_dates=["date"], infer_datetime_format=True, # 让 pandas 自动推断日期格式 )

5.5 大文件读取慢且内存占用高

现象:数据集里某些日志文件行数达到百万级,read_csv要卡几十秒,df.info()显示内存占用超过 500MB。

原因:pandas 默认把每列读成 int64 和 float64,字符串列默认存成 object,这些类型都比实际需要占用更多内存。

解决:分块读取,并指定高频列的类型:

# 用 chunksize 分块读取,逐块处理后合入结果,避免一次性加载全部数据 chunk_iter = pd.read_csv("data/large_log.csv", chunksize=10_000) result = pd.DataFrame() for chunk in chunk_iter: result = pd.concat([result, chunk], ignore_index=True) # 同时用 dtype 参数压缩类型,比如 ID 列用 int32 而不是 int64

这五条坑覆盖了数据集复现过程中的绝大多数翻车场景。每一条的解决方式都已经被验证过很多次,把这些写成肌肉记忆,比临时去查文档节省大量时间。

6. 把数据集变成训练场:复现、改写、自我出题

这套数据集最好的用法不是照书敲一遍,而是把它当成一个可以反复使用和改造的训练场。我的习惯是三轮用法。第一轮是「复现」:按书里的思路把每个数据集跑通,不跳过任何一步,重点体会作者为什么用这个 API 而不是另一个。第二轮是「改写」:换一种聚合方式或者换一组筛选条件,把同样的数据做出不同的结果——比如原书用均值汇总,我就改用中位数再看一遍,观察两者结论的差异。第三轮是「自我出题」:用数据集里的字段组合,给自己设定一个分析任务,比如「找出订单金额最高的用户集中在哪个时间段」「流失用户和活跃用户在行为类型上有哪些差异」。每一轮都能挖出新的 pandas 用法死角。

举个自我出题的例子。把用户数据和日志数据合并后,按用户统计月均消费和消费次数,然后画散点图观察两者关系:

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("data/user_log.csv", parse_dates=["event_time"]) users = pd.read_csv("data/users.csv") merged = pd.merge(df, users, on="user_id", how="inner") # 按用户聚合:消费次数、消费总额、平均单笔金额 user_stats = ( merged.groupby("user_id") .agg(order_total=("order_id", "nunique"), amount_sum=("amount", "sum"), amount_avg=("amount", "mean")) ) # 散点图看消费频次和平均单笔金额是否存在关联 plt.figure(figsize=(8, 6)) plt.scatter(user_stats["order_total"], user_stats["amount_avg"], alpha=0.4) plt.xlabel("order count") plt.ylabel("avg amount") plt.tight_layout() plt.savefig("user_scatter.png", dpi=150)

这种小型练习任务,一次只围绕一个数据集、一个业务问题,花费时间不多但收获很实在。我自己的感受是:把这本书的数据集跑三遍,每一遍的感悟都不同,第一遍学会了操作,第二遍理解了为什么用这个操作,第三遍开始能在新数据上迁移这些技能。这也是我为什么一直建议不要只收藏数据集而不动手——数据和代码只有在你自己的机器上跑过一遍,才能变成你自己的东西。希望今天的这些经验能帮你在使用这套数据集时少走几步弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询