数组运算
学习目标
- 理解向量化运算的概念和优势
- 掌握元素级运算和通用函数(ufunc)
- 理解广播机制(Broadcasting)的规则
- 熟练使用聚合函数进行统计计算
向量化运算:告别循环
向量化运算是 NumPy 的核心思想——对整个数组进行操作,不用写循环。
纯 Python vs NumPy
import numpy as np
# 纯 Python:逐个计算
prices = [100, 200, 300, 400, 500]
discounted = []
for p in prices:
discounted.append(p * 0.8)
print(discounted) # [80.0, 160.0, 240.0, 320.0, 400.0]
# NumPy:一行搞定
prices = np.array([100, 200, 300, 400, 500])
discounted = prices * 0.8
print(discounted) # [ 80. 160. 240. 320. 400.]
元素级运算
NumPy 数组的算术运算是逐元素进行的:
a = np.array([1, 2, 3, 4])
b = np.array([10, 20, 30, 40])
print(a + b) # [11 22 33 44] 对应位置相加
print(a - b) # [ -9 -18 -27 -36]
print(a * b) # [ 10 40 90 160] 对应位置相乘(不是矩阵乘法!)
print(a / b) # [0.1 0.1 0.1 0.1]
print(a ** 2) # [ 1 4 9 16] 平方
print(b % 3) # [1 2 0 1] 取余
print(b // 3) # [ 3 6 10 13] 整除
与标量运算
数组和单个数字(标量)运算时,会自动把标量应用到每个元素:
arr = np.array([10, 20, 30, 40])
print(arr + 5) # [15 25 35 45]
print(arr * 2) # [20 40 60 80]
print(arr / 10) # [1. 2. 3. 4.]
print(1 / arr) # [0.1 0.05 0.033 0.025]
比较运算
arr = np.array([15, 23, 8, 42, 31])
print(arr > 20) # [False True False True True]
print(arr == 23) # [False True False False False]
print(arr != 8) # [ True True False True True]
通用函数(ufunc)
NumPy 提供了大量的通用函数,可以对数组中每个元素应用数学运算:
常用数学函数
arr = np.array([1, 4, 9, 16, 25])
# 平方根
print(np.sqrt(arr)) # [1. 2. 3. 4. 5.]
# 绝对值
neg = np.array([-3, -1, 0, 2, 5])
print(np.abs(neg)) # [3 1 0 2 5]
# 幂运算
print(np.power(arr, 0.5)) # 和 sqrt 一样
# 指数和对数
print(np.exp([0, 1, 2])) # [1. 2.718 7.389] e 的幂
print(np.log([1, np.e, 10])) # [0. 1. 2.303] 自然对数
print(np.log10([1, 10, 100])) # [0. 1. 2.] 以 10 为底
print(np.log2([1, 2, 8, 64])) # [0. 1. 3. 6.] 以 2 为底
三角函数
# 创建 0 到 2π 的角度
angles = np.linspace(0, 2 * np.pi, 5) # [0, π/2, π, 3π/2, 2π]
print(np.sin(angles)) # [ 0. 1. 0. -1. 0.] ← 正弦
print(np.cos(angles)) # [ 1. 0. -1. 0. 1.] ← 余弦
取整函数
arr = np.array([1.2, 2.5, 3.7, -1.3, -2.8])
print(np.floor(arr)) # [ 1. 2. 3. -2. -3.] 向下取整
print(np.ceil(arr)) # [ 2. 3. 4. -1. -2.] 向上取整
print(np.round(arr)) # [ 1. 2. 4. -1. -3.] 四舍五入
print(np.trunc(arr)) # [ 1. 2. 3. -1. -2.] 截断小数
两个数组间的运算
a = np.array([3, 5, 7, 9])
b = np.array([1, 4, 2, 8])
print(np.maximum(a, b)) # [3 5 7 9] 对应位置取较大值
print(np.minimum(a, b)) # [1 4 2 8] 对应位置取较小值
print(np.where(a > b, a, b)) # 同 maximum,但更灵活
广播机制(Broadcasting)
当两个形状不同的数组运算时,NumPy 会自动"广播"较小的数组,使它们形状兼容。
最简单的例子
arr = np.array([1, 2, 3])
# 标量 + 数组 → 标量被广播成 [10, 10, 10]
print(arr + 10) # [11 12 13]
这其实就是广播——NumPy 把 10 扩展成了 [10, 10, 10],然后逐元素相加。
二维数组 + 一维数组
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
row = np.array([10, 20, 30])
# row 被广播到每一行
result = matrix + row
print(result)
# [[11 22 33]
# [14 25 36]
# [17 28 39]]
广播的过程可以这样理解:
matrix: row (广播前): row (广播后):
[[1, 2, 3], [10, 20, 30] → [[10, 20, 30],
[4, 5, 6], [10, 20, 30],
[7, 8, 9]] [10, 20, 30]]
列向量 + 行向量
col = np.array([[1], [2], [3]]) # shape: (3, 1) 列向量
row = np.array([10, 20, 30]) # shape: (3,) 行向量
# 两者都被广播
result = col + row
print(result)
# [[11 21 31]
# [12 22 32]
# [13 23 33]]