知识点思维导图
29 个知识节点
参考资料
Python(20) - NumPy 基础
读完后,你应能完成以下任务:
- 绘制“Python(20) - NumPy 基础 / 先建立前端锚点”的关键对象与数据流,解释“它最大的不同是——你不再写循环,而是把运算直接作用在整个数组上(这叫"向量化")。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(20) - NumPy 基础 / 安装与第一个数组”设计正常与异常输入,验证“因为普通 Python list 和 JS 数组一样是"通用容器",不为数学运算优化;”,输出首个偏差位置与回归测试结果。
- 实现“Python(20) - NumPy 基础 / 核心干货一:向量化运算(不写循环)”的最小代码或配置,检验“这是 NumPy 的灵魂。”,输出命令、结果与 Diff,并说明不适用边界。
你在前端处理数据,遇到一组数字想整体翻倍、求和、归一化,第一反应是
arr.map(...)/arr.reduce(...),逐个元素地循环。到了数据处理和 AI 这一阶段,数据量从几十条变成几十万行 × 上百列,逐个 for 循环会慢到无法接受。本篇解决一个核心问题:怎么把"对一堆数字逐个操作"变成"对整个数组一次性操作"——这就是 NumPy 的全部价值,也是后面 Pandas、向量、Embedding、机器学习的共同地基。
一、先建立前端锚点
一句话切入:NumPy 的 ndarray ≈ 一个开了挂的「超级 Array」。它最大的不同是——你不再写循环,而是把运算直接作用在整个数组上(这叫"向量化")。
| 你在 JS 里这么做 | NumPy 地道写法 | 说明 |
|---|---|---|
arr.map(x => x * 2) |
arr * 2 |
整个数组一次性翻倍,不写循环 |
arr.map((x, i) => x + b[i]) |
arr + b |
两个数组逐位相加 |
arr.reduce((a, b) => a + b, 0) |
arr.sum() |
求和 |
arr.filter(x => x > 0) |
arr[arr > 0] |
布尔筛选 |
Math.sqrt 配 map 逐个开方 |
np.sqrt(arr) |
整个数组开方 |
new Array(5).fill(0) |
np.zeros(5) |
创建全 0 数组 |
记住这个心智图:凡是你想写 for / map 逐个处理数字的地方,NumPy 让你直接对数组本身运算。 这就是"向量化",它既好写,又因为底层是 C 实现而快上几十倍。
二、安装与第一个数组
NumPy 是第三方库,不在标准库里,先装(建议在虚拟环境里,详见第 12 篇):
pip install numpy
约定俗成的导入别名是 np,几乎所有教程都这么写,照做即可:
对照 JS——JS 里数组就是数组,没有"转换"这一步:
const arr = [1, 2, 3, 4] // 普通 JS 数组
为什么 Python 要多包一层 np.array?因为普通 Python list 和 JS 数组一样是"通用容器",不为数学运算优化;np.array 才是那个"超级 Array",下面的所有能力都建立在它之上。
三、核心干货一:向量化运算(不写循环)
这是 NumPy 的灵魂。先看痛点——纯 Python 给一组数字整体翻倍要写循环:
NumPy 直接对数组运算,没有循环:
并排看 JS,能看出 NumPy 写法更接近"数学公式"而非"遍历逻辑":
const arr = [1, 2, 3, 4]
const doubled = arr.map(x => x * 2) // JS 必须显式 map 逐个处理
两个数组之间也能逐位运算(前提是形状匹配):
对照 JS——你得手动 map 配下标,啰嗦且易错:
const a = [1, 2, 3]
const b = [10, 20, 30]
const sum = a.map((x, i) => x + b[i]) // [11, 22, 33]
⚠️ 关键边界:
a * b在 NumPy 里是"逐位相乘",不是线性代数里的矩阵乘法。矩阵乘法要用a @ b或np.dot(a, b)。这是从前端转过来最容易想当然的点。
四、核心干货二:dtype 和 shape(和 JS 数组最大的不同)
JS 数组可以随便混类型、随便变长:[1, "a", true] 完全合法。NumPy 数组恰恰相反——它有两个铁律:
- 同质:所有元素必须是同一种类型(
dtype)。 - 定形:形状(
shape)在创建时确定,是个有维度的网格。
二维数组(你可以理解成"数组的数组",但它是规整的矩形网格,不是 JS 那种参差不齐的嵌套):
dtype 为什么重要?因为它直接决定内存和精度。比如机器学习里常把数据转成 float32 省一半内存:
对照 JS——JS 数组完全没有这套类型/形状约束,所以也享受不到 NumPy 的内存与速度优化。最接近的只有 TypedArray(如 Float32Array),其实 NumPy 的 dtype 思路和它一脉相承。
五、核心干货三:索引与切片(最大的坑在这)
一维索引切片,语法和 Python 列表、JS 数组都很像:
二维用逗号分隔行列,比 JS 的 matrix[i][j] 更简洁:
⚠️ 本篇最大的坑:切片返回的是「视图(view)」而不是「拷贝」。 JS 的
arr.slice(1, 3)给你一个全新数组,改它不影响原数组。NumPy 的切片默认是原数组的一个"窗口",改切片会改到原数组:
对照 JS,这是新手必踩的认知差:
const arr = [1, 2, 3, 4, 5]
const sub = arr.slice(1, 3) // 全新数组
sub[0] = 999
console.log(arr) // [1, 2, 3, 4, 5] 原数组安然无恙
需要"像 JS 那样的独立副本"时,显式调用 .copy():
为什么 NumPy 这么设计?因为视图不复制数据,处理超大数组时省内存又快。这是性能取舍,不是 bug。
六、核心干货四:布尔索引(替代 filter)
NumPy 里 arr > 2 不返回单个布尔值,而是返回一个布尔数组,逐个元素判断:
通常合成一行,这就是 NumPy 版的 filter:
对照 JS:
const arr = [1, 2, 3, 4, 5]
console.log(arr.filter(x => x > 2)) // [3, 4, 5]
还能直接批量赋值,比如"把所有负数清零"(数据清洗常用):
⚠️ 注意:布尔数组之间组合条件不能用
and/or(那是给单个布尔值用的),要用&/|,且每个条件必须加括号:
七、核心干货五:聚合与 axis(按行/按列统计)
求和、平均、最大最小,对标 JS 的 reduce,但一个方法就够:
对照 JS 的 reduce,NumPy 明显更省心:
const arr = [1, 2, 3, 4]
const sum = arr.reduce((a, b) => a + b, 0) // 10
const mean = sum / arr.length // 2.5
二维数组的精髓在 axis 参数——它决定"沿哪个方向压扁"。这是新手最绕的概念,记住:axis=0 是"竖着压"(按列),axis=1 是"横着压"(按行):
记忆口诀:axis 指的是"被消掉的那个维度"。axis=0 把行这一维消掉(2 行 → 没了,剩 3 个列结果),axis=1 把列这一维消掉。
八、形状操作:reshape
reshape 把数据重新排成另一种形状,元素总数必须不变:
reshape 在深度学习里无处不在——比如把一张图片从 (28, 28) 的二维像素拉平成 (784,) 的一维向量喂给模型,靠的就是它。
九、为什么这是 AI 的地基(衔接后续)
到这里你可能会问:学这个跟 AI 有什么关系?关系极大——后面所有 AI 数据都是 NumPy 数组:
- Embedding 向量(第 25 篇)本质就是一个一维 NumPy 数组,比如 OpenAI 的 embedding 是个长度 1536 的
float数组。"语义相似"在数学上就是"两个向量距离近"。
用 CSS 颜色打个直觉比方:RGB 颜色 (255, 0, 0) 就是一个三维向量,(250, 5, 5) 和它数值接近,所以看起来也是相近的红。Embedding 是同一回事,只是把一段文字压成了上千维的坐标点,语义相近的文字,坐标点也挨得近。检索(第 26 篇 RAG)就是"在这堆坐标点里找离我最近的几个"。算"距离/相似度"用的就是本篇的向量运算:
这段代码现在看个意思就行——重点是让你知道:本篇的向量运算,就是后面 Embedding 检索、相似度计算的底层操作。先把"对数组整体运算"这个肌肉记忆练出来,到第 25、26 篇就不会卡。
十、最容易踩的坑(前端视角汇总)
-
切片是视图不是拷贝——改切片会改原数组,要独立副本必须
.copy()。这是和 JSslice最大的认知差,排第一位。 -
a * b是逐位相乘不是矩阵乘法——矩阵乘法用a @ b。别拿线性代数直觉套。 -
数组是同质 + 定形的——不能像 JS 数组那样随意混类型、随意 push 改长度。要"加元素"通常是重新构造或用
np.concatenate,频繁增删请用 Python list。 -
布尔条件组合用
&/|且要加括号——不能用and/or,括号不能省。 -
axis搞反——记住"axis 是被消掉的维度",axis=0按列、axis=1按行。 -
别再写 for 循环逐个处理——能向量化就向量化。看到自己在 NumPy 数组上写
for,先停下想想有没有整体运算的写法(性能差几十倍)。
十一、总结
- 先建立前端锚点:它最大的不同是——你不再写循环,而是把运算直接作用在整个数组上(这叫"向量化")。
- 安装与第一个数组:因为普通 Python list 和 JS 数组一样是"通用容器",不为数学运算优化;
- 核心干货二:dtype 和 shape(和 JS 数组最大的不同):同质:所有元素必须是同一种类型(dtype)。 -> 定形:形状(shape)在创建时确定,是个有维度的网格。
- 核心干货三:索引与切片(最大的坑在这):⚠️ 本篇最大的坑:切片返回的是「视图(view)」而不是「拷贝」。
- 核心干货五:聚合与 axis(按行/按列统计):二维数组的精髓在 axis 参数——它决定"沿哪个方向压扁"。
- 形状操作:reshape:reshape 在深度学习里无处不在——比如把一张图片从 (28, 28) 的二维像素拉平成 (784,) 的一维向量喂给模型,靠的就是它。
学完自测
选择所有正确答案;提交后逐项核对判断依据。