Numpy索引操作详解:从基础到高级用法
本文详细解析Numpy中各种下标操作,涵盖二维矩阵的单行与元素提取、List与Tuple索引机制的区别、冒号切片的高级用法、None扩维技巧以及高维张量的索引策略。重点阐明List与Tuple在语义上的差异及历史遗留问题,帮助开发者避免常见陷阱并掌握高效的数据访问方法。
技术背景
本文基于Numpy Version: 1.20.3版本进行讲解。作为Python中替代Matlab的主流工具,Numpy的核心优势在于其高效的矩阵运算能力。在矩阵运算中,精准地选取轴向和具体元素是基础且关键的操作。本文将系统梳理Numpy中常见的下标选取方式及其标记规则。
二维矩阵的取法
为了直观演示下标操作,我们构建一个4*4的矩阵,其中所有元素均不相同,以便清晰区分每个索引对应的值:
In [1]: import numpy as np
In [2]: x = np.arange(16).reshape((4,4))
In [3]: x
Out[3]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])取单行和单个元素
若需获取第一行的所有元素,可使用x[0];若需获取第一行第一列的具体元素,通常写作x[0][0]。在Numpy中,为简化语法,x[0][0]可直接简写为x[0,0]的形式:
In [4]: id = 0
In [5]: x[id]
Out[5]: array([0, 1, 2, 3])
In [6]: x[id][id]
Out[6]: 0
In [7]: x[id,id]
Out[7]: 0下标的list和tuple格式区分
前文提到的x[0,0]本质上等同于x[(0,0)],即采用tuple(元组)格式。然而,若将下标改为list(列表)格式,其含义和返回结果将完全不同:
In [8]: id = [1,1]
In [9]: x[id]
Out[9]:
array([[4, 5, 6, 7],
[4, 5, 6, 7]])
In [10]: x[id,id]
Out[10]: array([5, 5])
In [11]: id = (1,1)
In [12]: x[id]
Out[12]: 5当使用list格式的下标时,例如x[[1]],表示分别提取第二行的内容,并将结果组合成一个新的矩阵。若下标设为[1,2],则表示分别提取第二行和第三行,而非提取第二行的第二个元素。若需提取第二行第二列的元素,仍必须使用tuple格式。值得注意的是,若输入重复的下标如x[[1,2],[1,2]],其含义是分别提取x[1][1]和x[2][2],并将这两个元素放入同一个矩阵中,这是一种常用的分离式索引方法。
冒号的使用
在Numpy索引中,冒号与后置逗号同时出现表示对该轴向进行全量选取。例如,x[0,:]表示提取x的第一行所有数据,而x[:,0]则表示提取第一列的所有数据:
In [14]: id = 1
In [15]: x[id,:]
Out[15]: array([4, 5, 6, 7])
In [16]: x[:,id]
Out[16]: array([ 1, 5, 9, 13])现存的list与numpy.array不相兼容的取法
尽管前文指出list格式下标表示分别提取,但Numpy当前实现中存在一个遗留问题:当使用多维list格式进行索引时,系统会自动将最外层转换为tuple格式,从而采用tuple的索引逻辑。虽然计算时会发出警告,但仍需引起注意:
In [17]: id = [[1],[1]]
In [18]: x[id]
<ipython-input-18-23f8764f4b7e>:1: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.
x[id]
Out[18]: array([5])
In [19]: id = np.array([[1],[1]])
In [20]: x[id]
Out[20]:
array([[[4, 5, 6, 7]],
[[4, 5, 6, 7]]])两个冒号的组合用法
当冒号不与后置逗号同时出现时,表示从冒号前的元素取值至冒号后的元素。例如x[0:3]表示提取[x[0],x[1],x[2]]。若使用两个冒号且中间无逗号,如x[0:3:2],则表示每隔2个元素取一个,最终结果为[x[0],x[2]]。此外,常见的[::-1]操作表示对当前轴进行倒序排列:
In [31]: x[::-1]
Out[31]:
array([[12, 13, 14, 15],
[ 8, 9, 10, 11],
[ 4, 5, 6, 7],
[ 0, 1, 2, 3]])
In [32]: x[::-1,::-1]
Out[32]:
array([[15, 14, 13, 12],
[11, 10, 9, 8],
[ 7, 6, 5, 4],
[ 3, 2, 1, 0]])用None作扩维
虽然Numpy提供了broadcast和expand_dim等函数用于矩阵扩维或广播,但更便捷的方法是对需扩展的维度使用None作为下标。例如,将(4,4)大小的矩阵扩展为(1,4,4),可使用[None,:]或[None,:,:]。若需将(4,4)变为(4,1,4),则需调整None的位置为[:,None,:]即可实现:
In [33]: x[None,:]
Out[33]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
In [34]: x[:,None,:]
Out[34]:
array([[[ 0, 1, 2, 3]],
[[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11]],
[[12, 13, 14, 15]]])
In [35]: x[:,:,None]
Out[35]:
array([[[ 0],
[ 1],
[ 2],
[ 3]],
[[ 4],
[ 5],
[ 6],
[ 7]],
[[ 8],
[ 9],
[10],
[11]],
[[12],
[13],
[14],
[15]]])高维矩阵的取法
在高维矩阵中,由于不再适用简单的行和列概念,需从轴向角度理解相关操作。我们先定义一个简单的三维张量:
In [49]: y = np.arange(32).reshape((2,4,4))
In [50]: y
Out[50]:
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, 30, 31]]])常规操作与前述二维张量一致。这里探讨一种特殊场景:若使用二维矩阵的取法,但仅对第一条轴的每个元素选取一个id(例如取第0条轴的[0,1]元素和第1条轴的[2,3]元素),最简单的方案是在第一个下标位置添加一个位置元素,该元素由下标id的第一个轴长度定义:
In [58]: id = np.array([[0,1],[2,3]])
In [59]: y[np.arange(id.shape[0]),id[:,0],id[:,1]]
Out[59]: array([ 1, 27])总结概要
本文梳理了Numpy中常用的各类下标操作,包括指定轴的全量元素提取、指定位置的单个或多元素提取、扩维操作以及未显式给定位置的多个元素提取等。重点强调了Numpy中tuple与list索引机制的含义差异,并指出了因历史原因导致的list索引与numpy.array索引不一致的情况,供读者参考。
到此这篇关于Numpy的各种下标操作的示例代码的文章就介绍到这了,更多相关Numpy 下标 内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













