【Numpy学习07】索引与切片

前言

ndarray对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样。

1.整数索引

【例1】通过整数索引访问数组

>>> a = np.random.randint(0,10,5)
>>> print(a,a[2])
[2 7 3 6 6] 3
>>> b = np.random.randint(0,10,(5,5))
>>> print(b,b[3][1],b[3,1])
[[3 7 0 7 9]
 [1 6 9 3 1]
 [2 7 5 0 7]
 [6 2 6 1 0]
 [5 5 6 8 1]] 2 2
>>> c = np.random.randint(0,10,(3,3,3))
>>> print(c,c[0][0][0],c[0,0,0])
[[[6 1 5]
  [9 5 9]
  [2 3 5]]

 [[1 1 5]
  [2 1 5]
  [1 5 4]]

 [[3 5 9]
  [8 9 6]
  [7 3 3]]] 6 6

这里用了np.random.randint()方法生成了随机ndarray数组,然后利用整数索引进行访问。

注:[x][y][z] 与 [x,y,z]的效果相同

在这里插入图片描述

顾名思义,我们可以把切片理解成一系列切蛋糕的行为,第一个参数是第一刀,第二个参数是最后一刀后面的位置,第三个参数是刀与刀之间的距离,那么整数索引在二维数组就相当于与横纵都仅切1刀,且刀宽为1。

2.切片索引

我们在上一章知道,python切片获得原来数据的副本(深拷贝),而ndarray数据类型切片获得原数据的视图。

ndarray 数组可以基于 0 - n 的下标进行索引,切片对象可以通过内置的 slice 函数,并设置 start, stop 及 step 参数进行,从原数组中切割出一个新数组,也可以通过冒号分隔切片参数 start:stop:step 来进行切片操作。

【例2-1】单维度切片

#一维数组
>>> a = np.random.randint(0,10,5)
>>> print(a)
>>> print(a[:])
>>> print(a[:-2])
>>> print(a[1:])
>>> print(a[::2])
[8 6 4 8 6]
[8 6 4 8 6]
[8 6 4]
[6 4 8 6]
[8 4 6]

#二维数组
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[1 7 5 8 3]
 [1 7 8 7 3]
 [2 1 7 3 7]
 [3 3 4 1 4]
 [6 4 3 8 6]]
#打印倒数前2行之前的数组(不包括倒数第二行)
>>> print(a[:-2])
[[1 7 5 8 3]
 [1 7 8 7 3]
 [2 1 7 3 7]]
#步长为2打印整个数组
>>> print(a[::2])
[[1 7 5 8 3]
 [2 1 7 3 7]
 [6 4 3 8 6]]
#打印倒数前2列之前的数组(不包括倒数第二列)
>>> print(a[...,:-2])
[[1 7 5]
 [1 7 8]
 [2 1 7]
 [3 3 4]
 [6 4 3]]
#打印倒数前2行之前的数组(不包括倒数第二行)
>>> print(a[:-2,...])
[[1 7 5 8 3]
 [1 7 8 7 3]
 [2 1 7 3 7]]

二维数组单维度切片示意图如下:

在这里插入图片描述

上面的案例仅以一维、二维数组为例进行单维度切片(在二维数组中要么是行,要么是列),上升到多维数组后,行列就要换成第几维去描述。

我们注意到a[:-2]其实是和a[:-2,...]是等价的,那么就此引出dots:

NumPy 允许使用...表示足够多的冒号来构建完整的索引列表。

那么在上述案例中,:::其实就和...是等价。对于其他维度的数组,...可以起到一个补全的效果,我们接下来会在多维度切片中进行演示:

【例2-2】多维度切片

#二维数组
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[7 7 9 8 0]
 [7 9 9 9 9]
 [2 1 4 7 7]
 [4 9 7 3 8]
 [2 1 8 2 8]]
#第2行与第3、4列交汇的数据
>>> print(a[1,2:4])
[9 9]
#第2列与第3、4行交汇的数据
>>> print(a[2:4,1])
[1 9]
#第3、4行与第3、4列交汇的数据
>>> print(a[2:4,2:4])
[[4 7]
 [7 3]]
#从第一行开始以2为步长的行与从第一行开始以2为步长的列交汇的数据
>>> print(a[::2,::2])
[[7 9 0]
 [2 4 7]
 [2 8 8]]
#去掉最后一列
>>> print(a[...,:-1])
[[7 7 9 8]
 [7 9 9 9]
 [2 1 4 7]
 [4 9 7 3]
 [2 1 8 2]]

二维数组多维度切片示意图:

在这里插入图片描述

复杂版:
在这里插入图片描述

在这里比较好奇,如果是一行一列,返回的结果是怎样的呢?

>>> print(a[0,0])
7

是一个整数,切片切多了,忘了[0,0]其实就是整数索引 —_-!

3.整数数组索引

先简单看一个案例:

【例3-1】单维度整数数组索引

>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[0 3 2 8 0]
 [2 6 8 7 9]
 [4 0 5 0 5]
 [8 5 9 0 0]
 [1 9 8 8 5]]
>>> s = [0,2,4]
#取a的第1,3,5行
>>> print(a[s])
[[0 3 2 8 0]
 [4 0 5 0 5]
 [1 9 8 8 5]]

整数数组解决的是不连续切片且间隔是随机非等差的问题,上面的案例是操作行,我们也可以同时操作行和列:

【例3-2】多维度整数数组索引

>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[6 2 9 7 2]
 [8 5 4 5 9]
 [1 4 3 3 9
 [9 8 0 8 8]
 [7 1 6 5 2]]
>>> s = [0,2,3]
>>> p = [1,2,4]
>>> q = [1,2]
>>> print(a[s,p])
[2 3 8]
>>> print(a[s,q])
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (3,) (2,)

最后一句代码出现问题,原因是传入的两个index数组的数量必须相等,怎样理解,就是将整数索引重复n遍,那么两个index数组当然要相等。

二维数组多维度整数数组切片示意图:
在这里插入图片描述

假设a是一个ndarray的二维数组,p是一个一维list,那么a[p]的结果是一个ndarray二维数组。

若p也是一个ndarray数组呢?请看下面的例子:

【例3-3】索引标志为ndarray的整数数组索引(单维度)

#单维度情况
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[5 0 1 7 4]
 [3 9 2 5 1]
 [3 5 8 3 2]
 [5 4 4 6 5]
 [5 7 7 5 6]]
>>> p = np.array([[0,2],[2,3]])
>>> print(a[p])
[[[5 0 1 7 4]
  [3 5 8 3 2]]

 [[3 5 8 3 2]
  [5 4 4 6 5]]]

你会发现其实索引标志为ndarray时,其实就是多次整数数组索引,然后再将结果整合到一个ndarray中,在这种单维度的情况下:

目标数组二维+索引标志一维 = 结果数组二维
目标数组二维+索引标志二维 = 结果数组三维

显然,结果数组维数 = 目标数组维数 + 索引标志维数 - 1

【例3-4】索引标志为ndarray的整数数组索引(多维度)

>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[0 9 8 2 4]
 [7 8 7 2 0]
 [8 7 3 1 5]
 [9 8 4 8 2]
 [0 9 7 9 4]]
>>> p = np.array([[0,2],[2,3]])
>>> q = np.array([[0,4],[0,4]])
>>> print(a[p,q])
[[0 5]
 [8 2]]

其实可以把这种索引标志为ndarray的切片分解成多个子切片行为,即[a[p1,q1]+a[p2+q2]],如下图所示,求解过程可化解为2个普通多维度整数数组索引,如例3-2图所示。
在这里插入图片描述

易知,条件允许的情况下:

  • 目标数组维数 + 索引标志维数 - 2
  • 且多个索引标志维数必须相等,不然会报错

我们再来介绍一下take()函数:

numpy.take(a,indices,axis = None,out = None,mode =‘raise’ )

在这里插入图片描述

其中,当axis不是None时,此函数与使用数组索引数组的功能相同,但是若未指定axis时,会默认把目标数组维度打成一维,数据扁平化:

【例3-5】ndarray.take()的使用

>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[8 4 0 3 2]
 [0 0 1 0 8]
 [6 7 4 7 2]
 [0 7 9 6 2]
 [4 0 9 1 9]]
>>> print(np.take(a,[0,1,2],axis=0))
[[8 4 0 3 2]
 [0 0 1 0 8]
 [6 7 4 7 2]]
>>> print(np.take(a,[0,1,2],axis=1))
[[8 4 0]
 [0 0 1]
 [6 7 4]
 [0 7 9]
 [4 0 9]]
>>> print(np.take(a,[0,1,2]))
[8 4 0]

如上述案例所示,当axis=0时,取的是1-3行;当axis=1时,取的是1-3列;当axis未赋值时,数据被扁平化,取的是扁平化后的前三个数组元素。

4.布尔索引

与其称为布尔索引,不如理解成条件索引:

【例4-1】控制最小值及类型的布尔索引

#控制最小值
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[5 9 6 6 1]
 [0 0 4 0 0]
 [0 8 2 5 7]
 [8 0 2 0 0]
 [3 4 0 1 6]]
>>> b = a > 5
>>> print(b)
[[False  True  True  True False]
 [False False False False False]
 [False  True False False  True]
 [ True False False False False]
 [False False False False  True]]
>>> print(a[b])
[9 6 6 8 7 8 6]

#控制类型,找到不是nan的数据
>>> c = np.array([np.nan,9,8,7,np.nan,6,5])
>>> d = np.logical_not(np.isnan(c))
>>> print(d)
[False  True  True  True False  True  True]
>>> print(c[d])
[9. 8. 7. 6. 5.]

我们其实可以看出,最终带入的就是一个布尔数组b,然后利用a[b]去生成满足条件的数组。

【例4-2】布尔索引在matplotlib上的应用

>>> import matplotlib.pyplot as plt

#y = sinx函数图像 x在[0,2pi]之间,最小刻度为pi/20
>>> x = np.linspace(0, 2 * np.pi, 50)
>>> y = np.sin(x)
>>> print(len(x))  
>>> plt.plot(x, y)

#mask1
#mask1 = y >= 0
>>> mask1 = np.logical_and(y >= 0, x >= 0)
>>> plt.plot(x[mask1],y[mask1],'ro')


#mask2
>>> mask2 = np.logical_and(y <= 0, x >= 3*np.pi/2 )
>>> plt.plot(x[mask2],y[mask2],'yo')

在这里插入图片描述
解释一下np.logical_and的含义,这个需要同时满足参数条件,比如 np.logical_and(y >= 0, x >= 0)代表x,y需要同时大于0。

可以把mask理解成一个范式,把它放入x,y中会根据范式规则返回需要的数据。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值