(八)逐元素运算与条件更新
1. 数组条件按元素组合
筛选 20 至 39 的数,分别比较以后用 & 组合;| 表示逐项满足任意一项,~ 反转布尔数组。第二章已经比较过这些符号与 and、or 的作用:这里操作的是 NumPy 数组,结果也逐项保存在数组中。
按第二章的优先级,& 会先于大小比较组合。因此 a >= 20 & a < 40 中的 20 & a 会先组合,形成另一种表达式。写成 (a >= 20) & (a < 40),才是先得到两份布尔数组,再把对应位置合并。
# 项目文件:demos/ufunc_mask.py
# 逐元素组合条件;预先准备输出,再只在分母非零的位置执行除法。
import numpy as np
a = np.array([10, 20, 30, 40])
mask = (a >= 20) & (a < 40)
print("条件", mask.tolist(), "选择", a[mask].tolist())
print("平方根", np.sqrt(np.array([1, 4, 9])).tolist())
print("截取数值", np.clip(a, 15, 35).tolist())
numerator, denominator = np.array([4.0, 9.0]), np.array([2.0, 0.0])
answer = np.full(2, np.nan)
np.divide(numerator, denominator, out=answer, where=denominator != 0)
print("有效除法", answer.tolist())
counts = np.zeros(3, dtype=np.int64)
counts[[0, 0]] += 1
print("普通重复索引", counts.tolist())
counts[:] = 0
np.add.at(counts, [0, 0], 1)
print("逐次累计", counts.tolist())条件为 [False,True,True,False],选出 [20,30]。Python 的 and、or 要判断整个对象的真假,不能代替这里的逐元素逻辑;需要判断是否至少有一项、是否全部满足时,分别使用 .any()、.all()。
sqrt、clip、add 等函数把操作应用到对应元素。这样的通用函数称为 ufunc;clip 把数值限制在给出的两端之间,例子得到 [15,20,30,35]。
2. 只在需要的位置执行运算
两个分母为 [2,0],希望第二项保留缺失标记。先用 full 建立 [nan,nan],divide 的 out 指定写入结果的位置,where 指定哪些位置真的执行除法,最后得到 [2,nan]。
np.where(条件,左结果,右结果) 用于从两个已经求出的结果中选择;把除零表达式放进参数,它仍然会先被计算。需要跳过无效除法时,使用上面的 ufunc where。
重复索引也要区分动作:counts[[0,0]]+=1 先取得两个旧值,再写回,位置 0 最终为 1;np.add.at(counts,[0,0],1) 按两次索引分别累计,位置 0 最终为 2。