参见
Indexing
Indexing routines
数组索引是指使用方括号([])对数组值进行索引。索引有很多选择,这给numpy索引带来了巨大的力量,但是随着力量的增加,会带来一些复杂性和潜在的混乱。本节只是与索引相关的各种选项和问题的概述。除了单元素索引之外,大多数选项的详细信息都可以在相关章节中找到。
下面的大多数示例显示了在引用数组中的数据时使用索引。这些示例在分配给数组时也同样有效。有关作业如何工作的具体示例和解释,请参见结尾部分。
一维数组的单元素索引是我们所期望的。它的工作方式与其他标准的Python序列完全相同。它是基于0的,并且接受从数组末尾开始索引的负索引。::
>>> x = np.arange(10) >>> x[2] 2 >>> x[-2] 8
与列表和元组不同,numpy数组支持多维数组的多维索引。这意味着不需要将每个维度的索引分隔成它自己的方括号集。::
>>> 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的剩余维度未指定,返回的是该维度和大小的数组。必须注意,返回的数组不是原始数组的副本,而是指向内存中与原始数组相同的值。在这种情况下,返回第一个位置(0)的一维数组。因此,对返回的数组使用单个索引,会导致返回单个元素。那是::
>>> x[0][2] 2
所以请注意 x[0,2] = x[0][2] 尽管第二种情况效率更低,因为在第一个索引之后创建了一个新的临时数组,随后该索引被2索引。
x[0,2] = x[0][2]
请注意那些用于IDL或Fortran内存顺序的内存,因为它与索引相关。numpy使用C顺序索引。这意味着最后一个索引通常表示变化最快的内存位置,与FORTRAN或IDL不同,前者表示内存中变化最快的位置。这种差异代表了一种巨大的混淆可能性。
可以对数组进行切片和跨步,以提取具有相同维数但与原始数组大小不同的数组。切片和删除的工作方式与列表和元组完全相同,只是它们也可以应用于多个维度。几个例子最能说明::
>>> x = np.arange(10) >>> x[2:5] array([2, 3, 4]) >>> x[:-7] array([0, 1, 2]) >>> x[1:7:2] array([1, 3, 5]) >>> y = np.arange(35).reshape(5,7) >>> y[1:5:2,::3] array([[ 7, 10, 13], [21, 24, 27]])
请注意,数组切片不会复制内部数组数据,而只生成原始数据的新视图。这与列表或元组切片和显式 copy() 如果不再需要原始数据,建议使用。
copy()
为了将数组中的值列表选择到新数组中,可以将数组与其他数组建立索引。实现这一目标有两种不同的方法。一个使用一个或多个索引值数组。另一种方法是给出一个适当形状的布尔数组,以指示要选择的值。索引数组是一个非常强大的工具,它可以避免在数组中的单个元素上循环,从而大大提高性能。
通过索引,可以使用特殊的特征来有效地增加数组中的维数,从而得到表达式或特定函数所需的形状。
numpy数组可以与其他数组(或任何其他可以转换为数组的类似序列的对象,如列表,但元组除外;有关这一点的原因,请参见本文档末尾)。索引数组的使用范围从简单、简单的情况到复杂、难以理解的情况。对于索引数组的所有情况,返回的是原始数据的副本,而不是切片的视图。
索引数组必须是整数类型。数组中的每个值指示数组中要使用哪个值来代替索引。举例说明:
>>> 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])
由值3、3、1和8组成的索引数组相应地创建一个长度为4的数组(与索引数组相同),其中每个索引都被索引数组中的值替换。
允许负值,并像处理单个索引或切片那样工作:::
>>> x[np.array([3,3,-3,8])] array([7, 7, 4, 2])
索引值越界是错误的:::
>>> x[np.array([3, 3, 20, 8])] <type 'exceptions.IndexError'>: index 20 out of bounds 0<=index<9
一般来说,使用索引数组时返回的是一个形状与索引数组相同的数组,但数组的类型和值是被索引的。例如,我们可以使用多维索引数组来代替:::
>>> x[np.array([[1,1],[2,3]])] array([[9, 9], [8, 7]])
当多维数组被索引时,事情变得更加复杂,特别是在多维索引数组中。这些往往是更不寻常的用途,但它们是允许的,并且对某些问题有用。我们将从最简单的多维情况开始(使用前面示例中的数组y):::
>>> 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])] <type 'exceptions.ValueError'>: shape mismatch: objects cannot be broadcast to a single shape
广播机制允许索引数组与其他索引的标量相结合。结果是,标量值用于索引数组的所有对应值:::
>>> 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]])
结果是构建一个新数组,其中索引数组的每个值从被索引的数组中选择一行,结果数组具有结果形状(索引元素的数量、行的大小)。
一个可能有用的例子是,对于一个颜色查找表,我们希望将一个图像的值映射到一个用于显示的RGB三元组中。查阅表格可以有一个形状(nlookup,3)。用dtype=np.uint8的形状(ny,nx)的图像(或任何整数类型,只要值与查找表的边界在一起)索引这样的数组将导致形状(ny,nx,3)的数组,其中三倍的rgb值与每个像素位置相关联。
通常,结果数组的形状将是索引数组的形状(或所有索引数组广播到的形状)与被索引数组中任何未使用维度(未索引的维度)的形状的串联。
用作索引的布尔数组的处理方式与索引数组完全不同。布尔数组的形状必须与被索引数组的初始维度相同。在最简单的情况下,布尔数组具有相同的形状:::
>>> b = y>20 >>> y[b] array([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34])
与整数索引数组的情况不同,在布尔情况下,结果是一个一维数组,其中包含索引数组中对应于布尔数组中所有真元素的所有元素。索引数组中的元素总是迭代并返回到 row-major (C型)订单。结果也与 y[np.nonzero(b)] . 与索引数组一样,返回的是数据的副本,而不是切片视图。
y[np.nonzero(b)]
如果Y的维度多于B,则结果将是多维的。例如:::
>>> b[:,5] # use a 1-D boolean whose first dim agrees with the first dim of y array([False, False, False, True, True]) >>> y[b[:,5]] array([[21, 22, 23, 24, 25, 26, 27], [28, 29, 30, 31, 32, 33, 34]])
在这里,第4行和第5行是从索引数组中选择的,并组合成二维数组。
通常,当布尔数组的维数小于要索引的数组时,这相当于y [B,…] ,也就是说,y被b索引,后面跟着任意多个:根据需要填充y的秩。因此,结果的形状是一个维度,包含布尔数组的真元素数,后面是被索引数组的其余维度。
例如,使用具有四个真元素的二维形状布尔数组(2,3)从三维形状数组(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]])
有关更多详细信息,请参阅有关数组索引的numpy参考文档。
索引数组可以与切片组合。例如:
>>> y[np.array([0, 2, 4]), 1:3] array([[ 1, 2], [15, 16], [29, 30]])
实际上,切片和索引数组操作是独立的。slice操作提取索引为1和2的列(即第2列和第3列),然后index array操作提取索引为0、2和4的行(即第一行、第三行和第五行)。
这相当于:
>>> y[:, 1:3][np.array([0, 2, 4]), :] array([[ 1, 2], [15, 16], [29, 30]])
同样,切片可以与广播的布尔索引组合使用:::
>>> b = y > 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]]) >>> y[b[:,5],1:3] array([[22, 23], [29, 30]])
为了方便将数组形状与表达式和分配进行匹配,可以在数组索引中使用np.newaxis对象添加大小为1的新维度。例如:
>>> y.shape (5, 7) >>> y[:,np.newaxis,:].shape (5, 1, 7)
注意,数组中没有新的元素,只是增加了维数。这样可以方便地将两个数组组合在一起,否则将需要显式地重新整形操作。例如:
>>> 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]])
省略号语法可用于指示完全选择任何剩余的未指定维度。例如:
>>> z = np.arange(81).reshape(3,3,3,3) >>> z[1,...,2] array([[29, 32, 35], [38, 41, 44], [47, 50, 53]])
这相当于:::
>>> z[1,:,:,2] array([[29, 32, 35], [38, 41, 44], [47, 50, 53]])
如前所述,可以使用单个索引、切片、索引和掩码数组选择要分配给的数组子集。分配给索引数组的值必须是形状一致的(相同的形状或可广播到索引生成的形状)。例如,允许将常量赋给切片:::
>>> x = np.arange(10) >>> x[2:7] = 1
或大小合适的数组:::
>>> x[2:7] = np.arange(5)
请注意,如果将更高的类型分配给较低的类型(如floattoints),甚至是异常(将complex分配给floats或ints),则分配可能会导致更改:::
>>> x[1] = 1.2 >>> x[1] 1 >>> x[1] = 1.2j 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] 三次,而不是增加三次。
索引语法非常强大,但在处理数量可变的索引时受到限制。例如,如果您想编写一个函数,该函数可以处理具有不同维数的参数,而不必为每个可能的维数编写特殊的大小写代码,那么该如何完成呢?如果向索引提供元组,则该元组将被解释为索引列表。例如(使用数组z的先前定义)::
>>> 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])
同样,代码也可以通过使用省略号对象来指定省略号:::
>>> 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