学习笔记:由numpy.argsort()引发的思考

一、numpy.argsort() 函数定义

函数的定义

首先函数的定义比较简洁:

argsort()函数是将x中的元素从小到大排列,提取其对应的index(索引),然后输出到y。

实例解析

我将用一个例子来直观示意:

首先导入numpy和pandas,并随机定义一个序列变量\(a\):

import numpy as np
import pandas as pd
# 首先定义一个序列 a
a = pd.Series(np.random.choice(10,5, replace=False))

序列\(a\)输出结果如下:

0    7
1 1
2 6
3 4
4 8
dtype: int32

接着我对其使用一次argsort()函数后输出结果如下:

a.argsort()

0    1
1 3
2 2
3 0
4 4
dtype: int64

可以发现,原序列\(a\)中最小的数为1且其index为1,因此根据argsort()的定义,其index值被排在了最前面即,a.argsort()序列的index = 0处;与此同时,原序列中最大的值(8)所对应的index值(4)也在a.argsort()序列中被相应地排在了末尾(即index = 4)处。相信这个例子已经能够使读者清晰地明白argsort()函数的定义了。

  • 值得一提的是:原序列\(a\)和变化后序列a.argsort()的输出类型是不一样的。这是因为,两者形成机制是不一样的,原序列由np.choice生成的,而a.argsort()则是由pandas序列的index所生成的序列。

二、numpy.argsort()函数与最大最小值

我们在数据处理的时候时常会碰到一些需要得到一个序列中最大值或最小值的问题,或者我们有时候希望能够得到序列中第\(k\)大的值或第\(k\)小的值。可以考虑这样一个匹配问题:在一个由\(n\)位参与者构成的网络中,所有人都希望能够和能力更强的参与者匹配,且每个人最多只能与一位参与者进行匹配,这时会用到一个经典的匹配算法。即将参与者能力按照从小到大进行排序,最最强的参与者开始依次开始进行选取匹配的对象。在这样的算法中,我们很自然的就需要考虑上述的问题,最强的参与者的编号是多少?或者第\(k\)强的参与者编号是多少?numpy.argsort()函数为此类问题提供了一种便捷的解决方法。

这部分我参考了[月夜汐枫的文章](浅述python中argsort()函数的用法 - 2师兄不会胖 - 博客园 (cnblogs.com),我认为他在文章中的分析是很有启发性的。

下述代码可以直接得到序列\(a\)中最大值所对应的序号:

a.argsort()[len(a)-1] # a中最大值的序号

4

在本例中,最大值为8所对应的序号即为4,因此很自然可以通过下述代码得到序列中的最大值:

a[a.argsort()[len(a)-1]] # a中最大值

8

可以将这一写法进一步推广,假设要得到序列中第\(k\)大的值及其序号,只需要修改索引即可:

k =2

a.argsort()[len(a)-k] # a中第k大的值的序号

0

a[a.argsort()[len(a)-k]] # a中第k大值

7

上述代码演示了访问序列\(a\)中第二大的值及其索引。

三、函数双重引用的性质a.argsort().argsort()

一个双重引用的问题

之所以要研究这个问题是因为本人曾经参加过某985金融职业社团的选拔,尽管选拔结果不尽如人意,但其中笔试中所提到的这个问题激发了本人的研究兴趣,故而在此稍作讨论。

原题如图所示:

我依然沿用上述的例子,先看代码结果:

a.argsort().argsort()

0    3
1 0
2 2
3 1
4 4
dtype: int64

上述提到的题目中要求阐述该写法的含义,并提供等价写法。囿于本人眼拙,我实在无法一眼看出该序列与原序列之间存在怎样精巧的数学联系。只有在电脑上通过多个例的方式来寻找规律。

当函数迭代次数大于等于一次时的规律——理论推导

很快我发现,当我继续往后加argsort()时,所生成的新序列好像是有规律的。具体而言,从a.argsort()开始,每次在后面多加两个argsort(),输出的序列结果不变。

a.argsort().argsort().argsort()

0    1
1 3
2 2
3 0
4 4
dtype: int64 a.argsort().argsort().argsort().argsort() 0 3
1 0
2 2
3 1
4 4
dtype: int64

下面我将从理论上证明该结论。

设\(\{a_n^o\}\)为一个序列,\(\mu(\{a_n\})={b_n}\)为一种变换,记作\(\{b_n\}=\{a_n\}^1\),它将原序列按照从小到大的方式进行排序,并提取其原索引值生成一个新的序列. 令\(\{a_n^1\}=\{a_n^o\}^1\),则易知\(\{a_n^1\}\)为集合\(\{0,1,\dots,n-1\}\)的一个排列. 令\(\{a_n^{k}\}=\{a_n^1\}^{k}\),代表对\(\{a_n^{o}\}\)做\(k\)次\(\mu\)变换后的序列,可知\(\forall k \in N^+, \{a_n^{k}\} \in A(n)\),其中\(A(n)\)代表由集合\(\{0,1,\dots,n-1\}\)中所有的元素进行排列所得到的所有序列的集合。

\[\forall k \in N^+ and\space\space\forall l,m \in[0,n)\cap N^+,
\\
a_l^{k+1}=m\space\space if \space and \space only \space if \space a_m^k = l,
\\
a_m^{k+2}=l\space\space if \space and \space only \space if \space a_l^{k+1} = m,
\\
\Rightarrow a_m^{k+2}=l\iff a_l^{k+1} = m \iff a_m^k = l,
\\
\Rightarrow \forall k \in N^+ and\space\space\forall m \in[0,n)\cap N^+,a_m^{k+2}=a_m^k,
\\
\Rightarrow \forall k \in N^+,\{a_n^{k+2}\}=\{a_n^{k}\}
\\
\Rightarrow \forall k \in N^+, \{a_n^{2k+1}\}=\{a_n^{1}\},\{a_n^{2k}\}=\{a_n^{2}\}
\]

函数迭代两次后的序列与原序列的关系

因此,我在理论上证明了上述结论是正确的。所以到这里,我至少有一种写法与a.argsort().argsort()等价,即在后面加上\(2k\)个argsort()。但对于这个”伤敌一千,自损一千二“的方法,我并不是很满意,因为尽管,我已经把使用一次argsort()后,继续迭代的所有情况都已经分析得十分透彻了,但我依然没能找到两次argsort()后的序列与原序列之间的关系,即上述定义下的\(\{a_n^2\}\)与\(\{a_n^o\}\)的关系。

由于\(\{a_n^2\}\)和\(\{a_n^1\}\)之间关系我已经十分清楚了,即\(a_m^{2}=l\iff a_l^{1} = m\),因此问题的关键在于如何数量化\(\{a_n^1\}\)与\(\{a_n^o\}\)之间的关系。在第一节的例子中我已经提到了原序列中最大值的序号将被安置在一次argsort()变换后序列的末尾,而最小值的序号将被排列在变换后序列的开头。因此我得到了这样的数量关系:如果原序列\(\{a_n^o\}\)的索引为\(m\)的值在原序列中是第\(l\)小,其中\(m\in[0,n-1]\cap N^+\),且第0小为最小,那么\(a_l^1=m, a_m^2=l\). 即\(\{a_n^2\}\)的各个索引所对应的值为原序列相同索引所对应的值的从小到大位次。

接下来我只需要知道如何得到原序列各个值的从小到大位次即可。我只需要通过两次对值排序便可以达到这一目的,代码如下:

b = pd.Series(pd.Series(a.sort_values().index).sort_values().index)
b 0 3
1 0
2 2
3 1
4 4
dtype: int64

非常可惜,我是在面试之后才彻底想清楚这一变化的含义,即\(\{a_n^2\}\)的各个索引所对应的值为原序列相同索引所对应的值的从小到大位次。不过也不亏啦,学会了一种函数,对今后的学习工作都有很大的帮助。

四、总结

本文简要分析了numpy.argsort()函数的定义和用途,并较为深入的探讨了一些简单的用法,其中包含最大值、最小值访问问题、以及双重调用、多重迭代问题。总体来说,argsort()函数的功能是十分强大的,它成为了沟通序列值和其序号之间的一道桥梁,使得一些对序列索引的调用得以简化。

最新文章

  1. <精通JavaScript>---阅读笔记01
  2. Google Developing for Android 三 - Performance最佳实践
  3. UE4动作流程总结
  4. java输出任意两个日期之间有多少天
  5. Linux sort --copy
  6. B/S 和 C/S
  7. Swift学习笔记(一)搭配环境以及代码执行成功
  8. AD域设置
  9. [汇编学习笔记][第十七章使用BIOS进行键盘输入和磁盘读写
  10. DW8051调试终结
  11. C++数组的初始化
  12. COM接口调用,CreateDispatch失败的问题
  13. freenode configuration sasl authentication in weechat
  14. sale.order
  15. HTML JavaScript练习
  16. 获取APP的启动图 -Launch Image
  17. Tomcat学习(一)------部署Web应用方法总结
  18. POJ 2195 Going Home(KM算法模板)
  19. ubuntu 13.04 编译 安装 升级 gcc 4.9.0 address sanitizer
  20. 【pyhon】理想论坛爬虫1.05版,将读取和写DB分离成两个文件

热门文章

  1. 陆地观测卫星数据服务(CRESDA)订单ftp地址错误—已解决不能下载问题
  2. git 多个commit 如何合并
  3. 事件循环Event Loop
  4. go基础语法50问,来看看你的go基础合格了吗?
  5. 基于docker和cri-dockerd部署kubernetes v1.25.3
  6. 机器学习中in-domine, out-domine的区别
  7. python的list,dict,set
  8. yaml使用
  9. windows中 mysql 免安装版安装
  10. MindSpore Graph Learning