基本的分片和索引操作
-
多维数组的分片操作:
>>> a = np.arange(27).reshape(3,3,3) >>> a 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]]])
对三维数组,就要在三个维度上去运用切片
>>> a[:,0,1] array([ 1, 10, 19])
: 代表第一个维度,全部都遍历。0表示第二个维度取第一个,即:[0,1,2],[9,10,11],[18,19,20],1表示第三个维度的第二个数字,就可以得到[1,10,19]
如果想取到1,4,7怎么办?
首先第一个维度先取第一个,即0,第二个维度是全部取的,因为每个都要。第三个维度取第二个数,即1。故:
>>> a[0,:,1] array([1, 4, 7])
除此之外,依旧可以取负值切片,如取7,4,1
>>> a[0,::-1,1] array([7, 4, 1])
高级索引
当方括号内的索引方式不再是之前介绍的简单序列的时候,就会触发高级索引。高级索引包括整数索引和布尔索引。
-
整数索引
对于一维数组而言,使用数组进行索引,返回的结果和索引数组的形状一样,不同的地方在于被索引数组的相关值会替代索引数组对应位置的值。
>>> test = np.arange(10) >>> test[np.array([[1,2],[3,4]])] array([[1, 2], [3, 4]])
由于1,2,3,4位置上的数字恰好是1,2,3,4。所以返回如上结果。
再看另一组例子,依旧用之前生成好的a:
>>> a 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]]])
如何取出0,13,26呢?
首先分析,这里是三维数组。在每一个维度上去取,第一维度上每一个都应该取到。即[0,1,2]都需要取到。
第二维度上,取到的分别是第一个[0,1,2],第二个[12,13,14],第三个[24,25,26]。然后我们分别取得是第一个:0,第二个:13,第三个:26。故
>>> a[[0,1,2],[0,1,2],[0,1,2]] array([ 0, 13, 26])
如果是6,10,20呢?同样分析后应该是
>>> a[[0,1,2],[2,0,0],[0,1,2]] array([ 6, 10, 20])
要和之前讲的索引数组进行区分
>>> a[np.array([[0,1,2],[2,0,0],[0,1,2]])] 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]]], [[[18, 19, 20], [21, 22, 23], [24, 25, 26]], [[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8]], [[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8]]], [[[ 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]]]])
我们有提到:返回的结果和索引数组的形状一样。这里的[0,1,2]代表的就是三维数组的第一维的第一个位置,是用我们a里的第三维的三块填充的(按照[0,1,2]三个部分的顺序)。
再看下一个例子:
>>> a
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]]])
>>> a[:,[2,0,0],[0,1,0]]
array([[ 6, 1, 0],
[15, 10, 9],
[24, 19, 18]])
这里可以看到我们的切片:和[0,1,2]是不一样的。(为啥?)
使用:时,先取第一维的第一个部分,再第二维的第三个部分[6,7,8],之后取第三维的第一个:6。之后,取二维的第一个部分[0,1,2]再取第三维的第二个:0。……
而使用[0,1,2]时,取过[6,7,8]中的6之后,直接跳到第一维的第二个部分 [[ 9, 10, 11],[12, 13, 14],[15, 16, 17]],不会继续在第一维第一部分循环。
-
布尔索引
当索引的方式是布尔数组的时候,会触发布尔索引。
首先我们可以使用random下面的and()函数生成一个10*4的均匀分布的二维数组,之后定义一个长度为10的一维数组,数组元素是一些人名,每行数据和人名一一对应
>>> data = np.random.rand(40).reshape(10,4) >>> data array([[0.75224117, 0.07255487, 0.15486979, 0.99832024], [0.89716759, 0.45973014, 0.69035348, 0.41042202], [0.8642536 , 0.24061544, 0.28480594, 0.66261885], [0.63386443, 0.00841045, 0.99501351, 0.49271741]]) >>> name = np.array(['小明', '小张', '小王', '小李', '小宋', '李华', '小王', '小张', '小王', '小王']) array(['小明', '小张', '小王', '小李', '小宋', '李华', '小王', '小张', '小王', '小王'], dtype='<U2') >>> data[name=='小王'] array([[0.75224117, 0.07255487, 0.15486979, 0.99832024], [0.89716759, 0.45973014, 0.69035348, 0.41042202], [0.8642536 , 0.24061544, 0.28480594, 0.66261885], [0.63386443, 0.00841045, 0.99501351, 0.49271741]])
布尔索引的同时也可以进行分片操作
>>> data[name=='小王', 1:] array([[0.07255487, 0.15486979, 0.99832024], [0.45973014, 0.69035348, 0.41042202], [0.24061544, 0.28480594, 0.66261885], [0.00841045, 0.99501351, 0.49271741]])
也可以使用不等号!=去排除数据
>>> data[name!='小王'] array([[0.6472885 , 0.3815376 , 0.55129018, 0.27300518], [0.06804647, 0.38165689, 0.70944563, 0.95890571], [0.54024513, 0.86783983, 0.03735209, 0.80200385], [0.46726378, 0.73882054, 0.20409086, 0.94286587], [0.27076598, 0.58085051, 0.0307553 , 0.76344933], [0.20232403, 0.73624633, 0.26808471, 0.74315558]])
可以使用& | ~ (即:与、或、非)进行运算。
* 布尔索引的简单应用
可以用布尔索引去筛选出值为NAN的元素并剔除。
>>> x = np.array([[1,2],[np.nan,3],[np.nan,np.nan]]) >>> x array([[ 1., 2.], [nan, 3.], [nan, nan]]) >>> x[~np.isnan(x)] array([1., 2., 3.])
也可以对某些值进行修改。比如给数组内元素小于1的元素加上一个值。
>>> x = np.array([1,-1,-2,3]) >>> x[x<0] += 20 >>> x array([ 1, 19, 18, 3])
可以使用nonzero()来生成元素值非0的整数数组索引
>>> x = np.array([[1,2],[np.nan,3],[np.nan,np.nan]]) >>> x array([[ 1., 2.], [nan, 3.], [nan, nan]]) >>> x.nonzero() (array([0, 0, 1, 1, 2, 2], dtype=int64), array([0, 1, 0, 1, 0, 1], dtype=int64))
nan不是0,因此我们不是0的坐标为(0,0),(0,1),……,(2,0),(2,1)
布尔索引的数值必须和被索引数值对应轴的长度一样,否则会引发异常。