numpy模块之数组的切片索引

时间:2022-06-04 21:21:40

我们先来看看一阶数组的切片索引

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]

高维数组的索引

在一个二维数组中,显而易见的是,各索引位置上的元素不再是标量而是一维数组

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

更高阶的数组

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]

和前面描述的类似,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]]]
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]]

布尔索引

我们先看一个例子,对一个数组进行==判定操作,会得到一个等维度的bool数组

import numpy as np  

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

[ True False False False  True]

这个得到的bool型数组可以用于数组索引,前提是bool型数组的长度和被索引数组的高维维度一致

import numpy as np  

names = np.array(['tom','bob','bill','jack','tom']) 
arr_bool = names == 'tom'  
data = np.random.rand(5,3) 
print(data) 
print(data[arr_bool])  

[[ 0.09769032  0.97245502  0.1762949 ]  
 [ 0.59949381  0.46735516  0.64142579]  
 [ 0.45941574  0.06747531  0.55780872]  
 [ 0.29714417  0.78494754  0.5194105 ]  
 [ 0.78078671  0.96654222  0.27489762]]

[[ 0.09769032  0.97245502  0.1762949 ]  
 [ 0.78078671  0.96654222  0.27489762]]

当然,显而易见的是,bool型数组也是可以和切片、整数索引混用的

import numpy as np  

names = np.array(['tom','bob','bill','jack','tom']) 
data = np.random.rand(5,3) 
print(data) 
print(data[names == 'tom',1:])  

[[ 0.16915065  0.15038684  0.34028728]  
 [ 0.32732646  0.69241855  0.65203056]  
 [ 0.84402175  0.77184234  0.48730057]  
 [ 0.94376757  0.33876278  0.77287885]  
 [ 0.58535219  0.50321862  0.28196336]]  

[[ 0.15038684  0.34028728]  
 [ 0.50321862  0.28196336]]

通过bool型索引选取数组中的数据,将总是创建数据的副本,而非视图

同时可以利用布尔型数组进行条件赋值,这也是会经常用到的

import numpy as np  

names = np.array(['tom','bob','bill','jack','tom']) 
data = np.random.rand(5,3) 
print(data) 
data[data<0.5]=0 
print(data)  

[[ 0.11395235  0.30461958  0.02896975]  
 [ 0.54829738  0.25386957  0.71327458]  
 [ 0.95840718  0.01013043  0.75231583]  
 [ 0.1600976   0.09872024  0.96804632]  
 [ 0.16516135  0.44308404  0.90036341]]  

[[ 0.          0.          0.        ]  
 [ 0.54829738  0.          0.71327458]  
 [ 0.95840718  0.          0.75231583]  
 [ 0.          0.          0.96804632]  
 [ 0.          0.          0.90036341]]

也可以利用一维bool数组进行整行、整列的赋值

import numpy as np  

names = np.array(['tom','bob','bill','jack','tom']) 
data = np.random.rand(5,3) 
print(data) 
data[names == 'tom']=0 
print(data)  

[[ 0.45025316  0.63122953  0.92612377]  
 [ 0.02918047  0.72361626  0.50190505]  
 [ 0.97611338  0.92807698  0.60883423]  
 [ 0.40124052  0.81103418  0.05447573]  
 [ 0.07352045  0.16173038  0.28114157]]  

[[ 0.          0.          0.        ]  
 [ 0.02918047  0.72361626  0.50190505]  
 [ 0.97611338  0.92807698  0.60883423]  
 [ 0.40124052  0.81103418  0.05447573]  
 [ 0.          0.          0.        ]]

关于多维数组的索引,我们最后看一种用法,利用整数数组进行索引:

按照整数数组中的索引值,来依次选取对应行

import numpy as np  

arr = np.empty((8,4)) 
for i in range(8):     
    arr[i] = i  
print(arr) 
print(arr[[4,3,0,6]])  

[[ 0.  0.  0.  0.]  
 [ 1.  1.  1.  1.]  
 [ 2.  2.  2.  2.]  
 [ 3.  3.  3.  3.]  
 [ 4.  4.  4.  4.]  
 [ 5.  5.  5.  5.]  
 [ 6.  6.  6.  6.]  
 [ 7.  7.  7.  7.]] 

[[ 4.  4.  4.  4.]  
 [ 3.  3.  3.  3.]  
 [ 0.  0.  0.  0.]  
 [ 6.  6.  6.  6.]]

在此基础上,再选取对应列,也是不难的

第一种情况是,针对每一行选取特定的列

import numpy as np

arr = np.arange(32).reshape(8,4)
print(arr)
print(arr[[1,5,7,2],[0,3,1,2]]) 

[[ 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 27]
 [28 29 30 31]]

[ 4 23 29 10]

可以看出,是在选取的特定四行后,再在每一行中挑选出指定的值,最后结果就是四个数

第二种使用场景是,在按照指定顺序选取行后,再按指定顺序选取整列

import numpy as np  

arr = np.arange(32).reshape(8,4) 
print(arr) 
print(arr[[4,3,0,6]][:,[0,3,1,2]])  
[[ 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 27]  
 [28 29 30 31]]  

[[16 19 17 18]  
 [12 15 13 14]  
 [ 0  3  1  2]  
 [24 27 25 26]]