整数数组索引#
整数数组索引允许根据其 N 维索引选择数组中的任意项。每个整数数组代表该维度中的多个索引。
索引数组中允许负值,其工作方式与单个索引或切片相同
>>> x = np.arange(10, 1, -1)
>>> x
array([10, 9, 8, 7, 6, 5, 4, 3, 2])
>>> x[np.array([3, 3, 1, 8])]
array([7, 7, 9, 2])
>>> x[np.array([3, 3, -3, 8])]
array([7, 7, 4, 2])
如果索引值超出范围,则会抛出 IndexError
>>> x = np.array([[1, 2], [3, 4], [5, 6]])
>>> x[np.array([1, -1])]
array([[3, 4],
[5, 6]])
>>> x[np.array([3, 4])]
Traceback (most recent call last):
...
IndexError: index 3 is out of bounds for axis 0 with size 3
当索引由与被索引数组的维度数量相同的整数数组组成时,索引是直接的,但与切片不同。
高级索引总是被广播并作为一个进行迭代
result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M],
..., ind_N[i_1, ..., i_M]]
请注意,结果形状与(广播后的)索引数组形状 ind_1, ..., ind_N 相同。如果索引无法广播到相同形状,则会引发异常 IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes...。
使用多维索引数组进行索引通常是较不寻常的用法,但它们是允许的,并且对于某些问题很有用。我们将从最简单的多维情况开始
>>> y = np.arange(35).reshape(5, 7)
>>> y
array([[ 0, 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12, 13],
[14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27],
[28, 29, 30, 31, 32, 33, 34]])
>>> y[np.array([0, 2, 4]), np.array([0, 1, 2])]
array([ 0, 15, 30])
在这种情况下,如果索引数组具有匹配的形状,并且被索引数组的每个维度都有一个索引数组,则结果数组的形状与索引数组相同,并且值对应于索引数组中每个位置的索引集。在此示例中,两个索引数组的第一个索引值都为 0,因此结果数组的第一个值为 y[0, 0]。下一个值为 y[2, 1],最后一个为 y[4, 2]。
如果索引数组的形状不同,会尝试将它们广播到相同形状。如果它们无法广播到相同形状,则会引发异常
>>> y[np.array([0, 2, 4]), np.array([0, 1])]
Traceback (most recent call last):
...
IndexError: shape mismatch: indexing arrays could not be broadcast
together with shapes (3,) (2,)
广播机制允许将索引数组与其他索引的标量结合。其效果是标量值用于索引数组的所有相应值
>>> y[np.array([0, 2, 4]), 1]
array([ 1, 15, 29])
跳到下一个复杂级别,可以仅使用索引数组部分索引数组。理解在这种情况下会发生什么需要一些思考。例如,如果我们只使用一个索引数组与 y
>>> y[np.array([0, 2, 4])]
array([[ 0, 1, 2, 3, 4, 5, 6],
[14, 15, 16, 17, 18, 19, 20],
[28, 29, 30, 31, 32, 33, 34]])
这导致构造了一个新数组,其中索引数组的每个值从被索引数组中选择一行,结果数组的形状为(索引元素数量,行大小)。
通常,结果数组的形状将是索引数组的形状(或所有索引数组广播到的形状)与被索引数组中任何未使用维度(未被索引的维度)的形状的串联。
示例
从每一行中,应选择一个特定元素。行索引只是 [0, 1, 2],列索引指定了要为相应行选择的元素,这里是 [0, 1, 0]。将两者结合起来,可以使用高级索引解决此任务
>>> x = np.array([[1, 2], [3, 4], [5, 6]])
>>> x[[0, 1, 2], [0, 1, 0]]
array([1, 4, 5])
为了实现类似于上述基本切片的行为,可以使用广播。函数 ix_ 可以帮助实现此广播。这最好通过一个示例来理解。
示例
从一个 4x3 数组中,应使用高级索引选择角元素。因此,需要选择列为 [0, 2] 之一且行为 [0, 3] 之一的所有元素。要使用高级索引,需要显式地选择所有元素。使用之前解释的方法,可以写出
>>> x = np.array([[ 0, 1, 2],
... [ 3, 4, 5],
... [ 6, 7, 8],
... [ 9, 10, 11]])
>>> rows = np.array([[0, 0],
... [3, 3]], dtype=np.intp)
>>> columns = np.array([[0, 2],
... [0, 2]], dtype=np.intp)
>>> x[rows, columns]
array([[ 0, 2],
[ 9, 11]])
然而,由于上面的索引数组只是重复自身,因此可以使用广播(比较诸如 rows[:, np.newaxis] + columns 之类的操作)来简化这一点
>>> rows = np.array([0, 3], dtype=np.intp)
>>> columns = np.array([0, 2], dtype=np.intp)
>>> rows[:, np.newaxis]
array([[0],
[3]])
>>> x[rows[:, np.newaxis], columns]
array([[ 0, 2],
[ 9, 11]])
这种广播也可以使用函数 ix_ 来实现
>>> x[np.ix_(rows, columns)]
array([[ 0, 2],
[ 9, 11]])
请注意,如果没有调用 np.ix_,将只选择对角线元素
>>> x[rows, columns]
array([ 0, 11])
这种差异是关于使用多个高级索引进行索引时最重要的一点。
示例
高级索引可能有用的一种实际示例是颜色查找表,我们希望将图像的值映射到 RGB 三元组以进行显示。查找表可以具有形状 (nlookup, 3)。使用形状为 (ny, nx) 且 dtype=np.uint8(或任何整数类型,只要值在查找表的范围内)的图像索引此类数组,将生成形状为 (ny, nx, 3) 的数组,其中 RGB 三元组与每个像素位置相关联。