NumPy基础-索引

242 阅读10分钟

数组索引是指使用方括号([])来索引数组值。索引有很多选项,这给NumPy提供了强大的索引功能,但是强大的功能也带来了一些复杂性和潜在的混乱。

单个元素的索引

一维数组的单元素索引是我们所期望的,它的工作方式与其他标准Python序列完全相同。它基于0,并且接受复索引以数组末尾开始索引。

与列表和元组不同,numpy数组支持多维数组的多维索引。这意味着不必将每个维度的索引都分成自己的一组方括号。

这里我们要注意,如果索引一个多维数组的索引比维数少,则将获得一个多维数组。例如:

也就是说,指定的每个索引都会选择与所选维度的其余部分相对应的数组。在上面的示例中,选择0表示长度5的剩余维未指定,返回的是该维和大小的数组。必须注意,返回的数组不是原始数组的副本,而是指向内存中与原始数组相同的值。在这种情况下,将返回第一个位置(0)的一维数组。因此,在返回的数组上使用单个索引会导致返回单个元素。例如:

因此请注意,尽管第二种情况的效率较低,因为在第一个索引之后创建了一个新的临时数组,随后将其索引为2。(x[0,2]=x[0][2])

我们要注意那些用于IDL或Fortran内存顺序的索引,因为它们与索引有关。NumPy使用C顺序索引。这意味着最后一个索引通常代表变化最快的内存位置,这与Fortran或IDL不同,在Fortran或IDL中,第一个索引代表内存中变化最快的位置。这种差异代表了很大的混淆可能性。

其它索引选项

可以对数组进行切片和步进以提取相同数量的维,但数组大小与原始数组不同。切片和步进的工作方式与列表和元组完全相同,只是它们也可以应用于多个维度。例如:

数组切片不会复制内部数组数据,只会生成原始数据的新视图。这与列表或元组切片不同,copy()如果不再需要原始数据,则建议使用显示切片。

为了从数组中选择值列表到新数组中,可以将数组与其他数组索引。有两种不同的方法可以实现这一点:其一是使用一个或多个索引值数组,其二是给出一个正确形状的布尔数组来指示要选择的值。索引数组是一种非常强大的工具,它可以避免数组中单个元素的循环,从而极大地提高性能。

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

索引数组

NumPy数组可以与其他数组(或可以转换为数组的任何其他类似序列的对象,例如列表,但元组除外)。索引数组的使用范围从简单的案例到复杂的难以理解的案例,对于所有索引数组,返回的都是原始数据的副本,而不是切片的视图。

索引数组必须为整型,数组中的每个值都指示了使用哪个值来代替索引。例如:

由3、3、1、8组成的索引数组将相应地创建一个长度为4的数组(与索引数组相同),其中每个索引都由索引数组在要索引的数组中具有的值替换。

负值是允许的,并且可以与单个索引或切片一样使用。

索引值超出范围是错误的:

一般而言,使用索引数组时返回的是与索引数组具有相同形状但具有要索引的数组的类型和值的数组,我们可以使用多维索引数组,例如:

索引多维数组

当对多维数组进行索引时,尤其是对于多维索引数组,情况会变得更加复杂。这些往往是更不寻常的用途,但被允许使用,它们对于某些问题很有用。我们先从最简单的多维情况开始(使用前面示例中的数组y):

在这种情况下,如果索引数组具有匹配的形状,并且要索引的数组的每个维都有一个索引数组,则结果数组的形状与索引数组的形状相同,并且值对应于为每个索引设置的索引在索引数组中的位置。在此示例中,两个索引数组的第一索引值均为0,因此,所得数组的第一索引值为y[0,0]。下一值为y[2,1],最后一个为y[4,2]。

如果索引数组的形状不同,则尝试将它们广播为相同的形状。如果无法将它们广播为相同形状,则会引发异常:

广播机制允许将索引数组与标量组合用于其他索引。结果是标量值用于索引数组的所有相应值:

跳转到下一个复杂级别,可以仅使用所有数组对数组进行部分索引。需要花些时间才能理解在这种情况下会发生什么。例如,如果我们仅将一个索引数组与y一起使用:

结果是构造了一个新的数组,其中索引数组的每个值从要索引的数组中选择一行,并且结果数组具有结果的形状(索引元素的数量,行的大小)。

一个可能有用的示例是一个颜色查找表,在该表中我们希望将图像的值映射到RGB三元组中以进行显示。查找表可以具有形状(nlookup,3)。用dtype=np.uint8(或任何整数类型,只要值在查找表的边界内)的形状为(ny,nx)的图像索引这样的数组,将得到形状为(ny,nx,3)RGB值的三倍与每个像素位置相关联。

通常,结果数组的形状将是索引数组的形状(或所有索引数组都广播到的形状)与要索引的数组中任何未使用的尺寸(未索引的形状)的连接。

布尔或掩码索引数组

用作索引的布尔数组与索引数组的处理方式完全不同。布尔数组必须与要索引的数组的初始尺寸具有相同的形状。在最直接的情况下,布尔数组具有相同的形状:

与整数索引数组不同,在布尔值情况下,结果是一维数组,其中包含索引数组中的所有元素,它们对应于布尔数组中的所有真实元素。索引数组中的元素始终按行优先(C样式)的顺序进行迭代和返回。结果也与y[np.nonzero(b)]相同。与索引数组一样,返回的是数据的副本,而不是与切片一起获取的视图。如果y的维数大于b,则结果将是多维的,例如:

在这里,第4行和第5行是从索引数组中选择的,并合成一个二维数组。

通常,当布尔数组的维数少于要建立索引的数组的维数时,这等效于y[b,…],这意味着y由b索引,后跟任意多个“:”来填充y的等级。因此结果的形状是一维,其中包含布尔数组的True元素的数量,其后是要索引的数组的其余维。

例如,使用带有四个True元素的二维布尔型形状(2,3)数组从三维形状(2,3,5)数组中选择行,会得到二维形状(4,5)。

结合索引数组和切片

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

实际上,切片和索引数组操作是独立的。切片操作将提取索引为1和2的列(即第二列和第三列),然后是索引数组操作,将提取索引为0、2和4的行(即第一行、第三行和第五行)。这等效于:

同样,切片可以与广播的布尔索引结合使用:

结构索引工具

为了便于将数组形状与表达式和赋值轻松匹配,可以在数组索引中使用np.newaxis对象添加大小为1的新尺寸。例如:

数组中没有新元素,只是增加了维数。以某种方式结合两个数组可能很方便,否则就需要显式地重构操作。例如:

省略号语法可用于指示完全选择任何剩余的未指定尺寸。例如:

这等效于:

像索引数组赋值

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

或大小合适的数组:

如果将较高的类型分配给较低的类型(例如将浮点数分配给整数),或者甚至将异常(将复杂的值分配给浮点数或整数),分配都可能导致更改:

与某些引用(例如数组和掩码索引)不同,赋值总是对数组中的原始数据进行分配(实际上没有其他意义!)。但是,某些操作可能无法像预期的那样起作用。例如:

人们期望第一个位置会增加3,实际上,它只会增加1。原因是,从包含值为1、1、3、1的原始数组(作为临时数组)中提取了一个新数组,然后将值1添加到临时数组中,然后将临时数组分配回原始数组。因此,数组在x[1]+1处的值被分配给x[1]三次,而不是递增3次。

处理程序中可变数量的索引

索引语法非常强大,但是在处理可变数量的索引时会受到限制。例如,如果你想编写一个函数,它可以处理不同维数的参数,而不必为每个可能维数编写特殊的代码,那该如何实现呢?如果向索引提供了一个元组,则该元组将被解释为一个索引列表。例如(使用前面对数组z的定义):

因此,可以使用代码构造任意数量的索引元组,然后在索引中使用它们。

可以使用Python中的slice()函数在程序中指定切片。例如:

同样,可以使用Ellipsis对象通过代码指定省略号:

因此,可以将np.nonzero()函数的输出直接用作索引,因为它总是返回索引数组的元组。

由于元组的特殊处理,它们不会像列表那样自动转换为数组,例如:

如果你想学习Python,但是找不到学习路径和资源,欢迎来指尖编程