6.12. 选择与重排¶
归约将数组折叠为一个标量或更低阶的结果。选择则涵盖了决定哪些元素得以保留以及它们落在何处的操作:条件选取、裁剪、排序、查找索引、沿某个轴重新排序。
6.12.1. 条件选取¶
where() 返回一个数组:在条件为真处取自 x 的元素,否则取自 y。三个操作数会一起广播:
a = np.array([1, 2, 3, 4, 5], dtype=np.float)
np.where(a < 3, a, 0.0)
# array([1.0, 2.0, 0.0, 0.0, 0.0])
这是无需编写 Python 循环就能实现“逐元素 if/else”的正确工具。
clip() 是 maximum(lo, minimum(a, hi)) 的简写——将值饱和到某个范围内:
np.clip(a, 2.0, 4.0)
# array([2.0, 2.0, 3.0, 4.0, 4.0])
maximum() 和 minimum() 接受两个操作数,并返回逐元素较大 / 较小的值:
np.maximum(a, 3.0)
np.minimum(a, np.array([5, 4, 3, 2, 1]))
6.12.2. 查找索引¶
nonzero() 返回每个非零元素的坐标,并按维度拆分为一个个索引数组。对于二维输入,结果是两个数组组成的元组:第一个保存行索引,第二个保存列索引。将它们按列配对即可得到每个非零位置的 (row, col):
m = np.array([[0, 2, 0],
[3, 0, 0]], dtype=np.float)
np.nonzero(m)
# (array([0, 1], dtype=uint16), array([1, 0], dtype=uint16))
m 中的非零项是 m[0, 1] = 2 和 m[1, 0] = 3。返回的第一个数组 [0, 1] 给出它们的行索引;第二个数组 [1, 0] 给出它们的列索引。把这两个数组并排读取就能还原出位置 (0, 1) 和 (1, 0)。
另有两个归约同样会产生索引:
argsort()—— 一个整数数组,按它能将输入沿给定轴排序(默认为最后一个轴):a = np.array([40, 10, 30, 20], dtype=np.uint8) idx = np.argsort(a) # array([1, 3, 2, 0], dtype=uint16) a[idx] # array([10, 20, 30, 40])
argsort始终返回uint16;因此被排序的数组在排序轴上的元素不能超过 65,535 个。
bincount() 统计一维 uint8 / uint16 输入中每个非负整数出现的次数:
histogram = np.bincount(np.array([0, 1, 1, 2, 2, 2], dtype=np.uint8))
# array([1, 2, 3], dtype=uint16)
对于无需编写 Python 循环即可构建小整数像素值的直方图非常有用。
6.12.3. 排序与重排¶
sort() 返回沿给定轴(默认为最后一个轴)排序后的数组副本。若需原地排序版本,请直接对数组使用 sort():
np.sort(np.array([3, 1, 2], dtype=np.float))
# array([1.0, 2.0, 3.0])
flip() 沿给定轴反转元素顺序(未传入 axis 时反转每个轴):
np.flip(np.array([1, 2, 3, 4]))
# array([4, 3, 2, 1])
roll() 按给定的数量循环移位元素。可用于实现环形缓冲区式的移位寄存器:
np.roll(np.array([1, 2, 3, 4]), 1)
# array([4, 1, 2, 3])
take() 是花式索引的显式形式——在任意索引处选取元素:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.take(a, [0, 2, 4])
# array([10, 30, 50], dtype=uint8)
6.12.4. 过滤与结构性编辑¶
compress() 是布尔索引的显式形式——返回 a 中由布尔条件选出的切片:
a = np.array([10, 20, 30, 40], dtype=np.uint8)
np.compress(a > 15, a)
# array([20, 30, 40], dtype=uint8)
delete() 返回一个删除了给定索引处条目的副本:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.delete(a, [1, 3])
# array([10, 30, 50], dtype=uint8)
diff() 返回数组沿某个轴的第 n 阶离散前向差分。用于计算相邻样本之间的一阶变化:
samples = np.array([1, 3, 6, 10, 15], dtype=np.float)
np.diff(samples)
# array([2.0, 3.0, 4.0, 5.0])
6.12.5. 各操作的开销¶
本页几乎所有函数都会返回一个新分配的数组。有两个例外:
在每秒运行多次的循环中,应优先使用原地的 sort(),并在其他地方都复用预分配的缓冲区。布尔掩码本身在每次比较运行时都会被分配——应只构建一次掩码并在多个操作间复用,而不是在每次迭代内部重新构建它。