在 ndarrays 上的索引#
ndarrays 可以使用标准 Python x[obj] 语法进行索引,其中 x 是数组,obj 是选择. 根据 obj,有不同类型的索引可用:基本索引,高级索引和字段访问.
以下大多数示例都显示了在引用数组中的数据时如何使用索引.这些示例在赋值给数组时同样适用.有关赋值如何工作的具体示例和说明,请参见 为索引数组赋值 .
请注意,在 Python 中, x[(exp1, exp2, ..., expN)] 等价于 x[exp1, exp2, ..., expN] ;后者只是前者的语法糖.
基本索引#
单元素索引#
单元素索引的工作方式与其他标准 Python 序列完全一样.它是基于 0 的,并接受负索引,从数组的末尾开始索引.:
>>> x = np.arange(10)
>>> x[2]
2
>>> x[-2]
8
没有必要将每个维度的索引分隔到各自的方括号中.:
>>> x.shape = (2, 5) # now x is 2-dimensional
>>> x[1, 3]
8
>>> x[1, -1]
9
请注意,如果用少于维度的索引来索引多维数组,就会得到一个子维数组.例如::
>>> x[0]
array([0, 1, 2, 3, 4])
也就是说,每个指定的索引选择与所选其余维度相对应的数组.在上面的示例中,选择 0 意味着长度为 5 的剩余维度未被指定,并且返回的是该维度和大小的数组.必须注意的是,返回的数组是一个 view ,即,它不是原始数组的副本,而是指向与原始数组在内存中相同的值. 在这种情况下,返回第一个位置 (0) 的一维数组.因此,在返回的数组上使用单个索引,将导致返回单个元素.即::
>>> x[0][2]
2
因此请注意, x[0, 2] == x[0][2] ,尽管第二种情况效率较低,因为在第一个索引之后创建了一个新的临时数组,随后通过 2 进行索引.
备注
NumPy 使用 C 顺序索引.这意味着最后一个索引通常代表变化最快的内存位置,这与 Fortran 或 IDL 不同,在 Fortran 或 IDL 中,第一个索引代表内存中变化最快的位置.这种差异代表了很大的混淆可能性.
切片和步长#
基本切片将 Python 的基本切片概念扩展到 N 维.当 obj 是一个 slice 对象(由括号内的 start:stop:step 符号构造),一个整数或一个切片对象和整数的元组时,就会发生基本切片. Ellipsis 和 newaxis 对象也可以穿插其中.
用 N 个整数进行索引的最简单情况返回一个表示对应项的 array scalar . 与 Python 中一样,所有索引都是从零开始的:对于第 i 个索引 \(n_i\) ,有效范围是 \(0 \le n_i < d_i\) ,其中 \(d_i\) 是数组形状的第 i 个元素. 负索引被解释为从数组末尾开始计数(即,如果 \(n_i < 0\) ,则表示 \(n_i + d_i\) ).
由基本切片生成的所有数组始终是原始数组的 views .
备注
NumPy 切片会创建一个 view ,而不是像内置 Python 序列(如字符串,元组和列表)那样创建一个副本.当从一个大型数组中提取一小部分,而在提取后该大型数组变得无用时,必须小心,因为提取出来的小部分包含对大型原始数组的引用,该数组的内存只有在所有派生数组都被垃圾回收后才会释放.在这种情况下,建议显式使用 copy() .
序列切片的标准规则适用于每个维度的基本切片(包括使用步长索引).需要记住的一些有用概念包括:
基本切片语法是
i:j:k,其中 i 是起始索引,j 是停止索引,k 是步长( \(k\neq0\) ).这将选择索引值为 i, i + k, …, i + (m - 1) k 的 m 个元素(在相应的维度中),其中 \(m = q + (r\neq0)\) ,q 和 r 是 j - i 除以 k 得到的商和余数: j - i = q k + r, 因此 i + (m - 1) k < j. 例如:>>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> x[1:7:2] array([1, 3, 5])
负 i 和 j 被解释为 n + i 和 n + j,其中 n 是相应维度中的元素数.负 k 使步进朝向较小的索引.从上面的例子:
>>> x[-2:10] array([8, 9]) >>> x[-3:3:-1] array([7, 6, 5, 4])
假设 n 是被切片的维度中的元素数.那么,如果 i 未给出,则对于 k > 0,它默认为 0,对于 k < 0,它默认为 n - 1.如果 j 未给出,则对于 k > 0,它默认为 n,对于 k < 0,它默认为 -n-1.如果 k 未给出,则默认为 1.请注意,
::与:相同,表示选择此轴上的所有索引.从上面的例子:>>> x[5:] array([5, 6, 7, 8, 9])
如果选择元组中的对象数量小于 N,则假定任何后续维度都为
:.例如:>>> x = np.array([[[1],[2],[3]], [[4],[5],[6]]]) >>> x.shape (2, 3, 1) >>> x[1:2] array([[[4], [5], [6]]])
整数 i 返回与
i:i+1相同的值,只不过返回对象的维度减少了 1.特别是,选择元组的第 p 个元素是整数(所有其他条目都是:)返回相应的子数组,维度为 N - 1.如果 N = 1,则返回的对象是数组标量.这些对象在 标量 中进行了解释.如果选择元组的所有条目都是
:,除了第 p 个条目是切片对象i:j:k,那么返回的数组的维度是 N,它通过沿第 p 个轴堆叠由元素 i,i+k,…,i + (m - 1) k < j 的整数索引返回的子数组而形成.在切片元组中具有多个非-
:条目的基本切片,其行为类似于使用单个非-:条目重复应用切片,其中非-:条目被连续获取(所有其他非-:条目被:替换).因此,x[ind1, ..., ind2,:]的行为类似于基本切片下的x[ind1][..., ind2, :].警告
上述情况对于高级索引是不成立的.
您可以使用切片来设置数组中的值,但是(与列表不同)您永远无法增加数组.在
x[obj] = value中要设置的值的大小必须(可广播到)与x[obj]的形状相同.切片元组始终可以构造为 obj,并在
x[obj]表示法中使用.切片对象可以在构造中代替[start:stop:step]表示法使用.例如,x[1:10:5, ::-1]也可以实现为obj = (slice(1, 10, 5), slice(None, None, -1)); x[obj].这对于构造适用于任意维数组的通用代码非常有用.有关更多信息,请参见 处理程序中可变数量的索引 .
维度索引工具#
有一些工具可以方便地将数组形状与表达式和赋值进行匹配.
Ellipsis 扩展为选择元组索引所有维度所需的 : 对象的数量.在大多数情况下,这意味着扩展后的选择元组的长度为 x.ndim .可能只存在一个省略号.从上面的例子:
>>> x[..., 0]
array([[1, 2, 3],
[4, 5, 6]])
这相当于:
>>> x[:, :, 0]
array([[1, 2, 3],
[4, 5, 6]])
选择元组中的每个 newaxis 对象都用于将结果选择的维度扩展一个单位长度的维度.添加的维度是选择元组中 newaxis 对象的位置. newaxis 是 None 的别名,并且可以使用 None 代替它,结果相同.从上面的例子:
>>> x[:, np.newaxis, :, :].shape
(2, 1, 3, 1)
>>> x[:, None, :, :].shape
(2, 1, 3, 1)
这可以方便地以某种方式组合两个数组,否则需要显式重塑操作.例如:
>>> x = np.arange(5)
>>> x[:, np.newaxis] + x[np.newaxis, :]
array([[0, 1, 2, 3, 4],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8]])
高级索引#
当选择对象 obj 是非元组序列对象, ndarray (整数或布尔数据类型)或至少具有一个序列对象或 ndarray (整数或布尔数据类型)的元组时,将触发高级索引.高级索引有两种类型:整数和布尔.
高级索引始终返回数据的副本(与返回 view 的基本切片形成对比).
警告
高级索引的定义意味着 x[(1, 2, 3),] 本质上不同于 x[(1, 2, 3)] .后者等价于 x[1, 2, 3] ,它将触发基本选择,而前者将触发高级索引.请务必理解为什么会发生这种情况.
整数数组索引#
整数数组索引允许基于 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
当索引包含的整数数组的数量与被索引数组的维度一样多时,索引是直接的,但与切片不同.
高级索引总是 broadcast 并且作为一个整体进行迭代:
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 值的三元组.
布尔数组索引#
当 obj 是布尔类型的数组对象时(例如可以从比较运算符返回的数组对象),就会发生这种高级索引.单个布尔索引数组实际上与 x[obj.nonzero()] 相同,如上所述, obj.nonzero() 返回一个元组(长度为 obj.ndim ),其中包含显示 obj 的 True 元素的整数索引数组.但是,当 obj.shape == x.shape 时它更快.
如果 obj.ndim == x.ndim ,则 x[obj] 返回一个 1 维数组,其中填充了 x 中与 obj 的 True 值相对应的元素.搜索顺序将是 row-major ,C 风格.如果 obj 的形状与 x 的相应维度不匹配,则会引发索引错误,无论这些值是 True 还是 False .
这种方法的一个常见用例是过滤所需的元素值.例如,可能希望从数组中选择所有不是 numpy.nan 的条目:
>>> x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]])
>>> x[~np.isnan(x)]
array([1., 2., 3.])
或者希望向所有负元素添加一个常数:
>>> x = np.array([1., -1., -2., 3])
>>> x[x < 0] += 20
>>> x
array([ 1., 19., 18., 3.])
一般来说,如果索引包含布尔数组,则结果将与将 obj.nonzero() 插入到相同位置并使用上述整数数组索引机制相同. x[ind_1, boolean_array, ind_2] 等价于 x[(ind_1,) + boolean_array.nonzero() + (ind_2,)] .
如果只有一个布尔数组并且没有整数索引数组,这很简单. 只需要注意确保布尔索引具有与其应该使用的维度完全一样多的维度.
通常,当布尔数组的维度小于被索引的数组时,这等效于 x[b, ...] ,这意味着 x 由 b 索引,后跟尽可能多的 : ,以填充 x 的秩. 因此,结果的形状是一个维度,其中包含布尔数组的 True 元素的数量,后跟被索引数组的其余维度:
>>> x = np.arange(35).reshape(5, 7)
>>> b = x > 20
>>> b[:, 5]
array([False, False, False, True, True])
>>> x[b[:, 5]]
array([[21, 22, 23, 24, 25, 26, 27],
[28, 29, 30, 31, 32, 33, 34]])
这里从索引数组中选择第 4 行和第 5 行,并将它们组合以创建一个二维数组.
示例
从一个数组中,选择所有总和小于或等于 2 的行:
>>> x = np.array([[0, 1], [1, 1], [2, 2]])
>>> rowsum = x.sum(-1)
>>> x[rowsum <= 2, :]
array([[0, 1],
[1, 1]])
结合多个布尔索引数组或一个布尔索引数组和一个整数索引数组,最好通过 obj.nonzero() 类比来理解. 函数 ix_ 也支持布尔数组,并且可以顺利运行.
示例
使用布尔索引选择所有加起来为偶数的行. 同时,应使用高级整数索引选择第 0 列和第 2 列. 使用 ix_ 函数,这可以通过以下方式完成:
>>> x = np.array([[ 0, 1, 2],
... [ 3, 4, 5],
... [ 6, 7, 8],
... [ 9, 10, 11]])
>>> rows = (x.sum(-1) % 2) == 0
>>> rows
array([False, True, False, True])
>>> columns = [0, 2]
>>> x[np.ix_(rows, columns)]
array([[ 3, 5],
[ 9, 11]])
如果没有 np.ix_ 调用,只会选择对角线元素.
或者不使用 np.ix_ (比较整型数组的例子):
>>> rows = rows.nonzero()[0]
>>> x[rows[:, np.newaxis], columns]
array([[ 3, 5],
[ 9, 11]])
示例
使用一个形状为 (2, 3),包含 4 个 True 元素的二维布尔数组,来从一个形状为 (2, 3, 5) 的三维数组中选择行,结果是一个形状为 (4, 5) 的二维数组:
>>> x = np.arange(30).reshape(2, 3, 5)
>>> x
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]]])
>>> b = np.array([[True, True, False], [False, True, True]])
>>> x[b]
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]])
高级索引和基本索引的结合#
当索引中至少有一个切片 ( : ),省略号 ( ... ) 或 newaxis (或者数组的维度多于高级索引的数量),那么行为可能更加复杂.它类似于连接每个高级索引元素的索引结果.
在最简单的情况下,只有一个高级索引与一个切片结合.例如:
>>> y = np.arange(35).reshape(5,7)
>>> y[np.array([0, 2, 4]), 1:3]
array([[ 1, 2],
[15, 16],
[29, 30]])
实际上,切片和索引数组操作是独立的.切片操作提取索引为 1 和 2 的列(即第二和第三列),然后是索引数组操作,提取索引为 0,2 和 4 的行(即第一,第三和第五行).这等同于:
>>> y[:, 1:3][np.array([0, 2, 4]), :]
array([[ 1, 2],
[15, 16],
[29, 30]])
单个高级索引可以例如替换一个切片,结果数组将是相同的.但是,它是一个副本,并且可能具有不同的内存布局.如果可能,切片是更可取的.例如:
>>> x = np.array([[ 0, 1, 2],
... [ 3, 4, 5],
... [ 6, 7, 8],
... [ 9, 10, 11]])
>>> x[1:2, 1:3]
array([[4, 5]])
>>> x[1:2, [1, 2]]
array([[4, 5]])
理解多个高级索引组合的最简单方法可能是从结果形状的角度来思考.索引操作分为两个部分:由基本索引(不包括整数)定义的子空间和来自高级索引部分的子空间.需要区分索引组合的两种情况:
高级索引都彼此相邻.例如
x[..., arr1, arr2, :],但不是x[arr1, :, 1],因为在这种情况下1是一个高级索引.
在第一种情况下,由高级索引操作产生的维度在结果数组中排在最前面,子空间维度排在其后.在第二种情况下,来自高级索引操作的维度插入到结果数组中与它们在初始数组中相同的位置(后一种逻辑使得简单的高级索引的行为就像切片一样).
示例
假设 x.shape 是 (10, 20, 30),并且 ind 是一个 (2, 5, 2) 形状的索引 intp 数组,那么 result = x[..., ind, :] 的形状为 (10, 2, 5, 2, 30),因为 (20,) 形状的子空间已被 (2, 5, 2) 形状的广播索引子空间替换.如果我们让 i,j,k 遍历 (2, 5, 2) 形状的子空间,那么 result[..., i, j, k, :] = x[..., ind[i, j, k], :] .此示例产生与 x.take(ind, axis=-2) 相同的结果.
示例
设 x.shape 为 (10, 20, 30, 40, 50),并假设 ind_1 和 ind_2 可以广播到 (2, 3, 4) 的形状.那么 x[:, ind_1, ind_2] 的形状为 (10, 2, 3, 4, 40, 50),因为来自 X 的 (20, 30) 形状的子空间已被来自索引的 (2, 3, 4) 子空间替换.但是, x[:, ind_1, :, ind_2] 的形状为 (2, 3, 4, 10, 30, 50),因为没有明确的位置放置索引子空间,因此它被附加到开头.始终可以使用 .transpose() 将子空间移动到任何想要的位置.请注意,此示例无法使用 take 复制.
示例
切片可以与广播的布尔索引结合使用:
>>> x = np.arange(35).reshape(5, 7)
>>> b = x > 20
>>> b
array([[False, False, False, False, False, False, False],
[False, False, False, False, False, False, False],
[False, False, False, False, False, False, False],
[ True, True, True, True, True, True, True],
[ True, True, True, True, True, True, True]])
>>> x[b[:, 5], 1:3]
array([[22, 23],
[29, 30]])
字段访问#
参见
如果 ndarray 对象是一个结构化数组,则可以通过使用字符串(类似于字典的方式)索引数组来访问数组的 fields .
索引 x['field-name'] 返回一个指向数组的新 view ,该视图与 x 具有相同的形状(除非字段是子数组),但数据类型为 x.dtype['field-name'] ,并且仅包含指定字段中的数据部分.此外, record array 标量可以通过这种方式进行“索引”.
使用字段名列表也可以对结构化数组进行索引,例如 x[['field-name1', 'field-name2']] .从 NumPy 1.16 开始,这将返回一个仅包含这些字段的视图.在较早版本的 NumPy 中,它返回一个副本.有关多字段索引的更多信息,请参见用户指南中关于 结构化数组 的章节.
如果访问的字段是子数组,则子数组的维度将附加到结果的形状.例如:
>>> x = np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))])
>>> x['a'].shape
(2, 2)
>>> x['a'].dtype
dtype('int32')
>>> x['b'].shape
(2, 2, 3, 3)
>>> x['b'].dtype
dtype('float64')
扁平迭代器索引#
x.flat 返回一个迭代器,该迭代器将迭代整个数组(以 C 连续样式,最后一个索引变化最快).只要选择对象不是元组,就可以使用基本切片或高级索引来索引此迭代器对象.从 x.flat 是一个 1 维视图这一事实可以清楚地看出这一点.它可以用于带有 1 维 C 风格扁平索引的整数索引.因此,任何返回数组的形状都是整数索引对象的形状.
为索引数组赋值#
如前所述,可以使用单个索引,切片以及索引和掩码数组来选择数组的子集以进行赋值.分配给索引数组的值必须在形状上一致(与索引产生的形状相同或可广播到该形状).例如,允许将常量分配给切片:
>>> x = np.arange(10)
>>> x[2:7] = 1
或正确大小的数组:
>>> x[2:7] = np.arange(5)
请注意,如果将较高类型分配给较低类型(如将浮点数分配给整数),则赋值可能会导致更改,甚至会引发异常(将复数分配给浮点数或整数):
>>> x[1] = 1.2
>>> x[1]
1
>>> x[1] = 1.2j
Traceback (most recent call last):
...
TypeError: can't convert complex to int
与某些引用(例如数组和掩码索引)不同,赋值总是对数组中的原始数据进行,(实际上,其他任何操作都没有意义!).但请注意,某些操作可能无法按人们最初的预期工作.这个特殊的例子经常让人感到惊讶:
>>> x = np.arange(0, 50, 10)
>>> x
array([ 0, 10, 20, 30, 40])
>>> x[np.array([1, 1, 3, 1])] += 1
>>> x
array([ 0, 11, 20, 31, 40])
人们期望第一个位置会增加 3.实际上,它只会增加 1.原因是,从原始数组中提取一个新数组(作为临时数组),该数组包含 1,1,3,1 处的值,然后将值 1 添加到临时数组,然后将临时数组分配回原始数组.因此,数组在 x[1] + 1 处的值会三次分配给 x[1] ,而不是增加 3 次.
处理程序中可变数量的索引#
索引语法非常强大,但在处理可变数量的索引时会受到限制.例如,如果您想编写一个可以处理具有各种维度数量的参数的函数,而无需为每个可能的维度数量编写特殊情况代码,该怎么做?如果向索引提供元组,则该元组将被解释为索引列表.例如:
>>> z = np.arange(81).reshape(3, 3, 3, 3)
>>> indices = (1, 1, 1, 1)
>>> z[indices]
40
因此,你可以使用代码来构造任意数量的索引的元组,然后在索引中使用它们.
可以通过在 Python 中使用 slice() 函数在程序中指定切片.例如:
>>> indices = (1, 1, 1, slice(0, 2)) # same as [1, 1, 1, 0:2]
>>> z[indices]
array([39, 40])
同样,可以通过使用 Ellipsis 对象通过代码指定省略号:
>>> indices = (1, Ellipsis, 1) # same as [1, ..., 1]
>>> z[indices]
array([[28, 31, 34],
[37, 40, 43],
[46, 49, 52]])
因此,可以直接使用 np.nonzero() 函数的输出作为索引,因为它总是返回一个索引数组的元组.
由于元组的特殊处理,它们不会像列表一样自动转换为数组.例如:
>>> z[[1, 1, 1, 1]] # produces a large array
array([[[[27, 28, 29],
[30, 31, 32], ...
>>> z[(1, 1, 1, 1)] # returns a single value
40
详细说明#
这些是一些详细的说明,对于日常索引来说并不重要(没有特别的顺序):
NumPy 的原生索引类型是
intp,可能与默认的整数数组类型不同.intp是安全索引任何数组所需的最小数据类型;对于高级索引,它可能比其他类型更快.对于高级索引赋值,通常不保证迭代顺序.这意味着如果一个元素被设置多次,则无法预测最终结果.
一个空的(元组)索引是对零维数组的完整标量索引.如果
x是零维的,则x[()]返回一个标量,否则返回一个视图.另一方面,x[...]始终返回一个视图.如果索引中存在一个零维数组,并且它是一个完整的整数索引,则结果将是一个标量,而不是一个零维数组.(不会触发高级索引.)
当省略号 (
...) 存在但没有大小(即替换零个:)时,结果仍然始终是一个数组.如果没有高级索引,则为视图,否则为副本.布尔数组的
nonzero等价性不适用于零维布尔数组.当高级索引操作的结果没有元素,但某个单独的索引超出范围时,是否引发
IndexError是未定义的(例如,x[[], [123]],其中123超出范围).当赋值期间发生强制转换错误时(例如,使用字符串序列更新数值数组),被赋值的数组最终可能会处于不可预测的部分更新状态.但是,如果发生任何其他错误(例如超出范围的索引),则数组将保持不变.
高级索引结果的内存布局针对每个索引操作进行了优化,并且不能假定任何特定的内存顺序.
当使用子类(特别是操纵其形状的子类)时,默认的
ndarray.__setitem__行为将为基本索引调用__getitem__,但不为高级索引调用.对于这样的子类,最好使用基类ndarray视图在数据上调用ndarray.__setitem__.如果子类的__getitem__不返回视图,则必须这样做.