【基本工具】S02E02 ndarray高维数组的索引和分片技巧

0.本集概览

1.ndarray高维数组的索引与切片
2.布尔索引与条件赋值
3.指定行和列的选取

1.ndarray数组的索引和切片

1.1.一维数组的情况

一维数组的相关内容非常简单,和我们之前讲过的python内置列表差不多,我们简单的浏览一下:
代码片段:

import numpy as np

arr = np.arange(10) 
print(arr) 
print(arr[5]) 
print(arr[5:8])  

运行结果:

[0 1 2 3 4 5 6 7 8 9] 
5 
[5 6 7]

特别需要注意的是,如果把一个标量值赋值给一个切片时,该值会自动传播给整个所选区域,而且这里数组的切片得到的是一个原始数组的一个视图,即原始数据并没有被复制而形成新的拷贝,视图上的任何修改都会直接反应到原始数组之上,这和python内置列表是有很大区别的。
代码片段:

import numpy as np  

arr = np.arange(10) 
arr[5:8] = 999 
print(arr)  

运行结果:

[  0   1   2   3   4 999 999 999   8   9]

代码片段:

import numpy as np  

arr = np.arange(10) 
temp = arr[5:8] 
temp[1] = 888 
print(arr)  

运行结果:

[  0   1   2   3   4   5 888   7   8   9]

这样做的原因是,numpy的设计目的是处理大数据,避免数据的反复复制有利于节省内存、提升性能。

如果执意要获得ndarray切片的一份副本而非视图,则需要显式的进行复制操作
代码片段:

import numpy as np  

arr = np.arange(10) 
temp = arr[5:8].copy() 
temp[1] = 888 
print(arr)  

运行结果:

[0 1 2 3 4 5 6 7 8 9]

1.2.高维数组索引

现在,我们重点说说高维数组的索引。在一个二维数组中,显而易见的是,各索引位置上的元素不再是标量而是一维数组:
代码片段:

import numpy as np  

arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]]) 
print(arr2d[2]) 
print(arr2d[0,1]) 
print(arr2d[0][1])  

运行结果:

[7 8 9] 
2
2

更高维的数组我们来举一个列子.高维数组的索引其实就是一个逐步去掉中括号[]的过程,我们来看一个2×2×3的例子:
代码片段:

import numpy as np  

arr3d = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]) 
print(arr3d[0]) 
print(arr3d[1][0]) 
print(arr3d[0][0][1]) 

运行结果:

[[1 2 3]  [4 5 6]]  
[7 8 9]  
2

我们看其中第二个例子,arr3d[1][0]:

第一个索引“1”:把[[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]最外层的中括号去掉,[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]],得到[[7,8,9],[10,11,12]],第二个索引“0”,再去掉一个中括号[7,8,9],[10,11,12],得到结果[7,8,9]

1.3.视图还是拷贝?

和前面描述的类似,ndarray利用索引返回的低维数组子集也是视图而非拷贝,获取拷贝也需要显式的调用copy函数。标量和等维的数组都可以对数据进行赋值
代码片段:

import numpy as np  

arr3d = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]) 
arr3d[0] = 111  
print(arr3d)  

运行结果:

[[[111 111 111]   [111 111 111]]   
 [[  7   8   9]   [ 10  11  12]]]

代码片段:

import numpy as np  

arr3d = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]) 
arr_cpy = arr3d[0].copy() 
arr3d[0] = 111  
print(arr3d) 

运行结果:

[[[111 111 111]   [111 111 111]]   
 [[  7   8   9]   [ 10  11  12]]]  

代码片段:

arr3d[0] = arr_cpy 
print(arr3d)  

运行结果:

[[[ 1  2  3]   [ 4  5  6]]   
 [[ 7  8  9]   [10 11 12]]]

1.4.高维数组切片

高维数组的索引弄明白了之后,我们再来看切片索引就非常好理解了,先看几个例子:
代码片段:

import numpy as np  

arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]]) 
print(arr2d[:2,1:])  

运行结果:

[[2 3]  [5 6]]

同理,从左到右的切片索引,就是从外到内处理中括号的过程,第一个“:2”高维度的切片,是对高维进行切片,即选取第0行和第1行(左闭右开),得到[[1,2,3],[4,5,6]]第二个“1:”则是在此基础上对低维进行切片,即第一列到最后一列。最终得到结果。

当然切片和整数索引是可以混用的,这个不难理解
代码片段:

import numpy as np  

arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]]) 
print(arr2d[:2,1])  

运行结果:

[2 5]

通过切片获取的也是原始数组的一个视图,并且对切片表达式进行赋值操作,也是扩散到整个选区
代码片段:

import numpy as np  

arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]]) 
arr2d[:2,1:] = 0 
print(arr2d)  

运行结果:

[[1 0 0]  [4 0 0]  [7 8 9]]

2.布尔索引

再说说布尔索引,我们先看一个例子,对一个数组进行==判定操作,会得到一个等维度的bool数组:
代码片段:
```
import numpy as np

names = np.array(['tom','bob','bill','jack','tom'])
arr_bool = names == 'tom' print(arr_bool)

top Created with Sketch.