本文首发于“生信补给站”公众号 https://mp.weixin.qq.com/s/0-9zUTxZC6qefyjLyw1SSA
分裂
使用「分裂」函数有两种选择
- 有通用的 split
- 有专门的 hsplit, vsplit
用下面数组来举例:
arr = np.arange(25).reshape((5,5))print( arr )
[[ 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]]
split
和 concatenate() 函数一样,我们可以在 split() 函数里通过设定轴,来对数组沿着竖直方向分裂 (轴 0) 和沿着水平方向分裂 (轴 1)。
first, second, third = np.split(arr,[1,3])print( 'The first split is', first )print( 'The second split is', second )print( 'The third split is', third )
The first split is [[0 1 2 3 4]] The second split is [[ 5 6 7 8 9] [10 11 12 13 14]] The third split is [[15 16 17 18 19] [20 21 22 23 24]]
split() 默认沿着轴 0 分裂,其第二个参数 [1, 3] 相当于是个切片操作,将数组分成三部分:
- 第一部分 - :1 (即第 1 行)
- 第二部分 - 1:3 (即第 2 到 3 行)
- 第二部分 - 3: (即第 4 到 5 行)
hsplit, vsplit
vsplit() 和 split(axis=0) 等价,hsplit() 和 split(axis=1) 等价。一图胜千言:
为了和上面不重复,我们只看 hsplit。
first, second, third = np.hsplit(arr,[1,3])print( 'The first split is', first )print( 'The second split is', second )print( 'The third split is', third )
The first split is [[ 0] [ 5] [10] [15] [20]] The second split is [[ 1 2] [ 6 7] [11 12] [16 17] [21 22]] The third split is [[ 3 4] [ 8 9] [13 14] [18 19] [23 24]]
4.3 重复和拼接
重复 (repeat) 和拼接 (tile) 这两个操作本质都是复制
- 重复是在元素层面复制
- 拼接是在数组层面复制
重复
函数 repeat() 复制的是数组的每一个元素,参数有几种设定方法:
- 一维数组:用标量和列表来复制元素的个数
- 多维数组:用标量和列表来复制元素的个数,用轴来控制复制的行和列
标量
arr = np.arange(3)print( arr )print( arr.repeat(3) )
[0 1 2] [0 0 0 1 1 1 2 2 2]
标量参数 3 - 数组 arr 中每个元素复制 3 遍。
列表
print( arr.repeat([2,3,4]) )
[0 0 1 1 1 2 2 2 2]
列表参数 [2,3,4] - 数组 arr 中每个元素分别复制 2, 3, 4 遍。
标量和轴
arr2d = np.arange(6).reshape((2,3))print( arr2d )print( arr2d.repeat(2, axis=0) )
[[0 1 2] [3 4 5]] [[0 1 2] [0 1 2] [3 4 5] [3 4 5]]
标量参数 2 和轴 0 - 数组 arr2d 中每个元素沿着轴 0 复制 2 遍。
列表和轴
print( arr2d.repeat([2,3,4], axis=1) )
[[0 0 1 1 1 2 2 2 2] [3 3 4 4 4 5 5 5 5]]
列表参数 [2,3,4] 和轴 1 - 数组 arr2d 中每个元素沿着轴 1 分别复制 2, 3, 4 遍。
拼接
函数 tile() 复制的是数组本身,参数有几种设定方法:
- 标量:把数组当成一个元素,一列一列复制
- 形状:把数组当成一个元素,按形状复制
标量
arr2d = np.arange(6).reshape((2,3))print( arr2d )print( np.tile(arr2d,2) )
[[0 1 2] [3 4 5]] [[0 1 2 0 1 2] [3 4 5 3 4 5]]
标量参数 2 - 数组 arr 按列复制 2 遍。
形状
print( np.tile(arr2d, (2,3)) )
[[0 1 2 0 1 2 0 1 2] [3 4 5 3 4 5 3 4 5] [0 1 2 0 1 2 0 1 2] [3 4 5 3 4 5 3 4 5]]
标量参数 (2,3) - 数组 arr 按形状复制 6 (2×3) 遍,并以 (2,3) 的形式展现。
4.4 其他操作
本节讨论数组的其他操作,包括排序 (sort),插入 (insert),删除 (delete) 和复制 (copy)。
排序
排序包括直接排序 (direct sort) 和间接排序 (indirect sort)。
直接排序
arr = np.array([5,3,2,6,1,4])print( 'Before sorting', arr )arr.sort()print( 'After sorting', arr )
Before sorting [5 3 2 6 1 4] After sorting [1 2 3 4 5 6]
sort()函数是按升序 (ascending order) 排列的,该函数里没有参数可以控制 order,因此你想要按降序排列的数组,只需
print( arr[::-1] )
[6 5 4 3 2 1]
现在让人困惑的地方来了。
知识点
用来排序 numpy 用两种方式:
- arr.sort()
- np.sort( arr )
第一种 sort 会改变 arr,第二种 sort 在排序时创建了 arr 的一个复制品,不会改变 arr。看下面代码,用一个形状是 (3, 4) 的「二维随机整数」数组来举例,用整数是为了便于读者好观察排序前后的变化:
arr = np.random.randint( 40, size=(3,4) )print( arr )
[[24 32 23 30] [26 27 28 0] [ 9 14 24 13]]
第一种 arr.sort(),对第一列排序,发现 arr 的元素改变了。
arr[:, 0].sort() print( arr )
[[ 9 32 23 30] [24 27 28 0] [26 14 24 13]]
第二种 np.sort(arr),对第二列排序,但是 arr 的元素不变。
np.sort(arr[:,1])
array([ 14, 27, 32])
print( arr )
[[ 9 32 23 30] [24 27 28 0] [26 14 24 13]]
此外也可以在不同的轴上排序,对于二维数组,在「轴 0」上排序是「跨行」排序,在「轴 1」上排序是「跨列」排序。
arr.sort(axis=1)print( arr )
[[ 9 23 30 32] [ 0 24 27 28] [13 14 24 26]]
间接排序
有时候我们不仅仅只想排序数组,还想在排序过程中提取每个元素在原数组对应的索引(index),这时 argsort() 就派上用场了。以排列下面五个学生的数学分数为例:
score = np.array([100, 60, 99, 80, 91])idx = score.argsort()print( idx )
[1 3 4 2 0]
这个 idx = [1 3 4 2 0] 怎么理解呢?很简单,排序完之后分数应该是 [60 80 91 99 100],
- 60,即 score[1] 排在第0位, 因此 idx[0] =1
- 80,即 score[3] 排在第1 位, 因此 idx[1] =3
- 91,即 score[4] 排在第2 位, 因此 idx[2] =4
- 99,即 score[2] 排在第3 位, 因此 idx[3] =2
- 100,即 score[0] 排在第4 位, 因此 idx[4] =0
用这个 idx 对 score 做一个「花式索引」得到 (还记得上贴的内容吗?)
print( score[idx] )
[ 60 80 91 99 100]
再看一个二维数组的例子。
arr = np.random.randint( 40, size=(3,4) )print( arr )
[[24 32 23 30] [26 27 28 0] [ 9 14 24 13]]
对其第一行 arr[0] 排序,获取索引,在应用到所用行上。
arr[:, arr[0].argsort()]
array([[23, 24, 30, 32], [28, 26, 0, 27], [24, 9, 13, 14]])
这不就是「花式索引」吗?来我们分解一下以上代码,先看看索引。
print( arr[0].argsort() )
[2, 0, 3, 1]
「花式索引」来了,结果和上面一样的。
arr[:, [2, 0, 3, 1]]
array([[23, 24, 30, 32], [28, 26, 0, 27], [24, 9, 13, 14]])
插入和删除
和列表一样,我们可以给 numpy 数组
- 用insert()函数在某个特定位置之前插入元素
- 用delete()函数删除某些特定元素
a = np.arange(6)print( a )print( np.insert(a, 1, 100) )print( np.delete(a, [1,3]) )
[0 1 2 3 4 5] [ 0 100 1 2 3 4 5] [0 2 4 5]
复制
用copy()函数来复制数组 a 得到 a_copy,很明显,改变 a_copy 里面的元素不会改变 a。
a = np.arange(6)a_copy = a.copy()print( 'Before changing value, a is', a )print( 'Before changing value, a_copy is', a_copy )a_copy[-1] = 99print( 'After changing value, a_copy is', a_copy )print( 'After changing value, a is', a )
Before changing value, a is [0 1 2 3 4 5] Before changing value, a_copy is [0 1 2 3 4 5] After changing value, a_copy is [ 0 1 2 3 4 99] After changing value, a is [0 1 2 3 4 5]