【零基础学AI】第 13 章——NumPy 和向量

第 13 章 NumPy 和向量

文章目录

第 11 章用循环累加分钟数,第 12 章把重复动作整理成函数。现在有四名学生、三门课,每名学生需要一个均分,每门课也需要一个均分。你可以再写几组循环,也可以把数字整理成数组,让 NumPy 一次处理。

本章会完成一份成绩统计 Notebook,算出学生均分和课程均分,筛选全部及格的学生,再按课程批量调整分数。需要先读完第 12 章,能使用函数、列表和循环。

🌈 关于《AI 零基础 36 讲》

🧠 零基础也能开始学 AI

《AI 零基础 36 讲》是一套面向初学者的 AI 入门教程。课程从认识和使用 AI 开始,逐步学习

Python、数据处理、机器学习与深度学习,再进入大模型应用开发,最后完成个人知识库助手的综合实践。

📖 本篇是正式讲解文章

通过具体例子、原创图示和操作结果讲清概念。涉及编程的章节提供可运行代码,方便你边读边试。

🎯 读完以后,动手检验

建议按专栏顺序学习,并完成对应的课后训练,检查自己能否独立运用本章知识。

博客专栏:《AI 零基础 36 讲》

先把依赖安装到自己的环境

NumPy 是提供数组和数值计算功能的 Python 库,需要额外安装。在本章目录创建虚拟环境,再使用该环境自己的解释器安装依赖。

powershell 复制代码
python -m venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txt
.venv\Scripts\python.exe -c "import numpy; print(numpy.__version__)"

最后一条命令应打印 2.3.5。这是本章实际验收用的版本,版本固定用于复现。查看文档网站的 stable 页面时可能遇到更新的版本,当前示例的解释仍以交付代码和对应版本的文档为准。

安装成功却提示 ModuleNotFoundError,通常要先核对运行脚本的解释器。编辑器选中的 Python、终端里的 python 和安装时使用的 Python 可能来自不同环境。本章命令明确写出 .venv\Scripts\python.exe,可以让安装和运行指向同一个解释器。macOS 与 Linux 使用 .venv/bin/python。

列表与数组怎样做加法

先比较两行最小代码。

python 复制代码
import numpy as np

print([1, 2] + [3, 4])
print(np.array([1, 2]) + np.array([3, 4]))

列表得到 [1, 2, 3, 4],数组得到 [4 6]。列表加法把两组元素接起来,数组加法把对应位置的数字相加。数组打印时常省略元素之间的逗号,这是显示格式的差异。

import numpy as np 把 NumPy 导入并取一个短名字,后面写 np.array 就能调用创建数组的功能。数组 array 把多项数值按形状排列,NumPy 常用的数组类型叫 ndarray。普通数值数组使用统一的数据类型,方便批量执行运算。

python 复制代码
import numpy as np

values = np.array([1, 2, 3], dtype=np.float64)
print(values)
print(values.dtype)
print(values * 2)

三行结果分别是 [1. 2. 3.]、float64 和 [2. 4. 6.]。dtype 指定元素的数据类型,float64 使用 64 位表示浮点数。后面的乘法作用在每个元素上,不需要自己写三次乘二。

如果需要带小数的成绩调整,开始就用浮点类型会更清楚。整数数组里直接写入 80.5 时,小数部分可能被转换掉。混入"缺考"字符串也会改变构造出来的数组类型,本章成绩统一要求为数值。

一张成绩表有两个轴

我们把四名学生排成行,三门课程排成列。先看原始表格。

学生 Python 数据处理 AI 基础
小林 80 70 90
小周 60 85 75
小陈 95 90 85
小吴 50 60 70

对应数组如下。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75], [95, 90, 85], [50, 60, 70]], dtype=np.float64)
print(scores.shape)
print(scores.ndim)
print(scores.size)

结果是 (4, 3)、2 和 12。shape 是形状,表示每个轴分别有多少元素;ndim 是轴的数量;size 是全部元素数量。这里沿行方向有四项,沿列方向有三项,所以形状是四行三列。

轴 axis 是数组中可以沿着取元素或汇总的一个方向。本章二维数组的第 0 轴对应学生,第 1 轴对应课程。这两个方向的编号固定,但"学生"和"课程"的含义来自我们怎样放置数据,NumPy 不会从数字猜到表头。

向量描述一名学生,矩阵组织多名学生

向量可以先理解成按固定顺序排列的一组数字。一名学生的三门成绩就可以表示为 [80, 70, 90]。这个向量有三个分量,每个位置都对应一项特征,也就是用于描述样本的一个数值。换成其他任务,特征可能是学习时长、练习次数或测验得分。

多名学生的向量按行排列,就形成一张矩阵。本章用二维 ndarray 保存这张数字表,不使用 NumPy 里另有运算规则的 matrix 类型。

python 复制代码
import numpy as np

one_student = np.array([80, 70, 90])
one_row = np.array([[80, 70, 90]])
one_column = one_student.reshape(3, 1)
print(one_student.shape)
print(one_row.shape)
print(one_column.shape)

结果依次是 (3,)、(1, 3) 和 (3, 1)。一个一维数组不带明确的行列朝向,两个二维数组则分别有一行和一列。reshape 在元素总数不变的前提下改变形状。(3,) 里的逗号表示单元素元组,和第 11 章的元组写法一致。

后面做模型输入时,特征顺序要固定。把 Python 与 AI 基础两列互换,数组形状仍然是四行三列,数字的含义已经变了。形状检查能发现一部分问题,列名和顺序也要保留。本章用 SUBJECTS 常量和 CSV 表头记录这个顺序。

同时写行下标和列下标

二维数组取一个元素时,可以在同一对方括号里写两个下标。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75], [95, 90, 85], [50, 60, 70]])
print(scores[1, 2])
print(scores[0])
print(scores[:, 0])
print(scores[:2, 1:])

第一行取到第二名学生、第三门课的 75。第二行得到第一名学生的 [80 70 90]。第三行取所有学生的第一门课,得到 [80 60 95 50]。第四行取前两名学生的后两门课,得到两行两列的数组。

下标仍然从 0 开始,切片右端仍不包括在内。冒号表示这一轴全部保留,scores[:, 0] 保留全部行并选第 0 列。先在原表上圈出想取的区域,再翻译成下标,能更快看出行列是否写反。

数组的基本切片通常得到视图。视图与原数组共享底层数据,修改视图可能改到原数组。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75]])
view = scores[:, 0]
view[0] = 0
print(scores[0, 0])
copied = scores[:, 1].copy()
copied[0] = 0
print(scores[0, 1])

输出是 0 和 70。想保留原成绩,先使用 copy 再修改。这里说明的是基本切片;布尔索引等其他取数方式可能产生副本,不能把所有索引都当作视图。

求均分之前先确定汇总方向

聚合就是把一组数字汇总成较少的数字,比如总和、均值、最大值。下面算三种均分。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75], [95, 90, 85], [50, 60, 70]], dtype=np.float64)
print(scores.mean(axis=1))
print(scores.mean(axis=0))
print(scores.mean())

axis=1 汇总课程这个轴,每名学生保留一个均分,得到约 80、73.33、90、60。axis=0 汇总学生这个轴,每门课程保留一个均分,得到 71.25、76.25、80。没有指定 axis 时,所有十二个数一起平均,得到约 75.83。

可以把 axis 参数读成"要消去哪个方向"。四行三列沿第 1 轴汇总,结果有四个数;沿第 0 轴汇总,结果有三个数。不要机械记成"0 求列,1 求行"然后直接用到三维数组上,本章先用结果长度核对二维数据的含义。

sum、min 和 max 分别求总和、最小值和最大值,也能使用 axis。查看均分时,显示到两位小数方便阅读,计算过程先保留原精度。均分的小数差异不要用字符串比较判断,程序使用 np.allclose 检查浮点结果是否在允许误差内一致。

广播让一组调整值作用于每一行

现在按课程给每名学生分别加 5、0、2 分。三个数字与三列对应,可以直接相加。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75]])
bonus = np.array([5, 0, 2])
print(scores + bonus)

结果是两行 [85 70 92] 和 [65 85 77]。这种让较小形状参与较大数组运算的规则叫广播 broadcasting。本例把同一组三列调整值用于每一名学生。

广播从形状最右边的轴开始比较。两边大小相同,或其中一边为 1,就能对应;较短形状在左侧缺少的轴按 1 看待。四行三列的 (4, 3) 和三元素的 (3,) 能对应,最后一个轴都是 3。把调整值写成 (2,),最后一个轴变成 3 与 2,会产生 ValueError。

如果想让每名学生所有课程都加上自己的调整量,四个数字需要变成 (4, 1),这样每行的一项才能扩展到三列。仅仅"总元素数相近"不足以决定能不能广播,要按轴比较形状。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75], [95, 90, 85], [50, 60, 70]])
student_bonus = np.array([1, 2, 3, 4]).reshape(4, 1)
print(scores + student_bonus)

四行结果依次是 [81 71 91]、[62 87 77]、[98 93 88]、[54 64 74]。小林每门加一,小周每门加二,其余依此类推。这个调整与前面的按课程加分回答不同的需求,数字摆在哪个轴上会决定作用对象。

汇总后怎样保留一个轴

如果想把每名学生的成绩减去自己的均分,均分也要与原表按行对应。mean 的 keepdims=True 会在汇总后保留长度为 1 的轴。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75]], dtype=np.float64)
means = scores.mean(axis=1, keepdims=True)
print(means.shape)
print(np.round(scores - means, 2))

均分形状是 (2, 1),差值两行约为 [0 -10 10] 和 [-13.33 11.67 1.67]。小林每门都减去 80,小周每门都减去约 73.33。负值只表示低于该学生的个人均分,原成绩仍保留在 scores 中。np.round 在这里帮助展示两位小数,没有把原始成绩改成整数。

数组结果保存前要转换格式

第 12 章直接把字典和列表交给 JSON 模块。本章计算出来的 ndarray,需要先用 tolist 转成普通 Python 列表,单个均值也可以用 float 转成 Python 浮点数。

python 复制代码
import json
import numpy as np

means = np.array([80, 73.33333333333333])
print(json.dumps({"means": means.tolist()}))

结果是一段含两个均分的 JSON 文本。主程序保存结果时采用相同做法,读回后便能用第 12 章学过的列表与字典操作核对。数据在不同工具之间传递时,既要保留数值,也要留住列名、形状与统计口径。

用布尔数组筛选成绩

比较数组与数字,会逐项产生布尔值组成的数组。我们可以用它筛选行。

python 复制代码
import numpy as np

scores = np.array([[80, 70, 90], [60, 85, 75], [95, 90, 85], [50, 60, 70]])
mask = (scores >= 60).all(axis=1)
print(mask)
print(scores[mask].shape)

mask 得到 [ True True True False],筛选结果形状为 (3, 3)。all 要求每项都为真,any 要求至少一项为真。axis=1 把每名学生的三个及格判断汇总成一个真假值,然后保留为真的学生行。

直接把多个元素的布尔数组放进 if,Python 无法知道你想要求全部为真还是至少一个为真,会提示真假含糊。要先用 all 或 any 说清口径。完整的错误、修复代码及结果见 error-samples.md。

跑完这一份成绩统计

score_stats.py 保存本章数据、统计函数和循环对照。程序要求二维数组、每行三门课程,至少一名学生,所有成绩都在 0 到 100 之间且没有缺失或无穷值。输入不符合条件时会产生带原因的 ValueError,避免把错误数据继续算成正常均分。

powershell 复制代码
.venv\Scripts\python.exe examples/score_stats.py

你会看到形状 (4, 3)、学生与课程均分、全部及格判断,以及"批量与循环一致 True"。结果同时保存到 score_summary.json。调整后的成绩使用 np.minimum 截到 100 分,给 95 加 5 后得到 100。

Notebook 保存在 score_analysis.ipynb,已经保留实际执行输出。在第 9 章的编辑器中打开它,选择安装了本章依赖的 Python 内核,再从头运行全部单元格。

留一道形状练习

在 Notebook 中取出小周和小陈的 Python 与 AI 基础成绩,结果形状应为 (2, 2);再算这两名学生在这两门课上的个人均分,应得到 67.5 和 90。先用纸面表格确认取了哪些数,再写索引和 axis。其余练习及答案见 【零基础学AI】第 13 章课后练习与答案。

下一章给数字重新带上列名,使用 Pandas 读取、清洗和保存表格。

资料来源

相关推荐
艾莉丝努力练剑1 小时前
【AI大模型接入SDK】ChatServer接口实现
c++·人工智能·学习·ai·架构·大模型·sdk
长不大的杨瑞杰1 小时前
把 Spring Boot 接口变成 AI 工具:OpenAPI、参数映射与异常语义化实战
人工智能·spring boot·后端
u1301301 小时前
GitHub 热榜项目:日榜(2026-10-19)
人工智能·github
QinHsiu1 小时前
语音识别的真瓶颈不是模型,是“已输出的字还能不能改”:网易有道R2T2登顶Hugging Face
人工智能
我喜欢就喜欢1 小时前
DeepSeek Harness 开源贡献手记:从零到合入主线
开发语言·人工智能
Είναι η κοπέλα1 小时前
Stable Diffusion文生图:本地 AI 绘画事实标准入门
人工智能·stable diffusion
znnnk1 小时前
2026 前端工程化新纪元:Rust 重塑工具链,AI Agent 接管流水线
前端·人工智能·rust
QinHsiu1 小时前
Agent没报错、退出码是0,但文件是坏的:上下文压缩正在静默污染你的工作产物
人工智能
DQQzero1 小时前
84.66% 的“实锤“是怎么来的:拆解模型指纹工具的闭集陷阱
人工智能·机器学习·ai·模型指纹