2.1.3 索引和切片
与Python的内置列表相似,NumPy可以通过索引的方式获取NumPy数组中的某个元素,或者通过切片的方式获取数组中的一块数据。与原生Python一旦切片就需要重新创建列表的高开销不同,NumPy中引入了视图(View)的概念,避免了数组的重新创建,从而显著提高了效率。常用的索引和切片方法包括普通索引、切片和布尔索引。
1.普通索引
NumPy数组提供了与Python列表相同的普通索引方式,将每个维度的索引值单独放到一个方括号“[]”中,拼接多个维度的索引值来获得某一个元素值。例如,为了在一个3×4形状的NumPy数组中获得一个元素,可以使用代码2-8中所示的方式。
代码2-8 NumPy数组的普通索引
import numpy as np a = np.array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9,10,11]]) b = a[0][1] print("数组a的第0行第1列元素为:\n", b)
上述代码的输出结果为
数组a的第0行第1列元素为 1
除了支持Python原生的索引方式,NumPy也对基本的索引方式进行了扩展,可以通过将每个维度的索引值集中放到一个方括号中来获得对应的元素值。例如,下面的代码可以获得与代码2-8中相同的索引效果。
b = a[0, 1]
2.切片
NumPy中的切片用于获取NumPy数组的一块数据,其操作方式与Python列表中的切片很相似,均使用“[]”指定索引实现。代码2-9给出了NumPy数组的常用切片操作。
在代码2-9中,第1个切片操作展示了最完整的方式:每个维度上使用冒号“:”分隔起始位置、截止位置(切片时不包含该位置)与步长,逗号“,”用于区分不同维度上的切片操作,用省略号“…”表示切片操作遍历所有剩余的维度。第2个切片操作的效果与第1个的等价,但使用了更精简的切片方式:省略了起始位置(默认为0)、步长(默认为1)和省略号(默认遍历剩余所有维度)。
代码2-9 NumPy数组的常用切片操作
import numpy as np a = np.arange(24).reshape(2, 3, 4) #生成形状为(2,3,4)的数组 print("数组对象a:\n", a) b = a[0:1:1, 0:2:1, ...] #第1次切片:在第0个和第1个维度上进行切片 print("\n第1次切片的结果:\n", b) c = a[:1, :2] #第2次切片:更精简的切片方式 print("\n第2次切片的结果:\n", c)
上述代码的输出结果为
数组对象a: [[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [[12 13 14 15] [16 17 18 19] [20 21 22 23]]] 第1次切片的结果: [[[0 1 2 3] [4 5 6 7]]] 第2次切片的结果: [[[0 1 2 3] [4 5 6 7]]]
获得切片后,可以对其进行运算或赋值。例如,如果希望将数组对象a中的部分数据设置为0,可以在切片后直接执行赋值操作,示例代码如下:
a[:1, :2] = 0
3.布尔索引
除了提供与原生Python类似的索引和切片功能,NumPy还可提供直接的数组比较。数组比较以后会产生布尔值,这些布尔值可用于定位特定的元素并为其赋值。
代码2-10展示了使用单条件布尔索引进行数组操作的过程。其中,表达式a<=0产生一个形状与数组对象a相同的布尔型数组,它被用作布尔索引访问Ndarray对象a中的数据,并且只访问索引值为True的对应位置上的数据。
代码2-10 NumPy数组中的单条件布尔索引的使用
import numpy as np a = np.random.randint(-5, 6, size = (3, 4)) print("数组对象a的原始值:\n", a) index = (a <= 0) #单条件索引 print("单条件索引的布尔数组:\n", index) a[index] = 0 #将布尔索引取值为True的对应位置上的数据赋值为0 print("数组对象a的新值:\n", a)
上述代码的输出结果为
数组对象a的原始值: [[-2 -4 2 -1] [ 0 0 -4 -4] [ 4 0 -5 -5]] 单条件索引的布尔数组: [[ True True False True] [ True True True True] [False True True True]] 数组对象a的新值: [[0 0 2 0] [0 0 0 0] [4 0 0 0]]