简介
索引和切片是NumPy中最重要最常用的操作。熟练使用NumPy切片操作是数据处理和机器学习的前提,所以一定要掌握好。
一维数组切片操作
从表面上看,一维数组的切片操作和python列表相似,索引都是从0开始。
代码演示:
1 2 3 4 5 6 7 8 |
>>>import numpy as np >>>a = np.arange(5) >>>a array([0, 1, 2, 3, 4]) >>>a[3] 3 >>>a[2:] array([2, 3, 4]) |
还可以将标量赋值给一个切片,此时会将该值自动赋值到整个选区,比如:
1 2 3 |
>>>a[2:4] = 5 >>>a array([0, 1, 5, 5, 4]) |
这个功能叫做“广播”。
另外,数组跟列表的的区别还在于,列表切片后是对源数据的复制,改变切片值不会影响到原来的列表;而数组不一样,数组切片后依然是源数据,改变切片值会影响到原来的数组。
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 列表 >>>a = [1, 2, 3, 4, 5] >>>b = a[3:] >>>b [4, 5] >>>b[0] = 8 >>>b [8, 5] >>>a [1, 2, 3, 4, 5] # 原来的列表不变 # 数组 >>>import numpy as np >>>c = np.array([1, 2, 3, 4, 5]) >>>d = c[3:] >>>d array([4, 5]) >>>d[0] = 8 >>>d array([8, 5]) >>>c array([1, 2, 3, 8, 5]) # 原数组也跟着改变 |
可以这样认为,NumPy处理的数组都很大,如果每次操作都将数据复制的话,会大大降低效率。但是如果想复制数组的话,就必须要显示复制操作。如下:
1 |
>>>e = c[3:].copy() |
这样就不会改变原来的数组了。
多维数组
NumPy强大的地方就是能够进行多维数组的快速运算。判断一个多维数组的大小是根据每条轴上的数量决定的:
1 |
数组大小:(第0轴,第1轴,第2轴...) |
我们可以根据[]
的数量计算维度的大小。
代码演示:
1 2 3 4 5 6 7 |
>>>import numpy as np >>>a = np.array([[1, 2, 3], [4, 5, 6]]) >>>a array([[1, 2, 3], [4, 5, 6]]) >>>a.shape (2, 3) |
在这里,我们创建了一个大小为(2, 3)的数组,也就是说在第0轴上有两行数[1, 2, 3]和[4, 5, 6],而在第1轴上有3列数[1, 4]、[2, 5]和[3, 6]。或者这样来看,从前面开始数有几个[
括号,表示几维。第一个[
包含的数据数量为第0维的大小,第二个[
包含的数据数量为第1维的大小。在这里第一个[
包含两个数据[1, 2, 3]、[4, 5, 6],第二个[包含3个数据。所以大小为(2, 3)。以此类推,我们就可以得到高维数据的大小了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
>>>a = np.randn(4, 3, 2) >>>a array([[[ 0.31667049, 0.16115283], [ 0.55099288, -1.12684939], [ 1.09766476, 0.59086491]], [[-1.77166868, -0.58705686], [-1.25394627, 0.43312151], [-0.50578369, -0.66472546]], [[-0.71483608, 0.5064243 ], [ 1.08001803, -0.38176132], [-0.18116786, 0.52656567]], [[ 1.59817834, -0.37466428], [ 0.27814287, -1.29082943], [-0.45922244, -0.5046503 ]]]) |
这里通过随机数函数randn()
生成了一个三维的数组。
访问多维数组中的元素
有两种方式访问多维数组中的元素。
代码演示:
1 2 3 4 5 6 7 8 9 10 |
>>>import numpy as np >>>a = np.arange(12).reshape(3, 4) >>>a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>>a[2][0] # 第一种方式 8 >>>a[2, 0] # 第二种方式 8 |
第一种方式通过递归访问,首先a[2]
将得到array([8, 9, 10, 11])
,然后a[2][0]
访问索引为0的数字,得到8;如果对数组的大小理解清楚了,那么第二种方式就是非常直接的指定了该数据在第0轴的索引2位置和第1轴的索引0位置。
为了加深印象,我们再举一个例子:
1 2 3 4 5 6 7 8 9 10 |
>>>import numpy as np >>>a = np.arange(12).reshape(2, 2, 3) >>>a array([[[ 0, 1, 2], [ 3, 4, 5]], [[ 6, 7, 8], [ 9, 10, 11]]]) >>>a[1, 0] >>>a array([6, 7, 8]) |
此时构建了一个三维的数组,我们只要返回第0轴上索引为1,第1轴上索引为0的数据,也就得到了一个数组array([6, 7, 8])
。
切片索引
明白高维数组每个轴的索引关系,那么高维数组的切片索引就很好理解了。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
>>>import numpy as np >>>a = np.arange(12).reshape(2, 2, 3) >>>a array([[[ 0, 1, 2], [ 3, 4, 5]], [[ 6, 7, 8], [ 9, 10, 11]]]) >>>a[1, 1, 1:] array([10, 11]) >>>a[:, :, 1] array([[ 1, 4], [ 7, 10]]) >>>a[:, :, 1] = 0 >>>a array([[[ 0, 0, 2], [ 3, 0, 5]], [[ 6, 0, 8], [ 9, 0, 11]]]) |
同样可以通过赋值操作“广播”数据。
布尔型索引
1 2 3 4 5 6 |
>>>import numpy as np >>>a = np.array(['www', 'finthon', 'com']) >>>a array(['www', 'finthon', 'com'], dtype='<U7') >>>a == 'finthon' array([False, True, False]) |
通过布尔型数组可以进行数组索引:
1 2 3 4 5 6 7 |
>>>b = np.arange(12).reshape(3, 4) >>>b array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>>b[a == 'finthon'] array([[4, 5, 6, 7]]) |
布尔型数组的长度必须跟被索引的轴长度一致。布尔型数组还能和切片合用:
1 2 |
>>>b[a == 'finthon', 2] array([6]) |
花式索引
花式索引是指通过整数数组进行索引。
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
>>>import numpy as np >>>a = np.arange(12).reshape(3, 4) >>>a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>>b = a[[2, 0, 1]] # 复制到新的数组,不影响原来的数组 >>>b array([[ 8, 9, 10, 11], [ 0, 1, 2, 3], [ 4, 5, 6, 7]]) >>>a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>>a[[1, 1, 2, 2], [1, 3, 0, 2]] array([ 5, 7, 8, 10]) |
需要注意的是,花式索引跟切片不同,总是将数据复制到新的数组。通过在索引轴上传入整数数组,可以精确地提取出某个数据。
总结
本文详细地介绍了NumPy中的数组索引和切片方法,要根据所需灵活使用;搞清楚多维数组每个轴的索引方式,配合花式索引会有意想不到的效果。