第 4 章 数组运算与通用函数
学习目标
- 掌握逐元素算术运算与就地运算
- 掌握比较运算,理解比较产生布尔数组
- 认识通用函数(ufunc):
sqrt、abs、exp、log、round、三角函数 - 会用数组方法做简单聚合:
sum、mean、max、min
4.1 逐元素算术运算
逐元素运算(element-wise)指两个数组对应位置上的元素各自运算,结果仍是数组。形状相同的数组之间、数组与标量之间都可以:
python
import numpy as np
a = np.array([10, 20, 30])
b = np.array([1, 2, 3])
print(a + b) # 加法
print(a - b) # 减法
print(a * b) # 乘法
print(a / b) # 除法(结果浮点)
print(a // b) # 整除
print(a % b) # 取余
print(a ** 2) # 幂输出:
[11 22 33]
[ 9 18 27]
[10 40 90]
[10. 10. 10.]
[10 10 10]
[0 0 0]
[100 400 900]与 Python 列表对比,再次强调:数组的 *、+ 是逐元素运算;列表的 +、* 是拼接和重复。 这是数组最核心的语义。
数组与标量运算时,标量被「广播」到每个元素(广播规则第 7 章正式讲):
python
import numpy as np
prices = np.array([3.5, 4.0, 5.5])
print(prices * 2) # 单价翻倍
print(prices + 1) # 每件加 1 元输出:
[ 7. 8. 11. ]
[4.5 5. 6.5]4.2 就地运算
+=、*= 等就地运算直接修改原数组,不创建新数组:
python
import numpy as np
c = np.array([1, 2, 3])
c += 10
print(c)输出:
[11 12 13]注意区分:c + 10 返回新数组、c 不变;c += 10 修改 c 本身。需要保留原数据时用前者。
4.3 比较运算:结果是布尔数组
比较运算符(> >= < <= == !=)逐元素比较,结果数组的元素是 True/False:
python
import numpy as np
a = np.array([10, 20, 30])
print(a > 15)
print(a == 20)输出:
[False True True]
[False True False]布尔数组可以直接传给 sum/mean 统计满足条件的个数与比例(第 8 章展开):
python
import numpy as np
scores = np.array([85, 92, 78, 90, 88])
print((scores >= 90).sum()) # 90 分以上的人数
print((scores >= 90).mean()) # 90 分以上占的比例输出:
2
0.44.4 通用函数(ufunc)
通用函数(ufunc)是 NumPy 提供的逐元素数学函数,用法和算术运算符一样,作用于整个数组。
python
import numpy as np
print(np.sqrt(np.array([1, 4, 9]))) # 平方根
print(np.abs(np.array([-1, 2, -3]))) # 绝对值
print(np.round(np.array([1.234, 5.678]), 1)) # 四舍五入,保留 1 位小数
print(np.exp(np.array([0, 1]))) # e 的幂
print(np.log(np.array([1, np.e]))) # 自然对数
print(np.sin(np.pi / 2)) # 正弦输出:
[1. 2. 3.]
[1 2 3]
[1.2 5.7]
[1. 2.71828183]
[0. 1.]
1.0几点说明:
round的第二个参数是保留的小数位数;也可以调用数组方法数组.round(1),效果相同。- 对负数开方会产生
nan(Not a Number,非数)并伴随RuntimeWarning,数据中出现nan说明有非法输入:
>>> np.sqrt(np.array([-1.0, 4.0]))
<stdin>:1: RuntimeWarning: invalid value encountered in sqrt
array([nan, 2.])- 三角函数等以弧度为单位:
np.pi是圆周率。
ufunc 的特征:输入数组、输出数组,逐元素计算,内部用编译代码批量执行。需要什么数学运算,先去 np 命名空间找,大概率已有现成函数。
4.5 数组方法:简单聚合
聚合把整个数组压缩成一个数。第 1 章见过,这里集中列出:
python
import numpy as np
a = np.array([10, 20, 30])
print(a.sum()) # 总和
print(a.mean()) # 平均值
print(a.max()) # 最大值
print(a.min()) # 最小值输出:
60
20.0
30
10mean 返回浮点,sum/max/min 保持原 dtype。按行、按列聚合(axis)在第 6 章讲。
动手实践
- 创建两个数组
np.array([1, 3, 5, 7])与np.array([2, 4, 6, 8]),计算和、差、积、商,观察除法结果的 dtype。 - 摄氏温度转华氏:
f = c * 9 / 5 + 32,对c = np.array([0, 10, 20, 30, 40])计算并打印。 - 对
np.array([-3.7, 0, 2.4, -1.2])分别取绝对值、四舍五入到整数,打印结果。 - 统计
np.array([72, 85, 90, 65, 88, 95])中及格(≥60)与优秀(≥90)的人数与比例。
常见错误
| 错误写法 | 现象 | 原因 |
|---|---|---|
a + b 时形状不同 | ValueError: operands could not be broadcast... | 形状不兼容,不能逐元素运算(广播规则见第 7 章) |
a > 15 后直接用 if 判断 | ValueError: truth value of an array... is ambiguous | 布尔数组有多个真值,不能整体作条件;用 .all()/.any() 或布尔索引 |
np.sqrt 负数数组 | 输出 nan + RuntimeWarning | 负数没有实数平方根,结果是 nan |
误以为 c + 10 修改了 c | 原数组没变 | + 返回新数组;就地修改要用 += |
用 a * b 计算矩阵乘法 | 结果与数学课本不符 | * 是逐元素乘法;矩阵乘法用 @(第 10 章) |
在列表上调用 np.sqrt(列表) | 返回元素是 float 的数组(可以) | 可以,但仍是逐元素转换;列表本身没有向量化运算 |
章末练习
基础
- 对
a = np.array([2, 4, 6, 8])计算a ** 2、a + 5、a / 2,写出结果。 - 对数组
[12, 7, 9, 15, 3]分别计算总和、平均、最大、最小。 - 用
np.sqrt计算[1, 4, 9, 16, 25]的平方根。
提高
- 已知某商品原价数组
[99, 199, 299],打八折再满 100 减 20,写代码计算最终价格数组(注意:折扣与满减都要逐元素处理)。 - 用比较运算统计
np.array([58, 72, 45, 90, 66])中不及格(<60)的个数,并打印不及格比例。
挑战
- 用
np.round把np.array([2.675, 1.225])保留两位小数,运行并观察结果,思考为什么2.675变成了2.67(提示:浮点数在二进制中的表示不精确)。 - 写一个 ufunc 链:对
np.array([1, 4, 9, 16])先开方、再乘 2、再四舍五入到 1 位小数,输出最终数组。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
np.array([1, 2, 3]) * 2的结果是?- A.
[1, 2, 3, 1, 2, 3] - B.
[2, 4, 6] - C.
2 - D. 报错
- A.
np.array([7, 8]) / 2的结果 dtype 是?- A.
int64 - B.
float64 - C.
bool - D. 报错
- A.
np.array([10, 20, 30]) > 15的结果是?- A.
[10, 20, 30] - B.
[False, True, True] - C.
True - D.
[0, 1, 1]
- A.
np.sqrt(np.array([1, 4, 9]))的结果是?- A.
[1, 2, 3] - B.
[1.0, 2.0, 3.0] - C.
[1, 16, 81] - D.
[0, 1, 2]
- A.
np.round(np.array([1.234, 5.678]), 1)的结果是?- A.
[1.2, 5.7] - B.
[1.2, 5.6] - C.
[1.23, 5.68] - D.
[1, 6]
- A.
- 下列哪个是就地修改数组的写法?
- A.
a = a + 10 - B.
a += 10 - C.
np.add(a, 10) - D. B 和 A 都会修改原数组
- A.
np.array([85, 92, 78]) >= 90的sum()结果是?- A.
85 - B.
2 - C.
1 - D.
False
- A.
- 对负数数组开方会出现?
- A. 直接报错
- B. 输出
nan并伴随RuntimeWarning - C. 自动取绝对值
- D. 返回原数组
a.sum()与a.mean()的区别是?- A. 没有区别
- B. sum 求和,mean 求平均
- C. sum 求平均,mean 求和
- D. 都返回数组
- 数组
*与列表*的区别是?- A. 数组是逐元素乘法,列表是重复
- B. 列表是逐元素乘法,数组是重复
- C. 两者相同
- D. 数组不能使用
*
