Python数据分析实战:配套数据集加载、清洗与避坑指南
简介:这份资源是《Python for Data Analysis, 2nd Edition》配套数据集,面向正在学习或实践Python数据分析的读者,尤其适合配合Wes McKinney原书做动手练习,解决从数据导入、清洗到建模分析缺少真实样例数据的问题。压缩包共209个文件,约40.36MB,以txt说明与数据文件、csv表格、ipynb笔记本为主,另含少量py脚本、json、pkl、xlsx及shp、dbf等地理数据文件,覆盖书中示例与练习所需素材。已有350人学习下载。数据集涵盖股票行情、Haiti地震、volume、train/test等典型场景,可直接用于Pandas的Series与DataFrame操作、缺失值处理、合并分组、时间序列分析及Matplotlib可视化练习,帮助读者把书中知识点落到真实数据上,提升数据清洗与探索性分析能力。
1. 数据分析入门绕不开的那本书,配套数据集到底怎么用
很多人学 Python 数据分析,卡住的地方不是语法,而是拿到一份 CSV 之后不知道下一步该干什么。教材翻了几章,pandas的 API 也认得几个,可一上手真实数据就懵:列名带空格、日期是字符串、缺失值藏在奇怪的位置。这种“看得懂代码、跑不通流程”的状态,恰恰是缺少一套结构清晰、覆盖完整的数据集来练手造成的。Python for Data Analysis 第 2 版配套的那批数据集,就是为这个场景准备的——它把 read_csv、groupby、时间序列、缺失值处理这些知识点,全部挂在了真实可跑的数据文件上。这套东西适合谁?适合已经会写 Python 基础语法、想系统过一遍数据分析全流程的人,也适合带新人时找一套“有标准答案”的练习素材。下面我按自己复现这套数据集的路径,把目录结构、加载方式、清洗套路和踩过的坑讲清楚。
2. 数据集目录结构与加载路径:先搞清楚文件都长什么样
2.1 这批数据集里到底有哪些文件类型
这套配套数据不是单一 CSV 集合,而是刻意混了多种格式,目的是让你在练pandas的同时把 IO 这一层也过一遍。常见的有这几类:
| 文件类型 | 典型用途 | 加载函数 | 注意点 |
|---|---|---|---|
.csv | 表格数据主力,如电影评分、婴儿姓名 | pd.read_csv | 编码、分隔符、缺失值标记 |
.txt | 制表符或竖线分隔的日志类数据 | pd.read_csv(sep=...) | 分隔符必须显式指定 |
.xlsx/.xls | 多 sheet 的报表数据 | pd.read_excel | 需要 openpyxl/xlrd |
.json | 嵌套结构的记录 | pd.read_json或json.load | 嵌套层级要手动展平 |
.hdf5 | 大数据集的二进制存储 | pd.read_hdf | 需要 tables 依赖 |
.dta | 统计软件导出的数据 | pd.read_stata | 列类型可能被推断错 |
我一般会先写一个扫描脚本,把目录下所有文件的扩展名、大小、前几行打出来,而不是凭记忆去猜哪个文件对应哪一章。这一步花五分钟,后面能省半小时的“文件找不到”排查。
import os import pandas as pd DATA_DIR = "./pydata-book/datasets" # 换成你自己的数据集根目录 for root, dirs, files in os.walk(DATA_DIR): for f in files: path = os.path.join(root, f) size_kb = os.path.getsize(path) / 1024 print(f"{path:60s} {size_kb:8.1f} KB")这段代码的逻辑很直白:递归遍历目录,打印相对路径和文件大小。参数上唯一要改的是DATA_DIR,指向你解压后的数据集根目录。跑完之后你会对“哪些文件大、哪些是辅助文件”有个整体印象,比如某些.csv只有几十 KB,那是教学用小样本;有些.hdf5上百 MB,那是用来练分块读取的。
2.2 用 read_csv 加载时的四个必调参数
直接pd.read_csv("xxx.csv")能跑通是运气,跑不通才是常态。我一般固定带上这四个参数,能挡掉大部分翻车:
df = pd.read_csv( "some_data.csv", sep=",", # 分隔符,txt 类文件常是 \t 或 | encoding="utf-8", # 中文或特殊字符报 UnicodeDecodeError 时换 latin1 试 na_values=["NA", "null", "-", ""], # 显式声明缺失值标记 parse_dates=["date_column"] # 提前把日期列解析成 datetime )sep不写默认是逗号,但日志类.txt经常是制表符,不指定就会把整行读成一列。encoding是最常见的报错来源,UnicodeDecodeError一出现先怀疑编码,utf-8不行就试latin1或gbk。na_values的作用是把各种“伪缺失”统一成NaN,否则groupby的时候这些字符串会混进统计里。parse_dates提前解析日期列,比读进来再pd.to_datetime少一步,而且能让后续的时间序列操作直接可用。
提示:
parse_dates只对格式规范的日期字符串有效,遇到2020/1/1和01-01-2020混排的列,先读成字符串再手动转换更稳。
2.3 相对路径与工作目录的坑
数据集加载失败,十次里有三次是路径问题。Jupyter 的工作目录是 notebook 所在目录,而脚本的工作目录是执行命令时所在的目录,两者经常不一致。我的习惯是在脚本开头打印一次os.getcwd(),确认当前在哪,再用相对于项目根目录的路径。如果数据集和代码不在同一层,用pathlib拼绝对路径比手写../..可靠:
from pathlib import Path BASE = Path(__file__).resolve().parent # 脚本所在目录 DATA = BASE / "datasets" / "some_data.csv" df = pd.read_csv(DATA)Path(__file__).resolve().parent拿到的是脚本文件的绝对父目录,不管从哪里执行都不会错。这个写法在 notebook 里不适用(没有__file__),notebook 里就用Path.cwd()往上找。搞清楚这一点,后面所有加载代码都能复用同一套路径逻辑。
3. 数据清洗的标准流程:从原始文件到可分析表
3.1 先看结构再看内容:info 和 describe 的分工
拿到一个 DataFrame,别急着head()。我一般先跑df.info()看列数、非空计数和 dtype,再跑df.describe()看数值列的分布。这两个的输出回答的是不同问题:info告诉你“数据完整不完整、类型对不对”,describe告诉你“数值合不合理、有没有离谱的极值”。
df.info() # 关注:每列非空数量是否等于总行数,object 类型的列是否本该是数值或日期 df.describe(include="all") # include="all" 让 object 列也参与统计,能看到唯一值数量和众数info里如果某列非空数明显少于总行数,说明缺失严重,要决定是删列还是填充。describe里如果min或max离均值差好几个数量级,先怀疑单位不统一或录入错误。这一步不写代码逻辑,纯粹是观察,但决定了后面清洗策略的方向。
3.2 缺失值处理的三种策略与选择依据
缺失值不是一律dropna或一律fillna,要看缺失比例和列的用途。我的判断顺序是:缺失超过 60% 且不是关键列,直接删;缺失在 5% 到 60% 之间,按列类型填充;缺失低于 5%,可以删行也可以填充,看样本量够不够。
# 统计每列缺失比例 missing_ratio = df.isnull().mean().sort_values(ascending=False) print(missing_ratio[missing_ratio > 0]) # 数值列用中位数填充(比均值抗极值) num_cols = df.select_dtypes(include="number").columns df[num_cols] = df[num_cols].fillna(df[num_cols].median()) # 类别列用众数填充 cat_cols = df.select_dtypes(include="object").columns for c in cat_cols: df[c] = df[c].fillna(df[c].mode()[0])isnull().mean()算的是缺失比例,比isnull().sum()更直观,因为你能直接看出百分比。数值列选中位数而不是均值,是因为中位数对极端值不敏感,填充后不会把分布拉偏。类别列用众数填充有个前提:该列的众数确实有代表性,如果众数占比不到 30%,填充等于引入噪声,这时候宁可保留NaN或单独标一个“未知”类别。
3.3 类型转换与重复值去重
类型不对是groupby和绘图报错的常见根源。数值列被读成object,多半是因为混进了"1,234"这种带千分位的字符串,或者有"unknown"之类的占位符。处理方式是先pd.to_numeric(errors="coerce")强制转换,转不了的变NaN,再回头决定怎么处理这些NaN。
df["price"] = pd.to_numeric( df["price"].astype(str).str.replace(",", ""), errors="coerce" ) # 去重前先确认重复的判定口径 dup_count = df.duplicated(subset=["id"], keep=False).sum() print(f"按 id 判定的重复行数:{dup_count}") df = df.drop_duplicates(subset=["id"], keep="first")str.replace(",", "")去掉千分位,errors="coerce"让无法转换的值变成NaN而不是抛异常,这样你能一次性看到所有问题值。duplicated(subset=["id"])指定按业务主键判重,比全列比对更符合实际——两行所有字段都一样才算重复,往往漏掉“同一 id 多条记录”的情况。keep="first"保留第一条,如果业务上应该保留最新一条,就先按时间排序再keep="last"。
3.4 用 groupby 做第一轮聚合验证
清洗完别急着往下分析,先做一轮聚合验证数据是否“讲得通”。比如按类别分组算均值、计数,看结果是否符合常识。这一步能抓出单位错误、编码错误、分组键选错等问题。
summary = ( df.groupby("category", dropna=False) .agg( count=("id", "count"), avg_price=("price", "mean"), max_price=("price", "max") ) .sort_values("count", ascending=False) ) print(summary.head(10))dropna=False让NaN分组也出现在结果里,避免“明明有数据但分组后消失了”的困惑。agg里用字典形式给每个聚合起名字,输出表头清晰。sort_values("count")按样本量排序,能快速看出哪些类别样本太少、后续分析要不要合并。如果某个类别的avg_price是另一个的几十倍,先别下结论,回去查这一类的原始记录,大概率是单位或录入问题。
4. 时间序列与多表关联:数据集里最容易翻车的两块
4.1 日期解析失败的四种典型原因
时间序列分析的前提是日期列真的是datetime类型。pd.to_datetime报错或静默产生NaT,常见原因有四种:格式不统一、带时区后缀、有非法日期(如 2 月 30 日)、混入了非日期字符串。我的处理套路是先errors="coerce"转一遍,统计NaT数量,再抽样看这些失败的值长什么样。
raw = df["order_date"].astype(str) parsed = pd.to_datetime(raw, errors="coerce", format="mixed") bad = df.loc[parsed.isna(), "order_date"].unique()[:20] print("解析失败的样本:", bad) print("失败比例:", parsed.isna().mean())format="mixed"让 pandas 逐行推断格式,适合格式不统一的列,但速度比指定固定格式慢。先看失败样本再决定是清洗还是丢弃,比盲目dropna稳妥。如果失败比例低于 1% 且这些行不影响主分析,直接删;如果比例高,就得写针对性的替换规则。
4.2 merge 的四种连接方式与键的选择
多表关联翻车,八成是键选错或连接方式选错。merge的how参数有inner、left、right、outer四种,默认inner只保留两表都有的键,容易“莫名其妙少了很多行”。
merged = pd.merge( orders, customers, on="customer_id", how="left", validate="many_to_one", indicator=True ) print(merged["_merge"].value_counts())how="left"保留左表全部行,右表匹配不上就填NaN,这是最常用的方式。validate="many_to_one"会在右表键不唯一时直接报错,帮你提前发现“一对多被误当成一对一”的问题。indicator=True加一列_merge,值有both、left_only、right_only,跑完看一眼value_counts()就知道匹配率如何。如果left_only占比很高,说明键的取值对不上,可能是格式差异(带空格、大小写)或编码不同。
4.3 时间序列重采样的频率参数怎么设
resample的频率参数写错,结果会静默出错而不是报错。常用的是D(日)、W(周)、M(月)、Q(季)、Y(年),但要注意M是月末,MS是月初,两者结果差一个月。
ts = df.set_index("order_date")["amount"] monthly = ts.resample("MS").sum() # 月初为锚点 weekly = ts.resample("W-MON").sum() # 每周一为锚点MS和M的区别是血泪经验:用M做月度汇总,时间戳会落在月末,和业务上“按月统计”的口径对不上时很难排查。W-MON指定周一为周锚点,不写的话默认周日。重采样前确保索引是datetime且已排序,否则结果顺序会乱。
5. 避坑与排查:这套数据集练手时最容易踩的五个坑
5.1 编码报错反复出现
现象:read_csv抛UnicodeDecodeError: 'utf-8' codec can't decode byte...。原因:文件不是 UTF-8 编码,可能是latin1或gbk。解决:先试encoding="latin1",它能解码任意字节不报错,读进来后看有没有乱码;如果乱码集中在中文列,换gbk。不要盲目errors="ignore",那会静默丢字符。
5.2 数值列被读成 object
现象:df["price"].mean()报TypeError,或describe里这列只显示count、unique。原因:列里混了非数值字符(千分位、货币符号、"N/A")。解决:先astype(str).str.replace清掉符号,再pd.to_numeric(errors="coerce"),最后统计NaN比例决定填充还是删行。
5.3 groupby 之后行数对不上
现象:分组后某些类别消失了。原因:分组键含NaN,默认被丢弃。解决:groupby(..., dropna=False)。另一个原因是分组前做了dropna(),把整行删了,回去检查清洗步骤的顺序。
5.4 merge 后数据量暴增
现象:两表各几千行,merge 完变成几十万行。原因:连接键在某一侧不唯一,形成笛卡尔积。解决:merge 前对键做value_counts()检查唯一性,加validate="one_to_one"或many_to_one"让 pandas 帮你拦截。
5.5 时间序列重采样结果为空
现象:resample("M").sum()返回空或全NaN。原因:索引不是datetime类型,或者没排序。解决:df.index = pd.to_datetime(df.index)后df = df.sort_index(),再重采样。排序这一步经常被忘,不排序时resample行为不确定。
6. 进阶技巧:把数据集变成可复用的练习模板
练完一遍就丢,等于白练。我的习惯是把这套数据集的加载和清洗逻辑抽成一个可复用的小模块,下次换数据只改配置不改结构。核心思路是把“路径、分隔符、编码、日期列、缺失标记”这些易变项放进一个字典,加载函数读配置干活。
import pandas as pd from pathlib import Path CONFIGS = { "orders": { "file": "orders.csv", "sep": ",", "encoding": "utf-8", "parse_dates": ["order_date"], "na_values": ["NA", ""], }, "customers": { "file": "customers.txt", "sep": "\t", "encoding": "latin1", "parse_dates": [], "na_values": ["null"], }, } def load_dataset(name, base_dir="./datasets"): cfg = CONFIGS[name].copy() path = Path(base_dir) / cfg.pop("file") return pd.read_csv(path, **cfg) orders = load_dataset("orders") customers = load_dataset("customers")这个模板的价值在于:新增数据集只加一条配置,加载逻辑不用动;参数集中在一处,排查时一眼能看到某个文件用了什么编码和分隔符。cfg.pop("file")把文件名从参数字典里摘出来单独拼路径,剩下的键正好是read_csv的关键字参数,直接**cfg展开。这种写法在数据集文件多、格式杂的场景下特别省事。
验证清洗结果是否可靠,我一般用两个交叉检查:一是聚合后的总数和原始行数对得上(没意外丢行),二是关键数值列的describe在清洗前后分布没有剧烈偏移(填充没引入偏差)。这两条过了,再往下做分析和建模才踏实。
最后说个我自己的习惯:每练完一个数据集,把踩过的坑和对应的解决命令记在一个notes.md里,按“现象 → 原因 → 命令”三列记。下次遇到同类报错,翻笔记比搜引擎快得多。这套数据集真正的价值不在文件本身,而在于它逼你把 IO、清洗、聚合、时间序列这几条链路都走一遍,走完一遍再走第二遍,速度会快很多。希望帮到你。
本文还有配套的精品资源,点击获取