P2-12.3 准备学习数据集(dataset)的直觉¶
Section ID:
P2-12.3Version:v2026.07.23
在 P2-12.1 中,我们把 DataFrame 读成表格型数据结构。在 P2-12.2 中,我们从这张表里挑出需要的列,用条件筛掉行,并查看了总结值。现在问题再往前走一步:如果要把这张表变成模型能读取的学习数据集,需要准备什么?
这里重要的一点是,会熟练使用 Pandas 与 会准备学习数据集 并不是同一件事。前者是操作表格的技术,后者是决定模型接收什么输入、学习什么答案的工作。
本节说明 数据集(dataset)、特征(feature)、目标(target)、验证(validation)、数据泄漏(data leakage) 的基本区分。DataFrame 与表选择的代表性说明放在 P2-12.1、P2-12.2 与概念词汇表中,这里关注的是:如何把那张表重新组织成学习输入与答案。
如果说 Chapter 11 让我们得到可计算的数组形状,那么现在的 Chapter 12 就是在表中决定哪些列要留下、哪些列要去掉。这里整理出的输入与答案候选,会接到下一章 Chapter 13 的可视化,以及 Chapter 14 的记录整理。
核心判断标准:准备学习数据集(dataset)的直觉¶
- 你可以把学习数据集准备说明成
把原始表重新组织成模型输入与答案的过程。 - 你可以把一行(row)读成一个样本(sample),把一列(column)读成一个特征(feature)或目标(target)候选。
- 你可以说明为什么要分开输入
X与答案y。 - 你可以说明为什么要分开 train、validation、test。
- 你可以说明为什么数据泄漏(data leakage)会扭曲评估。
- 你可以说明 Pandas 用于读数据、选择、过滤、生成列、做基础检查,而训练划分常常会与其他工具一起处理。
先抓住的一个画面¶
本节最先要抓住的画面是:一行(row)通常是一个样本(sample),一列(column)是特征(feature)或目标(target)候选,X 是输入列的集合,y 是要预测的答案列。
再看一次一个小型学生表:
| 在表里看到的东西 | 换成学习数据集语言后如何读取 |
|---|---|
| 一行 | 一个学生样本 |
region、absences、score 列 | 输入特征候选 |
passed 列 | 答案目标候选 |
X.shape = (4, 3) | 4 个样本,3 个特征 |
y.shape = (4,) | 4 个样本对应的一组答案 |
如果先把这张图景抓住,后面的 train_test_split、fit、predict 读起来就不会那么抽象。
三个判断标准¶
| 标准 | 为什么重要 | 本节需要达到的理解程度 |
|---|---|---|
| 为什么不能把整张表原样拿去训练 | 它能让你区分表结构与模型输入结构。 | 理解到和问题无关的列、答案列、标识列可能会混在一起。 |
为什么要分开 X 与 y | 它能把输入与答案之间的边界说清楚。 | 理解到必须把输入与要预测的对象分开,学习结构才会清晰。 |
| 为什么要先划分数据 | 它是防止泄漏与防止评估被抬高的判断标准。 | 理解到这是为了避免把后续评估要用的信息提前漏进训练里。 |
| 术语 | 本节先固定的含义 |
|---|---|
| 数据集(dataset) | 为了学习或评估而整理好的样本与变量集合。 |
| 特征(feature) | 用作模型输入的列或值。 |
| 目标(target) | 模型要预测的答案列。 |
| 验证(validation) | 用来比较设置与选择的中间评估数据。 |
| 数据泄漏(data leakage) | 在预测时本来不该知道的信息,提前混进学习过程的问题。 |
不是把表原样喂给模型,而是按问题重新组织¶
原始表往往更适合人来阅读,但未必已经是模型可以直接学习的形状。
例如,看下面这张表。
| student_id | name | region | absences | score | passed |
|---|---|---|---|---|---|
| S001 | Kim | Seoul | 1 | 82 | yes |
| S002 | Park | Busan | 5 | 45 | no |
| S003 | Lee | Seoul | 0 | 90 | yes |
| S004 | Choi | Busan | 2 | 73 | yes |
人从这张表里可以提出很多问题。
- 是不是想预测分数(score)?
- 是不是想分类通过/不通过(passed)?
- 是不是想观察按地区(region)划分的倾向?
即使是同一张表,只要问题变了,学习数据集的组织方式也会跟着变。
| 问题 | y 候选 | X 候选 |
|---|---|---|
| 是不是要预测是否通过 | passed | region、absences、score 等 |
| 是不是要预测分数 | score | 需要重新考虑是否排除 passed,以及 region、absences |
| 是不是要识别学生身份 | 需要单独的问题定义 | student_id、name 通常更像标识列 |
所以,数据集准备与其说是整理表格,不如更准确地理解成:根据问题重新划定输入与答案的边界。
分开 X 与 y¶
scikit-learn 文档通常把 X 用作输入特征矩阵(feature matrix),把 y 用作目标(target)。在 glossary 中,feature 被说明成用数值或类别值来表示样本(sample)的量,在数据矩阵里通常以列(columns)表示。sample 通常被说明成一个 feature vector,而 target 则是在监督学习(supervised learning)中的因变量(dependent variable)。
这里先记住下面这组差别就够了。
X:模型会拿来观察并用于判断的输入列集合y:模型要预测的答案列
看一个小例子。
问题场景:要把学生表改成预测是否通过的分类问题时,必须先把输入 X 与答案 y 分开。 输入(input):一个小型 DataFrame,里面包含每位学生的地区、缺勤数、分数、是否通过。 预期输出(output):输入列集合 X 与答案列 y 被分离出来。 要确认的概念:准备学习数据集不是原样使用整张表,而是按照问题定义拆出输入与目标。
这段代码可以读成一个预测 passed 的分类(classification)问题。
- 每一行都是一个学生样本(sample)。
region、absences、score是输入特征(feature)。passed是答案目标(target)。
画成图后如下。
flowchart TD
raw["原始 DataFrame<br/>student_id, region, absences, score, passed"]
choose["选择目标<br/>你想预测什么?"]
split["拆分列<br/>分开 X 和 y"]
x["X<br/>region, absences, score"]
y["y<br/>passed"]
raw --> choose --> split
split --> x
split --> y
核心点在于:X 与 y 不是一开始就固定好的。先有问题定义,再去拆列。
这里也保留一层会直接接到 P2-11.2 的视角。
| 表达 | 在本节里的读取方式 |
|---|---|
X.shape[0] | 样本(sample)数 |
X.shape[1] | 特征(feature)数 |
y.shape[0] | 答案个数,通常和样本数相同 |
所以,说要分开 X 与 y,并不只是拆列名,也是在决定模型要读取的数组形状(shape)。
行数稍多的输入也可以用同样方式确认这种分离。输入文件与 P2-12.2 相同,是 student-progress-samples.csv。一行表示一名学生的学习记录。这里把 passed 作为目标(target),把其他一部分列作为输入候选。
问题场景:想确认原始表中的输入列组 X、答案列 y、简单 train/test 候选会分成什么形状。 输入(input):36 行学生学习进度 CSV、feature_columns、target_column。 期望输出(output):X、y、编码后的输入列、train/test 分割候选的 shape。 要确认的概念:数据集准备是在模型学习之前,先在表中确定输入与答案的边界,并把评估用行单独留出来。
同样的代码也可以通过 p2_12_3_dataset_split_preview.py 执行。这里不会训练 scikit-learn 模型。Part 2 的目标,是先在 Pandas 层级确认哪些列是输入、哪一列是答案,以及哪些行应该留给之后检查。
不能把所有列原样放进去¶
表里的列并不都适合作为模型输入。通常要区分下面三类。
- 可以直接用于预测的列
- 需要先做变换的列
- 应该从输入里剔除的列
例如:
| 列 | 能否直接使用 | 原因 |
|---|---|---|
absences | 相对可以直接使用 | 因为它是数值列 |
region | 需要变换 | 因为它是字符串类别 |
student_id | 通常排除 | 因为它是标识列,本身未必代表一般模式 |
passed | 如果它是目标,就要从输入中排除 | 因为把答案列放进输入,会显著增加泄漏风险 |
这里像 region 这样的类别型(categorical)列,可能需要根据模型先转换成数值形式。pandas 的 get_dummies() 被介绍为把类别变量转换成 dummy/indicator variables 的函数。
例如:
问题场景:像 region 这样的字符串类别,许多模型不能直接读取,因此需要改变其表示方式。 输入(input):由 region、absences、score 组成的输入 X。 预期输出(output):region 被展开成多个 0/1 列之后的 X_encoded。 要确认的概念:类别列在学习之前,可能需要先重新表示成数值形式。
这个阶段的关键并不是背下编码方法。更重要的问题是:这列能不能像数字一样直接读?还是应该先改变表示方式?
如果不区分标识列与答案列,就很容易读错¶
一个常见混淆是:会感觉眼前出现的所有列都像是 feature。但标识列(identifier)与目标(target)的角色和特征并不一样。
例如:
student_id是区分每一行的标记。name是方便人阅读的名字。passed可能就是要预测的答案。
如果把它们原样放进输入,会出现两个问题。
- 模型可能被偶然的标识信息吸引,而不是学习一般模式。
- 太接近答案的信息可能流入输入,从而把评估抬高。
这种感觉在后面还会继续重要。好的数据集不是因为列很多才好,而更接近于:只保留了符合问题定义的列的数据集。
分成 train、validation、test¶
数据集准备中的下一个关键步骤是划分(split)。scikit-learn 的 train_test_split 文档把它说明成:把数组或矩阵随机拆成 train 与 test 子集的工具。
这里可以先这样理解。
- train:模型真正拿来学习的数据
- validation:用来检查设置、比较选择的数据
- test:最后当成第一次见到的数据来确认的数据
画成图就是:
flowchart TD
full["完整数据集"]
train["训练集<br/>用于学习的数据"]
val["验证集<br/>用于比较设置的数据"]
test["测试集<br/>用于最终检查的数据"]
full --> train
full --> val
full --> test
为什么一定要分开?因为模型在已经见过的数据上表现好,并不够。我们真正想知道的是:它在第一次见到的数据上,能不能也差不多地工作?
这个视角会直接连到 Part 3 的过拟合(overfitting)、泛化(generalization)、评估指标(metric)。
先划分,再只从 train 学到训练中要用的变换规则¶
scikit-learn 的 common pitfalls 文档强烈警告两类错误。
- inconsistent preprocessing:对训练数据与测试数据应用了不同预处理的错误
- data leakage:把预测时本不该知道的信息混进学习过程的错误
其中,文档特别说明 test data should never be used to make choices about the model,并把不要对测试数据调用 fit 或 fit_transform作为一般规则来提醒。
这里可以把下面这条顺序当作标准。
- 先从整张表里挑出输入候选与答案候选。
- 先把它们分成 train / validation / test。
- 像平均值、标准化、编码、特征选择这类
需要学出来的变换,只在 train 上建立规则。 - 对 validation、test 只应用同一套变换,不从它们身上再学习。
如果打乱这个顺序,就会变得像是模型提前偷看了答案。
把错误流程与更安全的流程并排看:
| 流程 | 问题 |
|---|---|
| 先用整份数据建立规则,再去划分 | test 信息可能提前混进来 |
| 先划分,再只用 train 建立规则 | 评估会更公平 |
本节先抓住的是这个判断标准,而不是实现细节。
再用下面的图看会更清楚。
flowchart TD
full["完整表格"]
choose["选择 X 和 y"]
split["先划分<br/>训练 / 验证 / 测试"]
fit_train["学习转换规则<br/>只在训练集上拟合"]
apply_other["应用相同转换<br/>只应用到验证集和测试集"]
bad["错误顺序<br/>先从完整表格学习规则"]
full --> choose --> split --> fit_train --> apply_other
full -. 避免 .-> bad
如果把错误顺序与更安全顺序重新写成问题,可以得到下面的表。
| 问题 | 更安全的回答 |
|---|---|
| 可以把整张表都整理完之后再划分吗? | 不可以,因为后续评估要用的信息可能会提前混进来,所以要先划分。 |
| 平均值、标准化、编码规则该从哪里学? | 只从 train 学。 |
| validation、test 的作用是什么? | 只接收 train 学到的同一套规则。 |
用案例来看¶
案例 1. 想预测是否通过,却把答案列一起塞进输入¶
假设学习者想用学生表做一个预测 是否通过(passed) 的模型。表里同时有 student_id、region、absences、score、passed。这时很容易想把所有看得见的列都放进 X。
但这样会出问题。passed 本来就是要预测的答案,却也被一起放进了输入;而 student_id 也可能只是区分学生的标记,并不直接对应一般模式。模型会更像是在偷看答案,或者被偶然的标识信息牵着走,而不是真的学到规则。
所以,在数据集准备里,先要决定到底想预测什么,然后把 y 分出来,再重新选择哪些特征该留下、哪些列该排除。接着先分 train、validation、test,再把像编码、缩放这样需要在训练中学规则的变换,只放在 train 一侧建立。
这个案例说明,数据集准备并不只是简单整理表格。即使是同一张表,X 与 y 的边界也会随问题变化;而如果划分顺序选错,评估就可能被抬高。Pandas 是这个过程前半段里用来挑列、检查结构的工具,而公平的学习评估,则必须连后面的划分与变换顺序也一起对齐。
读者在这里尤其要抓住下面这句话。
先分开 X 与 y,先分开 train / validation / test,再只从 train 建立要学习的变换规则。
Pandas 负责准备过程的前半段¶
Pandas 通常强在数据集准备的前半段。
- 读取 CSV、Excel、JSON 等原始来源。
- 只选出需要的列。
- 检查错误值或奇怪格式。
- 区分类别列与数值列。
- 进行基础的列生成与变换。
例如:
问题场景:在模型训练之前,你想先用眼睛检查一下输入列与答案列实际长什么样。 输入(input):已经分开的 X、y。 预期输出(output):输出 X 的前几行与 y 的前几个值。 要确认的概念:Pandas 很擅长在学习之前检查表结构与列角色。
或者:
问题场景:你需要检查是否存在缺失值,以及每一列被读成了什么类型。 输入(input):原始 DataFrame df。 预期输出(output):每列缺失值数量与每列数据类型列表。 要确认的概念:学习前检查不只是看值本身,也是在看缺失值与类型结构。
这样的代码,与其说是训练模型,不如更接近于在训练前检查表格。
相对地,真正的划分与学习 pipeline,通常会接到 scikit-learn 之类的工具上去处理。
例如:
问题场景:检查结束后,为了把学习与评估分开,你需要把数据分成 train/test。 输入(input):输入 X、答案 y、划分比例与随机种子。 预期输出(output):分成学习用与测试用的 X_train、X_test、y_train、y_test。 要确认的概念:表操作阶段与学习数据划分阶段是连着的,但角色不同。
这里重要的不是把函数用法全部背下来,而是理解表操作与学习划分是彼此衔接但不同的阶段。
如果用一句话概括学习数据集准备¶
如果把本节核心压缩成一句话,那么学习数据集准备就是:在原始表里重新分配 sample、feature、target 的角色,并守住划分顺序,避免评估被扭曲。
没有这个视角时,Pandas 代码越长,就越容易丢失“为什么要改列、为什么要分开”的判断。反过来,只要这个视角抓住了,即使还不懂所有预处理技术,你也可以继续追问:现在这段代码是在挑输入、挑答案,还是在防止泄漏?
检查清单¶
- 你能用一句话说出当前表里到底想预测什么吗?
- 你能区分哪一列是
y,哪些列是X候选吗? - 你能说明为什么把标识列与答案列直接放进输入会有风险吗?
- 你能说明为什么要分开 train / validation / test 吗?
- 你能说明为什么预处理规则只能从 train 学吗?
- 当你看到
X.shape = (4, 3)与y.shape = (4,)时,能说出哪个是样本数、哪个是特征数吗? - 你能说明数据集准备不是原样使用表格,而是按照问题定义拆成
X与y吗?
来源与参考资料¶
- pandas Developers, pandas.get_dummies, pandas 3.0.4 documentation,确认日期:2026-07-20。用于确认把 categorical variables 转换成 dummy/indicator variables 的示例。
- scikit-learn Developers, Glossary, scikit-learn 1.9.0 documentation,确认日期:2026-07-20。用于确认 1d/2d array、array-like、estimator input conventions 以及
X、y术语背景。 - scikit-learn Developers, train_test_split, scikit-learn 1.9.0 documentation,确认日期:2026-07-20。用于确认把 arrays 和 matrices 分成 train/test subsets 的 API,以及
test_size、random_state示例。 - scikit-learn Developers, Common pitfalls and recommended practices, scikit-learn 1.9.0 documentation,确认日期:2026-07-20。用于确认 train/test split 前后 preprocessing 顺序与 data leakage 注意说明。