【Numpy学习13】排序,搜索和计数

1.排序

NumPy 提供了多种排序的方法。 这些排序函数实现不同的排序算法,每个排序算法的特征在于执行速度,最坏情况性能,所需的工作空间和算法的稳定性。 下表显示了三种排序算法的比较:

种类速度最坏情况工作空间稳定性
‘quicksort’(快速排序)1O(n^2)0
‘mergesort’(归并排序)2O(n*log(n))~n/2
‘heapsort’(堆排序)3O(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/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值