python、numpy、Pytorch中的索引方式
能用索引方式过多,不止下标索引。自己写程序还好,关键是看人家写的程序,有时候会晕掉,不知所云。故在此总结。
首先对Numpy的下标索引、切片索引、布尔索引和花式索引进行介绍和分析,然后研究Python和Pytorch中的索引方式是否包括以上四种。
Numpy
下标索引
>>> a = numpy.arange(6)
# 正向从0开始
>>> a[2]
# 反向从-1开始
>>> a[-2]
4
# 多维数组
>>> a = numpy.arange(8).reshape([2,2,2])
>>> a
array([[[0, 1],
[2, 3]],
[[4, 5],
[6, 7]]])
# a[0][-2][1]和a[0,-2,1]输出相同。只是a[0][-2][1]先取a[0],再取[-2],最后[-1],会慢一些
>>> a[0,-2,1]
1
# 输入第一个维度值
>>> a[0]
array([[0, 1],
[2, 3]])
切片索引
>>> a = numpy.arange(6)
array([0, 1, 2, 3, 4, 5])
# start:end, end可以省略,也可以写一个大于实际长度的值
>>> a[4:]
array([4, 5])
>>> a[4:100]
array([4, 5])
# start:end:step
# 注意切片中不包括end
>>> a[1:5:2]
array([1, 3])
# 不写start和end就是分别默认首和尾
>>>a[::2]
array([0, 2, 4])
# 反向排列::-1,
# 并不能指定部分序列反向如a[1:5:-1],此时返回空
>>> a[::-1]
array([5, 4, 3, 2, 1, 0])
>>> a[1:5:-1]
array([], dtype=int64)
# 多维数组,
>>> a = numpy.arange(12).reshape([2,3,2])
>>> a
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]])
# 返回部分值,
#(1)end仍然可以大于实际值,
#(2):表示返回该维度全部,
#(3)以start:end切片返回的维度和原数组相同
>>>a[0:100,1:100,:]
array([[[ 2, 3],
[ 4, 5]],
[[ 8, 9],
[10, 11]]])
# 针对某一维度切片
# 若直接指定某一维度的值,返回数组的维度-1
>>>a[1,1,:]
array([8, 9])
>>>a[1,1:100,:]
array([[ 8, 9],
[10, 11]])
至此,相信大多数人都能看懂,因为在每个维度上明确指出了取多少。但有一些操作,会让你眼花缭乱。 比如省略号和冒号
>>> a = numpy.arange(12).reshape([2,3,2])
>>> a
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]])
# 省略号表示,其余所有维度的所有值都要。还算有用。
# a[1,...]等同于a[1,:,:],好处是无需把每个维度都写一遍
>>> a[1,...]
array([[ 6, 7],
[ 8, 9],
[10, 11]])
# a[...,1]等同于a[:,:,1]
>>> a[...,1]
array([[ 1, 3, 5],
[ 7, 9, 11]])
# 对于三维数组,一个冒号也可以和省略号相同,
# 表示其余所有维度的所有值都要。
# (1)但这是一个相当不好的设计,至少我这么认为。
# 因为读者可能不清楚这个数组到底几维,
# 你这么写极有可能会让读者误以为是二维数组。
>>> a[1,:]
array([[ 6, 7],
[ 8, 9],
[10, 11]])
布尔索引
这是个很实用的技巧
>>> a = numpy.arange(12).reshape([2,3,2])
>>> a
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]])
# 在Numpy中,对array做逻辑运算,返回的是同siaze的Bool型array
>>> a>6
array([[[False, False],
[False, False],
[False, False]],
[[False, True],
# 可根据这个结果进行索引
# (1)返回的总是一维数组
>>>a[a>6]
array([ 7, 8, 9, 10, 11])
花式索引(Fancy indexing)
通过数组作为坐标去索引。这个方式看了一会才明白。花式索引主要是针对不连续的索引下标,是有实际用处的。
>>> a = numpy.arange(12).reshape([2,3,2])
>>> a
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]])
先从简单的情况看起
此时,a为三维数组,你想找某个位置的值,得需要三个值作为坐标吧。即a[x,y,z],比如说a[1][2][0]。而花式索引就是把每个坐标以数组的方式传入。
>>> a[1][2][0]
10
# 花式索引, 此时变成x=[1], y=[2], z=[0]
>>> a[[1],[2],[0]]
array([10])
# 此时变成x=[1,0], y=[2,1], z=[0,1]
# 即a[1,2,0]和a[0,1,1]
>>>a[[1,0],[2,1],[0,1]]
array([10, 3])
稍微复杂点的切片
刚才是直接指定了三个维度,那么切片怎么做?少指定一个维度就行了
# 指定了两个维度,相当于找a[1,2,:](暂时忽略返回维度)
>>> a[[1],[2]]
array([[10, 11]])
>>> a[1,2,:]
array([10, 11])
# 相当于找a[1,2,:]和a[1,0,:]
>>>a[[1,1],[2,0]]
array([[10, 11],
[ 6, 7]])
此时另一个问题也可以解决了,为什么a[[1],[2]]会比a[1,2,:]多一个维度。需要首先考虑a[[1,1],[2,0]]:
- 如果使用a[1,2,:]和a[1,0,:],需要分别查找,得到两个结果。
- 而使用a[[1],[2]],只需一次即可得到结果,结果就是将a[1,2,:]和a[1,0,:]整合起来。
- 为了可能存在的“整合操作”,花式索引就要多出一个维度,即便结果不需要“整合”。
考虑广播机制
比如y维度只给了一个2,Python中的广播之后,变成了寻找a[0,2,:]和a[1,2,:]
>>>a[[0,1],[2]]
array([[ 4, 5],
[10, 11]])
Python
让我们看看List有什么方式
下标索引和切片索引
只能通过a[1][0]的方式。使用的时候将其考虑为一层一层的list即可。
>>> a = [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]]]
>>> a[1][0]
[7, 8]
>>> a[1][0][1]
8
# 不可以a[1,0]
>>> a[1,0]
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: list indices must be integers or slices, not tuple
>>>a[0][0:2]
[[1, 2], [3, 4]]
# 但倒序的操作还在,只针对最外层
>>>a[::-1]
[[5, 6], [3, 4], [1, 2]]
布尔索引
Numpy中的方式失败
>>> a = [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]]]
>>> a>0
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: '>' not supported between instances of 'list' and 'int'
但也不是没有,尽管没什么用。True是1,False是0
>>> a[0][False]
[1, 2]
>>> a[0][True]
[3, 4]
花式索引
没有
>>> a[[1],[2],[0]]
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: list indices must be integers or slices, not tuple
Pytorch
终于到了令人期待的Pytorch
下标索引和切片索引
>>> a = torch.randint(low=0,high=10,size=[2,3,2])
tensor([[[1, 4],
[5, 8],
[0, 1]],
[[0, 8],
[4, 7],
[1, 8]]])
>>> a[0,:,1]
tensor([4, 8, 1])
# 省略号省略号表示其余所有维度的所有值都要(此功能的冒号也支持)
>>> a[0,...]
tensor([[1, 4],
[5, 8],
[0, 1]])
# ::-1倒序不支持
>>> a[::-1]
Traceback (most recent call last):
File "<input>", line 1, in <module>
ValueError: step must be greater than zero
布尔索引
# 支持
>>> a>5
tensor([[[False, False],
[False, True],
[False, False]],
[[False, True],
[False, True],
[False, True]]])
>>> a[a>5]
tensor([8, 8, 7, 8])
花式索引
# 支持
>>> a[[1,0],[2,0]]
tensor([[1, 8],
[1, 4]])
>>> a[[1,0],[2]]
tensor([[1, 8],
[0, 1]])
参考
- https://blog.csdn.net/weixin_43569478/article/details/108079868
- https://zhuanlan.zhihu.com/p/123858781