第二章 NumPy
基础2
本章示例代码中的输入和输出均来自IPython会话。
2.4 一维数组的索引和切片
一维数组的切片操作与Python列表的切片操作很相似。例如,我们可以用下标3~7
来选取元素3~6
:
In [27]: a = np.arange(9)
In [28]: a[3:7]
Out[28]: array([3, 4, 5, 6])
也可以用下标0~7
,以2
为步长选取元素:
In [29]: a[:7:2]
Out[29]: array([0, 2, 4, 6])
和Python中一样,我们也可以利用负数下标翻转数组:
In [30]: a[::-1]
Out[30]: array([8, 7, 6, 5, 4, 3, 2, 1, 0])
2.5 动手实践:多维数组的切片和索引
ndarray
支持在多维数组上的切片操作。为了方便起见,我们可以用一个省略号(...
)来表示遍历剩下的维度。
(1) 举例来说,我们先用arange
函数创建一个数组并改变其维度,使之变成一个三维数组:
In [31]: b = np.arange(24).reshape(2,3,4)
In [32]: b.shape
Out[32]: (2, 3, 4)
In [33]: b
Out[33]:
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]]])
多维数组b
中有0~23
的整数,共24个元素,是一个2×3×4
的三维数组。我们可以形象地把它看做一个两层楼建筑,每层楼有12个房间,并排列成3行4列。或者,我们也可以将其看成是电子表格中工作表(sheet)、行和列的关系。你可能已经猜到,reshape
函数的作用是改变数组的“形状”,也就是改变数组的维度,其参数为一个正整数元组,分别指定数组在每个维度上的大小。如果指定的维度和数组的元素数目不相吻合,函数将抛出异常。
(2) 我们可以用三维坐标来选定任意一个房间,即楼层、行号和列号。例如,选定第1层楼、第1行、第1列的房间(也可以说是第0层楼、第0行、第0列,这只是习惯问题),可以这样表示:
In [34]: b[0,0,0]
Out[34]: 0
(3) 如果我们不关心楼层,也就是说要选取所有楼层的第1行、第1列的房间,那么可以将第1个下标用英文标点的冒号:
来代替:
In [35]: b[:,0,0]
Out[35]: array([ 0, 12])
In [36]: b[0]
Out[36]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
我们还可以这样写,选取第1层楼的所有房间:
I
n [37]: b[0, :, :]
Out[37]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
多个冒号可以用一个省略号(...
)来代替,因此上面的代码等价于:
In [38]: b[0, ...]
Out[38]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [39]: b[...,0]
Out[39]:
array([[ 0, 4, 8],
[12, 16, 20]])
In [40]: b[0,...,0]
Out[40]: array([0, 4, 8])
进而可以选取第1层楼、第2排的所有房间:
In [41]: b[0,1]
Out[41]: array([4, 5, 6, 7])
(4) 再进一步,我们可以在上面的数组切片中间隔地选定元素:
In [42]: b[0,1,::2]
Out[42]: array([4, 6])
(5) 如果要选取所有楼层的位于第2列的房间,即不指定楼层和行号,用如下代码即可:
In [43]: b[...,1]
Out[43]:
array([[ 1, 5, 9],
[13, 17, 21]])
类似地,我们可以选取所有位于第2行的房间,而不指定楼层和列号:
In [44]: b[:,1]
Out[44]:
array([[ 4, 5, 6, 7],
[16, 17, 18, 19]])
如果要选取第1层楼的所有位于第2列的房间,在对应的两个维度上指定即可:
In [45]: b[0,:,1]
Out[45]: array([1, 5, 9])
(6) 如果要选取第1层楼的最后一列的所有房间,使用如下代码:
In [46]: b[0,:,-1]
Out[46]: array([ 3, 7, 11])
如果要反向选取第1层楼的最后一列的所有房间,使用如下代码:
In [47]: b[0,::-1, -1]
Out[47]: array([11, 7, 3])
在该数组切片中间隔地选定元素:
In [48]: b[0,::2,-1]
Out[48]: array([ 3, 11])
如果在多维数组中执行翻转一维数组的命令,将在最前面的维度上翻转元素的顺序,在我们
的例子中将把第1层楼和第2层楼的房间交换:
In [49]: b[::-1]
Out[49]:
array([[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]],
[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]]])
2.6 动手实践:改变数组的维度
我们已经学习了怎样使用reshape
函数,现在来学习一下怎样将数组展平。
(1) ravel
我们可以用ravel
函数完成展平的操作:
In [50]: b
Out[50]:
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]]])
In [51]: b.ravel()
Out[51]:
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])
In [52]: b
Out[52]:
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]]])
(2) flatten
这个函数恰如其名, flatten
就是展平的意思,与ravel
函数的功能相同。
不过,flatten
函数会请求分配内存来保存结果,而ravel
函数只是返回数组的一个视图(view)
In [53]: b.flatten()
Out[53]:
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])
In [54]: b
Out[54]:
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]]])
注:flatten 展平后是原来数据的copy 但是ravel 是原来数据的视图:
In [61]: a=np.array([[1,2],[3,4]])
In [62]: a.flatten()
Out[62]: array([1, 2, 3, 4])
In [63]: a.flatten()[0]=2
In [64]: a
Out[64]:
array([[1, 2],
[3, 4]])
In [65]: a.ravel()
Out[65]: array([1, 2, 3, 4])
In [66]: a.ravel()[0]=2
In [67]: a
Out[67]:
array([[2, 2],
[3, 4]])
(3) 用元组设置维度
除了可以使用reshape
函数,我们也可以直接用一个正整数元组来设置数组的维度,如下所示:
In [68]: b.shape = (6,4)
In [69]: b
Out[69]:
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]])
正如你所看到的,这样的做法将直接改变所操作的数组,现在数组b
成了一个6×4
的多维数组。
(4) transpose
在线性代数中, 转置矩阵是很常见的操作。对于多维数组,我们也可以这样做:
In [70]: b.transpose()
Out[70]:
array([[ 0, 4, 8, 12, 16, 20],
[ 1, 5, 9, 13, 17, 21],
[ 2, 6, 10, 14, 18, 22],
[ 3, 7, 11, 15, 19, 23]])
In [71]: b
Out[71]:
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]])
(5) resize
resize
和reshape
函数的功能一样,但resize
会直接修改所操作的数组:
In [72]: b.resize((2,12))
In [73]: b
Out[73]:
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]])
小结:我们用ravel
、flatten
、 reshape
和resize
函数对NumPy
数组的维度进行了修改。