前言
本篇记录numpy数组的索引和切片方法。
数组索引
需要获取numpy数组中的元素,可以采用[index]
索引的方法,其中index
是每一维度的索引,下标从0开始,到n-1结束(类似C++数组索引)。
索引获得单个元素
当索引的维度与数组维度相同时,索引获得的是单个元素的值:
import numpy as np
a = np.array([[1, 2], [3, 4]])
print(a[0, 1]) # 2
print(a[1, 0]) # 3
另外,索引中的-1
表示最后一个元素,-2, -3
依此类推:
a[-1, -1] # 4
a[-2, -2] # 1
索引获得单个数组
当索引的维度小于数组维度时,索引获得的是一个低维数组:
import numpy as np
a = np.array([[1, 2], [3, 4]])
print(a[1]) # array([1, 2])
print(a[0]) # array([3, 4])
实际上,获得的低维数组在高维的角度下,其实就是数组的“元素”。
list索引
假设一维numpy数组a.shape=(4,)
,现在我想同时获得第一和第三个元素,可以在索引中使用list:
import numpy as np
a = np.random.randn(4)
a[[0, 2]] # np.ndarray, shape=(2,)
a[0, 2] # error!
这里需要分清,在[]
索引中,使用list和非list的区别:正如上面的示例中,a[[0, 2]]
的意思是提取第一维的第1和第3个元素(可以只有一维),得到的结果是一个新的一维数组;而a[0, 2]
则表示提取第一维的第一个、第二维的第三个元素(必须有两维以上),因此会报错。
简单而言,a[[ind1, ind2]]
是取索引为ind1, ind2
的两个元素作为新数组,而a[ind1, ind2]
是取坐标为(ind1, ind2)
的元素。
此外,使用多维np.ndarray的效果与python list的效果是相同的,但tuple则是与取坐标效果相同:
import numpy as np
a = np.random.randn(4, 4)
a[[0, 2]] # np.ndarray, shape=(2, 4)
a[np.array([0, 2])] # np.ndarray, shape=(2, 4)
a[0, 2] # scalar
a[(0, 2)] # scalar
数组切片
切片操作是numpy的一个特色机制(pytorch, paddle等也应用了这个机制)。数组切片用于一次获取数组中的多个元素,并保持一定的数组结构。
所谓切片,就是在数组中切割出一部分元素出来,通过在索引中使用:
实现。
:索引切片
ind1:ind2
在索引中的意思是获取从索引ind1
(包括)到ind2
(不包括)的元素,也就是左闭右开切片:
import numpy as np
a = np.array([1, 2, 3, 4])
a[1:3] # array([1, 2])
a[:-1] # array([1, 2, 3])
当ind1
为空,表示从该维度第一个元素开始;ind2
为空时,到最后一个元素结束(包含)切片:
a[:] # array([1, 2, 3, 4])
还有ind1:ind2:step
的形式,表示从ind1
开始到ind2
结束,按照step
的步进切片(常用的是[::-1]
,表示从最后一个元素到第一个元素切片):
a[::-1] # array([4, 3, 2, 1])
a[::2] # array([1, 3])
a[1::2] # array([2, 4])
a[1::-1] # array([2, 1])
a[1:2:-1] # array([]), empty array
注意:如果ind1, ind2
都不为空,必须满足step>0
,则ind1<ind2
,step<0
,则ind1>ind2
。否则切片操作将出现空数组。
多维切片
切片操作可用于多维数组的任意维度上:
import numpy as np
b = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
b[:, 1:] # array([[2, 3, 4], [6, 7, 8]])
索引+切片
索引和切片共同使用:
import numpy as np
b = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
b[:, 1] # array([2, 6])
list索引+切片
list索引和切片共同使用:
import numpy as np
b = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
b[:, [1, 3]] # array([[2, 4], [6, 8]])
…的作用
numpy中,...
也被用于数组的索引操作,其作用是根据...
在索引中的位置,自动推断获取位置的区域。
上面说的比较难以理解,还是举例:
import numpy as np
c = np.arange(24).rehsape([2,3,4])
# 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]]])
c[...] # equal to c
c[..., 1] # array([[ 1, 5, 9],
# [13, 17, 21]])
c[..., 1, 1] # array([ 5, 17])
c[1, ...] # array([[12, 13, 14, 15],
# [16, 17, 18, 19],
# [20, 21, 22, 23]])
c[1, 1, ...] #array([16, 17, 18, 19])
看上去不那么直观的样子,你可以这么理解:从索引中的...
处开始向左右添加:
,直到索引的长度与数组维度相同或左右都出现了index,打个比方:
# 向左添加':' 向左添加':' 索引长度与数组维度相同,停止添加
c[..., 1] ---------> c[:, 1] -------->c[:, :, 1]
# 向右添加':' 向右添加':' 索引长度与数组维度相同,停止添加
c[1, ...] ---------> c[1, :] -------->c[1, :, :]
验证一下:
c[:,:,1]
# array([[ 1, 5, 9],
# [13, 17, 21]])
c[1,:,:]
# array([[12, 13, 14, 15],
# [16, 17, 18, 19],
# [20, 21, 22, 23]])
果然没错。现在是不是就感觉...
非常简单了呢,就是代替连续重复的:
,比较简洁。