索引

数组索引是指使用方括号([])对数组值进行索引。有很多选项来索引,这使numpy索引很强大,但功能上的强大也带来一些复杂性和潜在的混乱。本节仅仅是与索引相关的各种选项和问题的概述。除了单元素索引,大多数选项的详细信息可以在相关部分找到。

Assignment vs referencing

大多数以下示例显示在引用数组中的数据时使用索引。这些示例在分配给数组时同样适用。有关分配如何工作的具体示例和说明,请参阅末尾的部分。

Single element indexing

1-D数组的单元素索引是人们期望的。它的工作原理与其他标准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)处的1-D数组。因此,对返回的数组使用单个索引,导致返回单个元素。那是:

>>> x[0][2]
2

因此,请注意,虽然第二种情况是x [0,2] = x [0] [2] 更低效率,因为在随后由2索引的第一索引之后创建新的临时数组。

注意那些用于IDL或Fortran内存顺序的索引。Numpy使用C阶索引。这意味着,最后的索引通常表示最快速变化的存储器位置,不同于Fortran或IDL,其中第一索引表示存储器中最快速变化的位置。这种差异代表着很大的混乱潜能。

Other indexing options

可以切片和跨步数组来提取相同数量维度的数组,但大小不同于原始数组。切片和跨步的工作方式与对列表和元组完全相同,除了它们可以应用于多个维度。几个例子说明最好:

>>> 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]])

请注意,数组片段不会复制内部数组数据,但也会生成原始数据的新视图。

可以将数组与其他数组建立索引,以便从数组中选择值列表到新数组中。有两种不同的方式来实现这一点。一个使用一个或多个索引值数组。另一个涉及给出适当形状的布尔数组以指示要选择的值。索引数组是一个非常强大的工具,允许避免循环数组中的单个元素,从而大大提高性能。

可以使用特殊功能通过索引有效地增加数组中的维数,以便生成的数组获得在表达式中使用或使用特定函数所需的形状。

Index arrays

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]])

Indexing Multi-dimensional arrays

当对多维数组进行索引时,特别是对于多维索引数组,事情变得更加复杂。这些往往是更多的非理性用途,但它们是允许的,它们对一些问题有用。我们将从最简单的多维情况开始(使用前面例子中的数组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值与每个像素位置相关联。

一般来说,resulant数组的形状将是索引数组的形状(或所有索引数组被广播到的形状)与被索引的数组中的任何未使用维度(未索引的那些)的形状的连接。

Boolean or “mask” index arrays

用作索引的布尔数组以不同于索引数组的方式进行处理。布尔数组必须与要编制索引的数组的初始维度具有相同的形状。在最直接的情况下,布尔数组具有相同的形状:

>>> b = y>20
>>> y[b]
array([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34])

与整数索引数组的情况不同,在布尔数组中,结果是1-D数组,其包含索引数组中的所有元素,对应于布尔数组中的所有真实元素。索引数组中的元素始终以row-major(C样式)顺序进行迭代和返回。结果也与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], dtype=bool)
>>> y[b[:,5]]
array([[21, 22, 23, 24, 25, 26, 27],
       [28, 29, 30, 31, 32, 33, 34]])

这里,从索引数组中选择第4和第5行,并组合以形成2-D数字组。

通常,当布尔数组具有比被索引的数组更少的维度时,这等同于y [b,...],这意味着y被索引为b,然后是多个:如同填充y。因此,结果的形状是包含布尔数组的True元素的数目的一个维度,后面是被索引的数组的剩余维度。

例如,使用具有四个真实元素的形状(2,3)的2-D布尔数组来从形状(2,3,5)的3-D数字组中选择行导致形状的2-D结果(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参考文档。

Combining index arrays with slices

索引数组可以与切片组合。例如:

>>> y[np.array([0,2,4]),1:3]
array([[ 1,  2],
       [15, 16],
       [29, 30]])

实际上,将切片转换为用索引数组广播的索引数组np.array([[1,2]])(shape(1,2)),以产生形状(3,2)的结果数组。

同样,切片可以与广播的布尔指数组合:

>>> y[b[:,5],1:3]
array([[22, 23],
       [29, 30]])

Structural indexing tools

为了便于数组形状与表达式和赋值关系的匹配,可以在数组索引中使用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]])

Assigning values to indexed arrays

如上所述,可以使用单个索引,切片,索引和掩码数组来选择数组的子集来分配。分配给索引数组的值必须是形状一致的(相同的形状或可广播到索引产生的形状)。例如,允许为切片分配常量:

>>> x = np.arange(10)
>>> x[2:7] = 1

或正确大小的数组:

>>> x[2:7] = np.arange(5)

请注意,如果将较高类型分配给较低类型(在int类型中添加浮点数(floats))或甚至导致异常(将复数分配给int/float类型),分配可能会导致更改:

>>> x[1] = 1.2
>>> x[1]
1
>>> x[1] = 1.2j
<type 'exceptions.TypeError'>: can't convert complex to long; use
long(abs(z))

与某些引用(例如数组和掩码索引)不同,它们总是对数组中的原始数据进行赋值(事实上,没有其他什么是有意义的!)。请注意,一些操作可能无法正常工作。这个特殊的例子往往是令人惊讶的人:

>>> 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次。

Dealing with variable numbers of indices within programs

索引语法非常强大,但在处理可变数量的索引时限制。例如,如果你想编写一个可以处理具有不同维数的参数的函数,而不必为每个可能的维度编写特殊的情况代码,那么如何做呢?如果向索引提供一个元组,则该元组将被解释为索引列表。例如(使用数组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.where()函数的输出作为索引,因为它总是返回一个索引数组的元组。

因为元组的特殊处理,它们不会像列表那样自动转换为数组。举个例子:

>>> 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