3. 数组的索引

3.1 一维数组的索引

  1. 一维数组的索引和列表相同。假设a1 是一维数组

    • 可以指定一个整数i作为索引下标,如a1[i] index_int

    • 可以指定一个切片作为索引下标,如a1[i:j]。通过切片获得的新的数组是原始数组的一个视图,它与原始数组共享相同的一块数据存储空间。 index_slice

    • 可以指定一个整数列表对数组进行存取,如a1[[i1,i2,i3]]。此时会将列表中的每个整数作为下标(i1/i2/i3),使用列表作为下标得到的数组(为 np.array([a1[i1],a1[i2],a1[i3]]))不和原始数组共享数据。 index_list

    • 可以指定一个整数数组作为数组下标,如a1[a2]此时会得到一个形状和下标数组a2相同的新数组。新数组的每个元素都是下标数组中对应位置的值作为下标从原始数组中获得的值。新数组不和原始数组共享数据。

      • 当下标数组是一维数组时,其结果和用列表作为下标的结果相同 index_array
      • 当下标是多维数组时,结果也是多维数组 index_array_2
    • 可以指定一个布尔数组作为数组下标,如a1[b]。此时将获得数组a1中与数组b中的True对应的元素。新数组不和原始数组共享数据。

      • 布尔数组的形状与数组a1 完全相同,它就是一个mask

      • 如果是布尔列表,情况也相同

      • 如果布尔数组的长度不够,则不够的部分作为False(该特性是deprecating,建议不要使用)

  2. 上述介绍的一维数组的索引,既可以用于数组元素的选取,也可以用于数组元素的赋值

    • 你可以赋一个值,此时该值会填充被选取出来的每一个位置

    • 你可以赋值一个数组或者列表,此时数组或者列表的形状要跟你选取出来的位置的形状完全匹配(否则报出警告)

      • 数组不同于列表。对于列表,你无法对列表切片赋一个值,而是要赋一个形状相同的值

    index_assign

3.2 多维数组的索引

  1. 多维数组使用元组作为数组的下标,如a[1,2],当然你也可以添加圆括号为a[(1,2)]

    • 元组中每个元素和数组的每个轴对应。下标元组的第 0 个元素与数组的第 0 轴对应,如第 1 个元素与数组的第 1 轴对应…
  2. 多维数组的下标必须是一个长度和数组的维度ndim相等的元组。

    • 如果下标元组的长度大于数组的维度ndim,则报错
    • 如果下标元组的长度小于数组的维度ndim,则在元组的后面补 :,使得下标元组的长度等于数组维度ndim
    • 如果下标对象不是元组,则Numpy会首先将其转换为元组。

    下面的讨论都是基于下标元组的长度等于数组维度ndim的条件。

  3. 单独生成切片时,需要使用slice(begin,end,step) 来创建。其参数分别为:开始值,结束值,间隔步长。如果某些参数需要省略,则使用None。因此, a[2:,2]等价于a[slice(2,None,None),2]

    • 使用python内置的slice()创建下标比较麻烦(首先构造切片,再构造下标元组),numpy提供了一个numpy.s_对象来帮助我们创建数组下标。s_对象实际上是IndexExpression类的一个对象 numpy_s_
  4. 多维数组的下标元组的元素可能为下列类型之一:整数、切片、整数数组、布尔数组。如果不是这些类型,如列表或者元组,则将其转换成整数数组。 mlt_index

    • 多维数组的下标全部是整数或者切片:索引得到的是元素数组的一个视图。 mlt_index_slice

    • 多维数组的下标全部是整数数组:假设多维数组为 3. 数组的索引 - 图10 。假设这些下标整数数组依次为 3. 数组的索引 - 图11。这 3. 数组的索引 - 图12 个数组必须满足广播条件。假设它们进行广播之后的维度为 3. 数组的索引 - 图13 ,形状为 3. 数组的索引 - 图14 即:广播之后有 3. 数组的索引 - 图15 个轴:第 0 轴长度为 3. 数组的索引 - 图16 ,…,第 3. 数组的索引 - 图17 轴长度为 3. 数组的索引 - 图18 。假设 3. 数组的索引 - 图19 经过广播之后分别为数组 3. 数组的索引 - 图20

      则:索引的结果也是一个数组 3. 数组的索引 - 图21 ,结果数组 3. 数组的索引 - 图22 的维度为 3. 数组的索引 - 图23 ,形状为 3. 数组的索引 - 图24 。其中

      3. 数组的索引 - 图25

      结果数组的下标并不来源于 3. 数组的索引 - 图26,而是来源于下标数组的广播之后的数组。相反,如果多维数组的下标为整数或者切片,则结果数组的下标来源于 3. 数组的索引 - 图27

    mlt_index_array

    • 多维数组的下标包含整数数组、切片:则切片/整数下标与整数数组下标分别处理。 mlt_index_array_slice

    • 多维数组的下标是布尔数组或者下标元组中包含了布尔数组,则相当于将布尔数组通过nonzero 将布尔数组转换成一个整数数组的元组,然后使用整数数组进行下标运行。

      • nonzero(a)返回数组a中,值非零的元素的下标。它返回值是一个长度为a.ndim的元组,元组的每个元素都是一个一维的整数数组,其值为非零元素的下标在对应轴上的值。如:第 0 个元素为a中的非零值元素在0轴的下标、第 1 个元素为a中的非零值元素在1轴的下标,… mlt_index_bool
  5. 当下标使用整数或者切片时,所取得的数据在数据存储区域中是等间隔分布的。因为只需要修改数组的ndim/shape/strides等属性以及指向数据存储区域的data指针就能够实现整数和切片下标的索引。所以新数组和原始数组能够共享数据存储区域。

    当使用整数数组(整数元组,整数列表页转换成整数数组),布尔数组时,不能保证所取得的数据在数据存储区中是等间隔的,因此无法和原始数组共享数据,只能对数据进行复制。

  6. 索引的下标元组中:

    • 如果下标元组都是切片,则索引结果的数组与原始数组的维度相同(轴的数量相等)
    • 每多一个整数下标,则索引结果的数组就少一个维度(少一个轴)
    • 如果所有的下标都是整数,则索引结果的维度为 0
    • 如果下标元组中存在数组,则还需要考虑该下标数组广播后的维度
  7. 通过索引获取的数组元素的类型为数组的dtype类型 。如果你想获取标准python类型,可以使用数组的item()方法。

    index_item

3.3 索引的维度变换

  1. 对于数组,如果我们不考虑下标数组的情况,也就是:其下标仅仅为整数、或者切片,则有:

    • 每次下标中出现一个整数下标,则索引结果的维度降 1。该维度被吸收掉
    • 每次下标中出现一个切片下标,则该维度保持不变 index_dim
  2. 前面提到:多维数组的下标必须是一个长度和数组的维度 ndim 相等的元组。但是如果下标中包含None,则可以突破这一限制。每多一个None,则索引结构维度升 1 。

    • 当数组的下标元组的长度小于等于数组的维度ndim时,元组中出现的None等价于切片:
    • 当数组的下标元组的长度大于数组的维度ndim时,元组中哪里出现None,索引结果就在哪里创建一个新轴,该轴长度为 1。如c=a[0,:,None],索引结果的维度为 (3,1);而d=a[0,None,:]的索引结果维度为(1,3)

    index_none