NumPy 中文文档

version: 1.14.0

Built with Teadocs

索引

# 索引

ndarrays可以使用标准Python x[obj] 语法编制索引,其中x是数组,obj是选择。有三种索引可用:字段访问,基本切片,高级索引。哪一个发生取决于obj。

注意
在Python中,x[(exp1, exp2, ..., expN)] 相当于 x[exp1, exp2, ..., expN] ; 后者只是前者的语法糖。

# 基本切片和索引

基本切片将Python的切片基本概念扩展到N维。 当obj是切片对象(由start:stop:括号内的步骤符号构造),整数或切片对象和整数的元组时,会发生基本切片。 省略号和newaxis对象也可以穿插其中。 为了保持向后兼容Numeric中的常见用法,如果选择对象是包含切片对象,Ellipsis对象或newaxis对象的任何非nararray序列(例如列表),则也会启动基本切片,但不是 整数数组或其他嵌入序列。

使用N个整数进行索引的最简单情况是返回表示相应项的数组标量。 与在Python中一样,所有索引都是从零开始的:对于第i个索引n_i,有效范围是 0\le n_i

通过基本切片生成的所有数组始终是原始数组的视图。

序列切片的标准规则适用于基于每维的基本切片(包括使用步骤索引)。 要记住的一些有用的概念包括:

  • 基本切片语法是 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 = qk + r,使得i +(m-1)k

    例子

      >>> 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]]])
  • Ellipsis 扩展为:x.ndim生成与长度相同的选择元组所需的对象数。可能只存在一个省略号。

    例子

      >>> x[...,0]
      array([[1, 2, 3],
          [4, 5, 6]])
  • 选择元组中的每个newaxis对象用于将结果选择的维度扩展一个单位长度维度。 添加的维度是选择元组中newaxis对象的位置。

    例子

      >>> x[:,np.newaxis,:,:].shape
      (2, 1, 3, 1)
  • 整数i返回与i:i + 1相同的值,除了返回对象的维数减少1.特别是,具有第p个元素的整数(和所有其他条目:)的选择元组返回 对应的子数组,其维数为N - 1.如果N = 1,则返回的对象是数组标量。 Scalars中解释了这些对象。

  • 如果选择元组具有所有条目:除了作为切片对象i:j:k的第p个条目之外,则返回的数组具有通过连接由元素i,i + k的整数索引返回的子数组而形成的维N. ,...,i + (m - 1)k

  • 在切片元组中使用多个非:条目进行基本切片,就像使用单个非:条目重复应用切片一样,其中连续采用非:条目(所有其他非:条目替换为:)。 因此,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]。这对于构造适用于任意维数组的通用代码非常有用。

# numpy.newaxis

可以在所有切片操作中使用newaxis对象来创建长度为1的轴。 newaxis是'None'的别名,'None'可以代替它使用相同的结果。

# 高级索引

当选择对象obj是非元组序列对象,ndarray(数据类型为integer或bool)或具有至少一个序列对象或ndarray(数据类型为integer或bool)的元组时,将触发高级索引。 高级索引有两种类型:整数和布尔值。

高级索引始终返回数据的副本(与返回视图的基本切片形成对比)。

错误

高级索引的定义意味着x[(1, 2, 3), ] 与 x[(1, 2, 3)]根本不同。 后者相当于 x[1,2,3],它将触发基本选择,而前者将触发高级索引。一定要明白为什么会这样。

还要认识到 x[[1,2,3]] 将触发高级索引,而 x[[1,2,slice(None)]] 将触发基本切片。

# 整数数组索引

整数数组索引允许基于其N维索引选择数组中的任意项。 每个整数数组表示该维度的多个索引。

# 纯整数数组索引

当索引由被索引的数组具有维度的整数数组组成时,索引是直接的,但与切片不同。

高级索引始终作为一个广播和迭代:

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相同。

例子

从每一行开始,应选择一个特定元素。 行索引只是 [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 = 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_调用,只会选择对角线元素,就像前面的例子中所使用的那样。 对于使用多个高级索引进行索引,这个差异是最重要的。

# 结合高级和基本索引

当索引中至少有一个切片(:),省略号(...)或np.newaxis时(或者数组的维度比高级索引多), 然后行为可能会更复杂。 这就像连接每个高级索引元素的索引结果一样

在最简单的情况下,只有一个高级索引。 单个高级索引可以例如替换切片,并且结果数组将是相同的,但是,它是副本并且可以具有不同的存储器布局。 当可能时,切片是优选的。

例子

>>> x[1:2, 1:3]
array([[4, 5]])
>>> x[1:2, [1, 2]]
array([[4, 5]])

理解这种情况的最简单方法可能是根据结果形状进行思考。 索引操作分为两部分,即由基本索引(不包括整数)定义的子空间和来自高级索引部分的子空间。 需要区分两种索引组合:

  • 高级索引由切片,省略号或新轴分隔。 例如x [arr1, :, arr2]
  • 高级索引彼此相邻。 例如x[..., arr1, arr2, :]但不是x[arr1, :, 1]因为1在这方面是一个高级索引。

在第一种情况下,高级索引操作产生的维度首先出现在结果数组中,然后是子空间维度。 在第二种情况下,高级索引操作的维度将插入到结果数组中与初始数组中相同的位置(后一种逻辑使简单的高级索引行为就像切片一样)。

例子

假设x.shape是(10, 20, 30)而ind是(2, 3, 4)形索引的intp数组,那么result=x[..., ind, :]有shape (10, 2, 3, 4, 30) 因为 (20, )-shaped 子空间已被 (2, 3, 4) 形广播索引子空间所取代。如果我们让i,j,k在(2,3,4)形子空间上循环,那么结果[..., 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)-shaped 子空间已被替换为来自 (2,3,4)的子空间 指数。但是,x[:, ind_1, :, ind_2]具有形状(2, 3, 4, 10, 30, 50),因为在索引子空间中没有明确的位置,因此它被添加到开头。始终可以使用.transpose()在任何需要的位置移动子空间。请注意,此示例无法使用take进行复制。

# 布尔数组索引

当obj是布尔类型的数组对象时,会发生此高级索引,例如可能从比较运算符返回。单个布尔索引数组实际上与x [obj.nonzero()]相同,其中,如上所述,Obj.nonzero() 返回一个整数索引数组的元组(长度为obj.ndim),显示True元素 对象 但是,当obj.shape == x.shape时它会更快。

如果obj.ndim == x.ndim,则x [obj]返回一个1维数组,该数组填充了与obj的True值对应的x元素。 搜索顺序为行主,C风格。 如果obj在x的边界之外的条目处具有True值,则将引发索引错误。 如果obj小于x,则与填充False相同。

例子

一个常见的用例是过滤所需的元素值。 例如,可能希望从阵列中选择非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, )]

如果只有一个布尔数组且没有整数索引数组,则这是直截了当的。 必须注意确保布尔索引具有与其应该使用的维度完全相同的维度。

例子

从数组中,选择总和小于或等于2的所有行:

>>> x = np.array([[0, 1], [1, 1], [2, 2]])
>>> rowsum = x.sum(-1)
>>> x[rowsum <= 2, :]
array([[0, 1],
       [1, 1]])

但是如果rowsum也有两个维度:

>>> rowsum = x.sum(-1, keepdims=True)
>>> rowsum.shape
(3, 1)
>>> x[rowsum <= 2, :]    # fails
IndexError: too many indices
>>> x[rowsum <= 2]
array([0, 1])

由于额外的维度,最后一个只给出了第一个元素。 比较rowsum.nonzero()来理解这个例子。

将多个布尔索引数组或布尔值与整数索引数组合在一起,可以用obj.nonzero()类比来理解。 函数ix_也支持布尔数组,并且可以毫无意外地工作。

例子

使用布尔索引选择加起来为偶数的所有行。 同时,应使用高级整数索引选择列0和2。 使用ix_函数可以通过以下方式完成:

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

# 详细说明

这些是一些详细的注释,对于日常索引(无特定顺序)并不重要:

  • 本机NumPy索引类型是intp,可能与默认的整数数组类型不同。 intp是足以安全索引任何数组的最小数据类型; 对于高级索引,它可能比其他类型更快。

  • 对于高级分配,通常不保证迭代顺序。 这意味着如果元素被设置多次,则无法预测最终结果。

  • 空(元组)索引是零维数组的完整标量索引。 如果x是零维,则 x[()]返回标量,否则返回视图。 另一方面,x[...]总是返回一个视图。

  • 如果索引中存在零维数组并且它是完整的整数索引,则结果将是标量而不是零维数组。 (不会触发高级索引。)
    当省略号(...)存在但没有大小(即替换为零)时,结果仍将始终为数组。 如果没有高级索引,则为视图,否则为副本。

  • 布尔数组的nonzero等价不适用于零维布尔数组。

  • 当高级索引操作的结果没有元素但单个索引超出范围时,是否引发IndexError是未定义的(例如 x[[],[123]],其中123超出界限)。
    当在赋值期间发生转换错误时(例如,使用字符串序列更新数值数组),被分配的数组可能最终处于不可预测的部分更新状态。 但是,如果发生任何其他错误(例如超出范围索引),则数组将保持不变。

  • 高级索引结果的内存布局针对每个索引操作进行了优化,并且不能假设特定的内存顺序。

  • 当使用子类(尤其是操纵其形状的子类)时,默认的ndarray .__ setitem__行为将调用getitem进行基本索引,但不调用高级索引。 对于这样的子类,最好使用数据的基类ndarray视图调用ndarray .__ setitem__。 如果子类getitem没有返回视图,则必须执行此操作。

# Field Access

另见:

数据类型对象(Dtype),标量

如果 ndarray 对象是一个结构化数组,则可以通过对数组中的字符串、类似字典的数组进行索引来访问数组的字段。

索引x['field-name']返回一个数组的新视图,它与x的形状相同(当字段是子数组时除外)但数据类型为x.dtype [ 'field-name']并且只包含指定字段中的部分数据。 记录数组标量也可以这种方式“索引”。

索引到结构化数组也可以使用字段名称列表来完成,例如,×[['字段名1', '字段名2']]。 目前,这将返回一个新数组,其中包含列表中指定的字段中的值的副本。 从NumPy 1.7开始,不推荐返回副本以支持返回视图。 现在将继续返回副本,但在写入副本时将发出FutureWarning。 如果依赖于当前行为,那么我们建议显式复制返回的数组,即使用x [['field-name1','field-name2']]。copy()。 这将适用于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-contiguous样式,最后一个索引变化最快)。 只要选择对象不是元组,也可以使用基本切片或高级索引对此迭代器对象建立索引。 从“x.flat`”是一维视图的事实可以清楚地看出这一点。 它可用于具有1维C风格平面索引的整数索引。 因此,任何返回数组的形状都是整数索引对象的形状。