3-1 NumPy数据的容器及数据集划分

🎯 教学目标与重难点…

【三维目标】

  • 📚 知识目标

    • 理解 NumPy 与 Python 原生列表的区别;
    • 深刻掌握 ndarray 的核心数据类型(type)、数据元素类型(dtype)与空间维度逻辑结构(shape/ndim);
    • 掌握 np.array()np.arange()np.zeros()np.ones() 等常用创建方法;
    • 掌握二维数组的索引与精准切片语法;
    • 理解数据集划分(训练集与测试集)的必要性,掌握 train_test_split 的基本用法。
  • ⚙️ 能力目标

    • 能够熟练使用常用创建函数构建指定形状与初始值的 ndarray
    • 能够运用二维切片准确分离数据集中的“特征集 $X$”与“标签集 $y$”;
    • 能够结合 train_test_split 规范地将数据集划分为训练集与测试集,并正确核对维度形状(shape)。
  • 💡 素养目标

    • 通过性能对比实验,体会 NumPy 向量化计算相较于原生循环的巨大效率优势;
    • 建立数据预处理中的“防过拟合(防作弊)”工程意识与数据流空间维度思维。

【重点与难点】

  • 🟢 教学重点ndarray 的维度逻辑结构;常用创建函数特性;二维数组切片语法 (data[行, 列]);使用 train_test_split 进行数据拆分。
  • 🟡 教学难点:利用负数索引与步长进行的高维数据分离;train_test_split 返回的 4 个变量顺序与 shape 维度的对应关系。

📌 一、 课程导入(10 分钟)

  1. 对比实验:杂物箱 vs 专用零件盒

    • Python 原生 list 就像一个杂物箱,里面装的是指向乱七八糟内存地址的“纸条(指针)”,遍历修改极慢。
    • NumPy 的 ndarray 就像精密的零件盒,在内存中是一块连续紧密平铺的存储空间,只能存同一种类型,支持 CPU 矢量并行计算。
  2. 直观感受“向量化计算”的威力

    • 在 Jupyter Notebook 中给 500 万个数字同时乘以 2,对比耗时:
      • 原生 for 循环修改原列表:耗时约 0.33 秒
      • NumPy 向量化计算 (array * 2):耗时仅约 0.002 秒快了上百倍!)。
  • 👨‍🏫 教师活动:运行耗时对比代码,用高达上百倍的速度差距震撼学生;用“内存盒子”比喻讲解 listndarray 底层结构的差异。
  • 🧑‍🎓 学生活动:观察代码运行结果,体会运行时间的数量级差距,思考为什么机器学习处理海量数据必须依赖 NumPy。

避开繁琐的数学概念,通过直观的性能对比与内存比喻,快速激发学生的学习兴趣,建立对 NumPy 核心优势“向量化并行处理”的直观认知。


📖 二、 解决问题过程(一):创建数据容器 ndarray 与类型结构(20 分钟)

1. 四种常见创建函数对比

函数 数据来源 / 状态 默认数据类型(dtype 典型用途
np.array() 现有 Python 列表/元组 自动推断(跟传入数据一致) 手动输入已知数据
np.arange() 按等差规律自动计算生成 通常为 int64 / int32 产生连续递增序列
np.zeros() 全 0 填充 float64 预分配内存空间 / 清零占位
np.ones() 全 1 填充 float64 基准掩码 / 乘法初始化

2. ndarray 的类型与维度逻辑结构

  • 返回类型(Type):不管调用上述哪个函数,返回的统一是 numpy.ndarray(N-Dimensional Array)。
  • 核心身份证属性
    • dtype:里面存储的数据类型。
    • ndim:数组的维度数量。
    • shape:数组的空间形状/结构(元组形式)。
      • 0 维 (Scalar标量):单个数字(如 5),shape()
      • 1 维 (Vector向量):一排数据串,shape 类似 (5,)
      • 2 维 (Matrix矩阵):Excel 表格,shape 类似 (3, 4)(包含 3 行 4 列)。
      • 3 维及以上 (Tensor张量):一叠表格或多维数据流。
1import numpy as np
2
3# 观察不同创建函数返回的类型与结构
4arr1 = np.arange(1, 10)
5arr2 = np.zeros((3, 4)) # 传入 shape 元组 (3, 4)
6
7print("arr1 的类型:", type(arr1), "| dtype:", arr1.dtype, "| shape:", arr1.shape)
8print("arr2 的类型:", type(arr2), "| dtype:", arr2.dtype, "| shape:", arr2.shape)
  • 👨‍🏫 教师活动:在 Jupyter 中逐一演示创建函数的语法,重点提醒 zeros/ones 的默认 dtypefloat 这一踩坑点。
  • 🧑‍🎓 学生活动:跟练代码,使用 type().dtype.shape 打印验证各个数组的身份证属性。

打牢数据载体的底层概念,帮助学生彻底理清 ndarray 的类型与空间维度结构,避免后续写代码时因维度不匹配而报错。


💻 三、 解决问题过程(二):精准切片与特征分离(20 分钟)

1. 二维数组精准切片(机器学习核心操作)

二维切片公式:data[行切片, 列切片]

  • 获取前 5 行:data[0:5, :]
  • 获取最后一列(目标标签 $y$):data[:, -1]
  • 获取除去最后一列的所有特征(特征集 $X$):data[:, :-1]
 1import numpy as np
 2
 3# 模拟 5 个样本的数据集(前 3 列为特征,最后一列为标签)
 4dataset = np.array([
 5    [180, 75, 25, 0],
 6    [165, 55, 30, 1],
 7    [175, 70, 22, 0],
 8    [160, 50, 28, 1],
 9    [170, 65, 35, 0]
10])
11
12# 1. 提取特征集 X (所有行,前 3 列)
13X = dataset[:, :-1]
14
15# 2. 提取标签集 y (所有行,最后一列)
16y = dataset[:, -1]
17
18print("特征集 X 形状:", X.shape, "\n", X)
19print("标签集 y 形状:", y.shape, "\n", y)
  • 👨‍🏫 教师活动:结合板书画图演示二维表格的“行”与“列”切割过程;重点拆解 data[:, :-1]data[:, -1] 中逗号两侧及负数索引的物理意义。
  • 🧑‍🎓 学生活动:在 Jupyter Notebook 中运行切片代码,尝试修改切片范围(如提取特定行或特定列特征),观察输出结果并记录。

切片是机器学习数据预处理中最基础、最频繁的操作。摒弃纯数学抽象演示,直接对接真实数据集场景(分离特征 $X$ 与标签 $y$),帮助学生快速掌握二维数据的定位提取能力。


🚀 四、 解决问题过程(三):进阶——标准数据集划分 train_test_split(20 分钟)

1. 探究思考:为什么不能只用纯 NumPy 切片划分训练集和测试集?

如果我们直接用切片划分(例如 X_train = X[:8]X_test = X[8:]),会存在两个严重问题:

  1. 顺序偏差:如果原始数据是按类别排序的(如前 50 个全为健康,后 50 个全为患病),直接切片会导致训练集只有单一样本,模型根本学不到全貌。
  2. 考试作弊风险:必须把数据集打乱(Shuffle)并留出一部分测试集(模拟期末考试),才能真实检测模型有没有“死记硬背”。

2. 使用 train_test_split 规范划分

调用 scikit-learn 库中的 train_test_split 函数,只需一行代码就能同时完成随机打乱按比例切割

  • 核心参数

  • test_size=0.2:测试集占比 20%(训练集自动占 80%)。

  • random_state=42:随机数种子,确保每次运行代码时打乱顺序的结果一致(便于复现)。

  • 返回值(注意严格顺序!):X_train, X_test, y_train, y_test

 1import numpy as np
 2from sklearn.model_selection import train_test_split
 3
 4# 模拟 10 个样本的数据集 (3 个特征,1 个标签)
 5dataset = np.array([
 6    [180, 75, 25, 0], [165, 55, 30, 1], [175, 70, 22, 0], [160, 50, 28, 1], [170, 65, 35, 0],
 7    [182, 80, 29, 0], [158, 48, 31, 1], [172, 68, 24, 0], [168, 60, 27, 1], [178, 72, 33, 0]
 8])
 9
10# 步骤 1:NumPy 切片分离 X 与 y
11X = dataset[:, :-1]
12y = dataset[:, -1]
13
14# 步骤 2:train_test_split 划分训练集与测试集(8:2 划分)
15X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
16
17# 步骤 3:打印核对四要素的 shape
18print("原始 X 形状:", X.shape)
19print("X_train 形状:", X_train.shape, "| X_test 形状:", X_test.shape)
20print("y_train 形状:", y_train.shape, "    | y_test 形状:", y_test.shape)
  • 👨‍🏫 教师活动:提出“只用切片划分训练集有什么隐患”的问题抛砖引玉;演示 train_test_split 代码,用口诀“先 X 后 y,先 Train 后 Test”帮助学生记忆 4 个变量的解包顺序,带领学生核对划分前后的 shape 变化。
  • 🧑‍🎓 学生活动:动手跟练,尝试更改 test_size0.3 或改变 random_state 的数值,打印观察 X_trainX_test 的行数如何自动按比例调整。

通过“从直接切片的缺陷”自然过渡到“专用工具库划分”,帮助学生理解机器学习防止过拟合的核心理念;通过对解包顺序与 shape 的严格校验,强化代码规范性与维度感知能力。


✍️ 五、 解决问题过程(四):充分课堂练习与巩固(20 分钟)

📝 任务一:ndarray 数组创建与学生成绩分析实操…

背景与题目

  1. 请使用 np.zeros() 创建一个 $5 \times 4$ 的二维数组,并检查打印其 typedtype
  2. 假设该矩阵代表 5 位学生的 4 门课程成绩(初始全为 0)。请利用切片和索引,将第 0 位学生(第 0 行)的所有成绩赋值修改为 85
  3. 请利用二维切片,提取出前 3 位学生(第 02 行)的后 2 门课程成绩(第 23 列)。
🔍 查看参考代码与解析…
 1import numpy as np
 2
 3# 1. 创建 5x4 全 0 数组
 4scores = np.zeros((5, 4))
 5print("数据类型:", type(scores))     # <class 'numpy.ndarray'>
 6print("元素类型:", scores.dtype)    # float64
 7
 8# 2. 修改第 0 行的成绩为 85
 9scores[0, :] = 85
10
11# 3. 提取前 3 位学生的后 2 门课程成绩
12sub_scores = scores[0:3, 2:4]
13print("前 3 位学生的后 2 门课程成绩:\n", sub_scores)

📝 任务二:二手房数据集“切片分离 + 规范划分”全流程实操…

背景与题目: 某中介公司收集了 10 套二手房的数据,每套数据包含 4 个维度:[面积(㎡), 房间数(间), 朝向代码, 售价(万元)]。 请编写完整的 Python 代码完成以下数据集标准预处理流程:

  1. 使用 NumPy 切片将 houses 数据集拆分为特征矩阵 X(前 3 列)和目标标签 y(售价,最后一列)。
  2. 使用 train_test_split 将数据集按 7:3 的比例划分为训练集和测试集(设置 random_state=10)。
  3. 打印输出 X_trainX_testy_trainy_testshape,验证数据维度是否正确。
1import numpy as np
2from sklearn.model_selection import train_test_split
3
4houses = np.array([
5    [85, 2, 1, 220],  [120, 3, 2, 350], [60, 1, 1, 150],  [150, 4, 3, 500], [95, 2, 2, 280],
6    [110, 3, 1, 310], [130, 3, 3, 400], [70, 2, 1, 180],  [160, 4, 2, 550], [100, 2, 1, 290]
7])
🔍 查看参考代码与解析…
 1# 1. 切片分离特征 X 与标签 y
 2X = houses[:, :-1]
 3y = houses[:, -1]
 4
 5# 2. 7:3 划分训练集与测试集
 6X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)
 7
 8# 3. 打印验证 shape
 9print("原始特征 X 形状:", X.shape)         # (10, 3)
10print("训练集 X_train 形状:", X_train.shape) # (7, 3)
11print("测试集 X_test 形状:", X_test.shape)   # (3, 3)
12print("训练集 y_train 形状:", y_train.shape) # (7,)
13print("测试集 y_test 形状:", y_test.shape)   # (3,)

📝 六、 课堂小结(5 分钟)

flowchart LR
    root["📊 3-1 NumPy 基础与数据集划分"]

    subgraph C1["💡 核心容器与结构"]
        direction TB
        A1["类型:numpy.ndarray"]
        A2["物理内存:连续平铺 (快百倍)"]
        A3["属性:dtype / shape / ndim"]
    end

    subgraph C2["✂️ 1. 切片分离 (NumPy)"]
        direction TB
        B1["公式:data[行切片, 列切片]"]
        B2["分离特征:X = data[:, :-1]"]
        B3["分离标签:y = data[:, -1]"]
    end

    subgraph C3["🎲 2. 标准划分 (sklearn)"]
        direction TB
        C1_node["函数:train_test_split()"]
        C2_node["功能:打乱(Shuffle) + 按比例(test_size)"]
        C3_node["返回:X_train, X_test, y_train, y_test"]
    end

    root --> C1
    root --> C2
    root --> C3

    %% 自定义主题色彩美化
    style root fill:#4b6cb7,stroke:#253b6e,color:#fff,stroke-width:2px,rx:8px,ry:8px
    style C1 fill:#e3f2fd,stroke:#2196f3,stroke-width:1px
    style C2 fill:#fff3e0,stroke:#ff9800,stroke-width:1px
    style C3 fill:#e8f5e9,stroke:#4caf50,stroke-width:1px

✏️ 随堂检测与互动练习

点击展开:随堂测试题(带解析)

一、 单选题

  1. 使用 np.zeros((3, 3)) 创建的数组,其元素的数据类型(dtype)默认是?
  • A. int32
  • B. int64
  • C. float64
  • D. bool
【答案】

【解析】Cnp.zeros()np.ones() 的默认元素类型是浮点型 float64

  1. 在 NumPy 中,对于一个二维数组 arr,如果想要提取所有行第 1 列到第 3 列(不包含第 3 列),正确的写法是?
  • A. arr[1:3, :]
  • B. arr[:, 1:3]
  • C. arr[:, 1:4]
  • D. arr[1:3, 1:3]
【答案】

【解析】B。根据 data[行, 列] 语法,所有行用 : 表示;列取 1:3(左闭右开,包含第 1、2 列)。

  1. 执行 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2),若 X 的形状为 (100, 5),则 X_train 的形状是?
  • A. (20, 5)
  • B. (80, 5)
  • C. (80,)
  • D. (100, 4)
【答案】

【解析】Btest_size=0.2 表示测试集占 20%(20 条),则训练集 X_train 占 80%(80 条),特征列数(5 列)保持不变,因此形状为 (80, 5)

二、 简答与思考题

题目:在数据集划分时,为什么不能直接截取数组的前 80% 作为训练集,而是建议使用 train_test_split

【答案】
  1. 消除样本排列顺序偏差:直接截取可能因为原始数据是按类别或时间有序排列的,导致训练集缺乏某些类别的特征。
  2. 自动打乱与比例分配train_test_split 能自动进行随机打乱(Shuffle)并精确按指定比例切分,避免模型过拟合,更真实地评估模型性能。

📮 七、 课后作业与拓展

📮 课后作业…
  1. 基础巩固(类型转换与矩阵修改)
  • 使用 np.ones((5, 5)) 创建数组 arr,使用 .astype(int) 方法将其转为整数类型。
  • 编写切片代码,将该矩阵最外围一圈的元素全部修改赋值为 0,并打印输出修改后的矩阵。
  1. 机器学习预处理完整链条实操
  • 自定义创建一个包含 20 行 6 列的 NumPy 数组模拟数据集(前 5 列为特征,最后一列为标签 01)。
  • 编写代码提取特征集 X 与标签集 y
  • 调用 train_test_split 将其划分为训练集与测试集(测试集占比 25%,设置 random_state=2026)。
  • 打印并核对 X_trainX_testy_trainy_test 这 4 个变量的 .shape
  1. 预习任务:预习下一个章节,思考:如果在划分数据集后发现特征数据的单位不统一(例如身高 cm 和体重 kg 数量级相差巨大),我们应该如何对矩阵进行标准化处理?

📋 八、 板书设计

🛠️ 板书设计…
 13-1 NumPy 基础与数据集划分
 2
 3一、 NumPy vs List
 4  - List: 内存分散、存指针(慢)
 5  - ndarray: 连续平铺内存、同类型(矢量化计算,快百倍!)
 6
 7二、 核心类型与属性
 8  - 返回对象: numpy.ndarray
 9  - 核心属性: dtype, shape (维度结构: 0D/1D/2D/3D), ndim
10  - 创建函数: np.array(), np.arange(), np.zeros(), np.ones() (默认 float)
11
12三、 2维数组切片 (NumPy 拆分 X/y)
13     公式: data[ 行切片 , 列切片 ]
14     - 特征集 X:  data[ : , :-1 ]   (所有行,除最后一列)
15     - 标签集 y:  data[ : ,  -1 ]   (所有行,仅最后一列)
16
17四、 数据集标准划分 (sklearn 拆分 Train/Test)
18     代码: X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
19     核心作用:
20     1. 随机打乱 (Shuffle) -> 防止顺序偏差
21     2. 留出测试集 -> 模拟考试,防止死记硬背 (防过拟合)