1.排序
NumPy 提供了多种排序的方法。 这些排序函数实现不同的排序算法,每个排序算法的特征在于执行速度,最坏情况性能,所需的工作空间和算法的稳定性。 下表显示了三种排序算法的比较:
种类 | 速度 | 最坏情况 | 工作空间 | 稳定性 |
---|---|---|---|---|
‘quicksort’(快速排序) | 1 | O(n^2) | 0 | 否 |
‘mergesort’(归并排序) | 2 | O(n*log(n)) | ~n/2 | 是 |
‘heapsort’(堆排序) | 3 | O(n*log(n)) | 0 | 否 |
1)numpy.sort()
参数说明:
- a: 要排序的数组
- axis: 沿着它排序数组的轴,如果是
axis = None
数组会被展开,沿着最后的轴排序, 默认axis = -1
- kind: 默认为’quicksort’(快速排序)
- order: 如果数组包含字段,则是要排序的字段
举例说明:
【例1-1】sort()方法举例
>>> x = np.random.randint(1,20,[5,5])
>>> print(x)
[[13 11 19 17 7]
[10 3 17 2 4]
[12 3 11 4 9]
[12 10 5 2 3]
[12 16 3 17 9]]
>>> print(np.sort(x))
[[ 7 11 13 17 19]
[ 2 3 4 10 17]
[ 3 4 9 11 12]
[ 2 3 5 10 12]
[ 3 9 12 16 17]]
>>> print(np.sort(x,axis=None))
[ 2 2 3 3 3 3 4 4 5 7 9 9 10 10 11 11 12 12 12 13 16 17 17 17
19]
>>> print(np.sort(x,axis=0))
[[10 3 3 2 3]
[12 3 5 2 4]
[12 10 11 4 7]
[12 11 17 17 9]
[13 16 19 17 9]]
>>> print(np.sort(x,axis=1))
[[ 7 11 13 17 19]
[ 2 3 4 10 17]
[ 3 4 9 11 12]
[ 2 3 5 10 12]
[ 3 9 12 16 17]]
#探究order参数的作用
>>> dt = np.dtype([('name', 'S10'),('age', int)])
>>> a = np.array([("ZhangSan",27),("LiSi",29),("WangWu",20), >>> ("ZhaoLiu",18)], dtype = dt)
>>> print(a)
[(b'ZhangSan', 27) (b'LiSi', 29) (b'WangWu', 20) (b'ZhaoLiu', 18)]
>>> print(np.sort(a,order='age'))
[(b'ZhaoLiu', 18) (b'WangWu', 20) (b'ZhangSan', 27) (b'LiSi', 29)]
axis = 0
的含义是沿着行去排序,最终的效果是将每列排序;axis = 0
恰恰相反。根据打印结果,我们可以清楚地验证默认axis = -1
。
在【例1-1】第二个例子中,我们可以看到order的使用方法,即按指定的属性(年龄)进行排序。
如果排序后,想用元素的索引位置替代排序后的实际结果,该怎么办呢?
2)numpy.argsort()
仔细观察,我们可以看到argsort()
和sort()
的参数基本上是一致,唯一的区别是argsort()
返回的是排序后的元素在原数组的index数组,具体让我们来举例说明:
【例1-2】argsort()方法举例
>>> x = np.random.randint(1,20,[5,5])
>>> print(x)
[[11 4 13 19 10]
[16 7 14 19 12]
[11 4 17 4 15]
[15 3 14 8 9]
[ 8 6 6 7 11]]
>>> print(np.argsort(x))
[[1 4 0 2 3]
[1 4 2 0 3]
[1 3 0 4 2]
[1 3 4 2 0]
[1 2 3 0 4]]
>>> print(np.argsort(x,axis=None))
[16 1 13 11 22 21 6 23 20 18 19 4 0 24 10 9 2 17 7 14 15 5 12 8
3]
>>> print(np.argsort(x,axis=0))
[[4 3 4 2 3]
[0 0 0 4 0]
[2 2 1 3 4]
[3 4 3 0 1]
[1 1 2 1 2]]
>>> print(np.argsort(x,axis=1))
[[1 4 0 2 3]
[1 4 2 0 3]
[1 3 0 4 2]
[1 3 4 2 0]
[1 2 3 0 4]]
#根据argsort()结果对x进行排序
>>> print(np.sort(x))
[[ 4 10 11 13 19]
[ 7 12 14 16 19]
[ 4 4 11 15 17]
[ 3 8 9 14 15]
[ 6 6 7 8 11]]
>>> y = np.array([np.take(x[i], np.argsort(x[i])) for i in range(5)])
>>> print(y)
[[ 4 10 11 13 19]
[ 7 12 14 16 19]
[ 4 4 11 15 17]
[ 3 8 9 14 15]
[ 6 6 7 8 11]]
我们可以看到它的用法和sort()
方法是类似的,只不过返回的数组有区别,以[11 4 13 19 10]
为例,它的排序结果是[ 4 10 11 13 19]
,其中结果数组每个元素在原数组的索引数组为:[ 1 4 0 2 3]
,即为argsort()
返回的结果。
另外,不可以使用x[np.argsort(x)]
返回排序后的数组,其返回值是5个乱序的x数组。
3)numpy.lexsort()
numpy.lexsort() 用于对多个序列进行排序。把它想象成对电子表格进行排序,每一列代表一个序列,排序时优先照顾靠后的列。
这里举一个应用场景:小升初考试,重点班录取学生按照总成绩录取。在总成绩相同时,数学成绩高的优先录取,在总成绩和数学成绩都相同时,按照英语成绩录取……
这里,总成绩排在电子表格的最后一列,数学成绩在倒数第二列,英语成绩在倒数第三列。
注意,numpy.lexsort()
返回的是索引数组,即和numpy.argsort()
类似,是是排序后的元素在原数组的index数组。
【例1-3】lexsort()方法举例
#1.探究要排序的输入结构
>>> x = np.random.randint(1,20,[5,5])
>>> print(x)
[[ 6 3 2 17 14]
[17 5 12 2 10]
[10 3 13 17 12]
[15 19 1 6 7]
[12 6 3 1 19]]
>>> y = [x[:,0]]
>>> print(y,y[0])
[array([ 6, 17, 10, 15, 12])] [ 6 17 10 15 12]
>>> print(np.lexsort(y))
[0 2 4 3 1]
#2.lexsort()方法真正的用途
>>> x = np.array([1, 5, 1, 4, 3, 4, 4])
>>> y = np.array([9, 4, 0, 4, 0, 2, 1])
>>> print(np.lexsort([y,x]))
[2 0 4 6 5 3 1]
>>> print(np.lexsort([x,y]))
[2 4 6 5 3 1 0]
先来说明例子中的1.,我们可以看到要输入进np.lexsort()
方法的数组必须外层有一层包裹,究其原因是因为np.lexsort()
要解决的是对n个序列的排序,而n = 1
只是一种特殊的情况,所以外面需要要加一层结构。
再来说例子中的2.,以np.lexsort([y,x])
为例:最小值为1,有2个,那么我们怎么判断先放哪个1,这时候就要根据这两个1的索引在y找到同一位置,判断在y的大小:index=0
的位置上是9,大于index=2
的位置上的0,所以第二个1放在前面。对于后面的4也是一样的方式,4的索引位置有三个,分别是3、5、6,对于在y的值为4,2,1,所以4在x的排放顺序为6、5、3,综上得出结果:[2 0 4 6 5 3 1]
。
4)numpy.partition()
numpy.partition()
的功能是,指定一个数,对数组进行分区:
首先进行一次从小到大的排序,以索引是 kth 的元素为基准,并将其固定到索引为k的位置,由此将元素分成两部分,即大于该元素的放在其后面,小于该元素的放在其前面,这里并不保证这两部分各自有序:
【例1-4】partition()方法举例
>>> x = np.random.randint(1,20,[6,4])
>>> print(x)
[[17 4 3 12]
[12 4 19 11]
[ 3 1 11 19]
[18 3 4 2]
[ 2 5 5 17]
[ 7 2 10 1]]
>>> print(np.sort(x,axis=0))
[[ 2 1 3 1]
[ 3 2 4 2]
[ 7 3 5 11]
[12 4 10 12]
[17 4 11 17]
[18 5 19 19]]
>>> print(np.partition(x, kth=2, axis=0))
[[ 2 1 3 1]
[ 3 2 4 2]
[ 7 3 5 11]
[18 4 19 19]
[17 5 11 17]
[12 4 10 12]]
>>> print(np.partition(x, kth=-2, axis=0))
[[ 2 1 4 2]
[ 3 2 3 1]
[ 7 3 5 11]
[12 4 10 12]
[17 4 11 17]
[18 5 19 19]]
注,这里的kth参数与a[kth]是等价的,就是索引的意思。
以kth=-2
为例,由于axis=0
,所以先按列从小到大排序,然后找到每列倒数第二小即第二大的数,即[17 4 11 17]
,然后将其固定在倒数第二行,并以此分块,小于他们的放在上面,大于或等于他们的放在下面。
注,其索引为kth的部分与np.sort()
结果中索引为kth的部分保持一致,而分出的另外两部分不保持严格有序,如:
第三行为基准行,下面那块数据是无序的。
5)numpy.argpartition()
我们查看官方文档后,可以看到numpy.argpartition()
与numpy.partition()
在返回数组这块区别较大,后者返回排序结果,而前者返回索引数组,它们之间的关系类似于numpy.sort()
和numpy.argsort()
的关系:
【例1-5】argpartition()方法举例
>>> x = np.random.randint(1,20,[6,4])
>>> print(x)
[[ 8 11 15 10]
[ 3 5 3 13]
[11 13 10 8]
[ 6 4 3 3]
[13 8 14 11]
[12 18 17 5]]
>>> print(np.argsort(x,axis=0))
[[1 3 1 3]
[3 1 3 5]
[0 4 2 2]
[2 0 4 0]
[5 2 0 4]
[4 5 5 1]]
>>> print(np.argpartition(x, kth=2, axis=0))
[[1 3 1 3]
[3 1 3 5]
[0 4 2 2]
[2 0 0 0]
[4 2 4 4]
[5 5 5 1]]
在这里,我们依然可以借鉴np.argsort()
的结果,将其与np.argpartition()
的结果进行对照,发现其第3行(索引为2)的数据仍保持一致。
2.搜索
1)numpy.argmax()、numpy.argmin()
由于argmax()和argmin()使用方法类似,所以这里以前者为重点进行介绍:
它返回的是根据axis=n为参考,沿着第n维度的最大值的索引,如果不设置axis,那么原数组会被平铺:
【例2-1】argmax()方法举例
>>> x = np.random.randint(1,10,[4,2])
>>> print(x,np.ravel(x))
[[4 8]
[6 3]
[7 4]
[8 9]] [4 8 6 3 7 4 8 9]
>>> print(np.argmax(x))
7
>>> print(np.argmax(x,axis=0))
[3 3]
>>> print(np.argmax(x,axis=1))
[1 0 0 1]
再来测验一下,如果有超过1个最大值,返回哪个索引:
>>> x[1][0] = 8
>>> print(x)
[[4 8]
[8 3]
[7 4]
[8 9]]
>>> print(np.argmax(x,axis=0))
[1 3]
我们可以发现是返回第一个出现的最大值。
2)numppy.nonzero()
numpy.nonzero() 函数返回输入数组中非零元素的索引:
【例2-2】nonzero()方法举例
#一维
>>> a = np.random.randint(0,2,3)
>>> print(a)
[1 1 0]
>>> print(np.nonzero(a))
(array([0, 1]),)
#二维
>>> a = np.random.randint(0,2,(3,3))
>>> print(a)
[[0 0 1]
[0 0 1]
[0 1 1]]
>>> print(np.nonzero(a))
>>> (array([0, 1, 2, 2]), array([2, 2, 1, 2]))
#三维
>>> a = np.random.randint(0,2,(2,2,2))
>>> print(a)
[[[1 0]
[1 1]]
[[0 1]
[0 0]]]
>>> b = np.nonzero(a)
>>> print(b)
(array([0, 0, 0, 1]), array([0, 1, 1, 0]), array([0, 0, 1, 1]))
>>> print(a[b])
[1 1 1 1]
- 只有
a
中非零元素才会有索引值,那些零值元素没有索引值 - 返回一个长度为
a.ndim
的元组(tuple),元组的每个元素都是一个整数数组(array) - 每一个array均是从一个维度上来描述其索引值。比如,如果
a
是一个二维数组,则tuple包含两个array,第一个array从行维度来描述索引值;第二个array从列维度来描述索引值。注意,一位数组返回的结果外层有一层元组包裹 - 通过
a[nonzero(a)]
得到所有a
中的非零值
3)numpy.where()
numpy.where() 函数返回输入数组中满足给定条件的元素的索引:
【例2-3】where()方法举例
>>> a = np.random.randint(0,10,10)
>>> print(a)
[8 3 1 2 3 9 8 3 2 6]
>>> print(np.where(a > 5))
(array([0, 5, 6, 9]),)
>>> a = np.random.randint(0,10,(2,10))
>>> print(a)
[[0 7 5 5 9 4 6 7 6 2]
[6 3 1 2 6 8 9 8 9 7]]
>>> print(np.where(a > 5))
(array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]), array([1, 4, 6, 7, 8, 0, 4, 5, 6, 7, 8, 9]))
注,此处返回元组与前面两个保持一致,当原始数组为一维时,仍需要在外面包裹一层元组。
4)numpy.searchsorted()
numpy.searchsorted() 函数返回输入数组中排序后的搜索结果:
- a:一维输入数组。当
sorter
参数为None
的时候,a
必须为升序数组;否则,sorter
不能为空,存放a
中元素的index
,用于反映a
数组的升序排列方式。 - v:插入
a
数组的值,可以为单个元素,list
或者ndarray
。 - side:查询方向,当为
left
时,将返回第一个符合条件的元素下标;当为right
时,将返回最后一个符合条件的元素下标。 - sorter:一维数组存放
a
数组元素的 index,index 对应元素为升序。
【例2-4】searchsorted()方法举例
>>> x = np.random.randint(0,40,8)
>>> print(x)
[14 17 38 1 29 33 27 17]
>>> x_sort = np.argsort(x)
>>> print(x_sort)
[3 0 1 7 6 4 5 2]
>>> print(x[x_sort])
[ 1 14 17 17 27 29 33 38]
>>> y = np.random.randint(0,40,6)
>>> print(y)
[ 8 37 14 1 12 7]
>>> z = np.searchsorted(x, y, sorter=x_sort)
>>> print(z)
[1 7 1 0 1 1]
如上,此时x为非升序,则sorter必须指定一个索引数组,存放x中元素的index,用于反映x数组的升序排列方式。
5)numpy.extract()
numpy.extract() 函数根据某个条件从数组中抽取元素,返回满条件的元素:
其中第一个参数condition是用来指明搜索条件,比如condition = np.mod(arr, 2)==0
可以用来搜索所有偶数:
【例2-5】extract()方法举例
>>> x = np.arange(9.).reshape(3, 3)
>>> print (x)
[[0. 1. 2.]
[3. 4. 5.]
[6. 7. 8.]]
>>> condition = np.mod(x,2) == 0
>>> print (condition)
[[ True False True]
[False True False]
[ True False True]]
>>> print (np.extract(condition, x))
[0. 2. 4. 6. 8.]
这样就能很快速的找到数组中的偶数了,当然在这个例题中我们也可以使用x[condition]
这种布尔切片来得到结果。
3.计数
1)numpy.count_nonzero()
numpy.count_nonzero() 函数返回输入数组中非零元素的个数:
【例3】count_nonzero()方法举例
>>> a = np.random.randint(-2,2,(3,3))
>>> print(a)
[[-2 -2 -2]
[ 0 0 1]
[ 0 0 0]]
>>> b = np.count_nonzero(a)
>>> print(b)
4
>>> b = np.count_nonzero(a,axis=0)
>>> print(b)
[1 1 2]
>>> b = np.count_nonzero(a,axis=1)
>>> print(b)
[3 1 0]
axis=0
依然代表是沿着行去查找非0元素,最终返回的是每列的非零元素个数。
参考文献
- https://www.runoob.com/numpy
- https://numpy.org/