本系列是《NumPy 速通》的配套章节。
NumPy 最大的魅力:数组可以直接做数学运算,而且是逐元素的(element-wise),不需要写循环。本章讲透四类运算:算术、标量、比较、* vs @,以及"就地运算"这个高效技巧。
逐元素算术运算
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
a + b # [5 7 9] 加法(对应位置相加)
a - b # [-3 -3 -3] 减法
a * b # [4 10 18] 乘法
a / b # [0.25 0.4 0.5 ] 除法(结果是浮点)
a // b # [0 0 0] 整除
a % b # [1 2 3] 取余
a ** 2 # [1 4 9] 乘方
-a # [-1 -2 -3] 取负
两个数组形状相同 → 对应位置逐个算,这就是"逐元素"(element-wise)。不需要写 for i in range(len(a))——这正是 NumPy 快的原因。
运算符 ↔ ufunc 对应表
| 运算符 | 对应 ufunc |
|---|---|
+ |
np.add |
- |
np.subtract |
* |
np.multiply |
/ |
np.divide |
** |
np.power |
> < == 等 |
np.greater 等 |
运算符是 ufunc 的"语法糖",二者等价。底层都是 C 实现的 ufunc(第 6 章详述)。
标量运算(广播的雏形)
一个数字和数组运算,会"广播"到每个元素(完整机制见第 8 章):
a + 10 # [11 12 13] 每个元素 +10
a * 2 # [2 4 6]
a / 0 # 警告 + [inf inf inf](除零得 inf,不抛异常!)
⚠️ 灵魂警告:NumPy 除以 0 不报错,而是得到
inf(无穷)并给个 RuntimeWarning。这在 Python 里是要抛ZeroDivisionError的。所以算完除法要检查是否有 inf / nan:result = a / b np.isinf(result).any() # 是否有无穷 np.isnan(result).any() # 是否有 NaN
比较运算:返回布尔数组
比较运算符逐元素返回 True / False,是筛选(第 9 章)的地基:
a > 2 # [False False True]
a == 2 # [False True False]
a != 2 # [ True False True]
别用 == 比较浮点数组!
np.array([0.1 + 0.2]) == 0.3 # [False] ⚠️ 浮点误差
np.allclose([0.1 + 0.2], [0.3]) # True 用 allclose!
浮点数的二进制表示有误差,比较浮点数组用 np.allclose(容忍微小误差),别用 ==。
⚠️ 两个"乘法":* vs @(新手最常踩的坑)
*:逐元素乘法(对应位置相乘,数组必须同形状)@:矩阵乘法(行 × 列,线性代数里的点积)
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
A * B # [[ 5 12] [21 32]] 逐元素:A[i,j]*B[i,j]
A @ B # [[19 22] [43 50]] 矩阵:行×列求和
np.dot(A, B) # 和 @ 等价
用错符号结果完全不同。想算线性代数的"矩阵乘法"(比如神经网络的前向传播 W @ x),必须用 @。
💡 注意维度要求:
@要求"内维相等",(m, n) @ (n, k) → (m, k)。(3,2) @ (3,2)会报错(2≠3),而*要求完全同形状。
就地运算:+= / *= 等(高效技巧)
a = np.array([1, 2, 3])
a += 10 # 就地加,不产生新数组(a 变成 [11 12 13])
a *= 2 # [22 24 26]
a[0] = 99 # 修改元素
a += 10 和 a = a + 10 的区别:
a += 10:原地修改 a,不分配新内存 → 大数据时快a = a + 10:新建数组再重新绑定 → 多占一份内存
⚠️ 但注意:
a += 10是原地修改。如果b = a,然后a += 10,b也跟着变(共享内存)。要避免就用a = a + 10或a = a + 10(重新绑定)。
聚合运算(预览)
对整组数算总账(完整版见第 7 章):
np.sum(a) # 总和
np.mean(a) # 平均
np.max(a) # 最大
np.min(a) # 最小
np.prod(a) # 连乘
小结
- 算术、比较都是逐元素的,不用写循环
*是逐元素,@是矩阵乘法——别混- 除零得
inf不报错,算完检查 inf/nan - 浮点比较用
np.allclose - 大数据循环运算用就地
+=/*=省内存
下一章:调整数组的形状。
上一篇:第 3 章 索引与切片 | 下一篇:第 5 章 形状操作