学习笔记:由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()函数的功能是十分强大的,它成为了沟通序列值和其序号之间的一道桥梁,使得一些对序列索引的调用得以简化。

对一个序列双重argsort的含义的更多相关文章

  1. Gym 101064 D Black Hills golden jewels 【二分套二分/给定一个序列,从序列中任意取两个数形成一个和,两个数不可相同,要求求出第k小的组合】

    D. Black Hills golden jewels time limit per test 2 seconds memory limit per test 256 megabytes input ...

  2. Swift处理堆栈问题——给定两组序列,其中一个序列表示栈的push 顺序,判断另一个序列有没有可能是对应的pop 顺序

    题目:输入两个整数序列.其中一个序列表示栈的push 顺序,判断另一个序列有没有可能是对应的pop 顺序.为了简单起见,我们假设push 序列的任意两个整数都是不相等的.比如输入的push 序列是1. ...

  3. SGU 179 Brackets light(生成字典序的下一个序列)

    题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=179 解题报告:输入一个合法的括号串,求出这个括号串的字典序的下一个串.(认为'(' ...

  4. 把一个序列转换成严格递增序列的最小花费 CF E - Sonya and Problem Wihtout a Legend

    //把一个序列转换成严格递增序列的最小花费 CF E - Sonya and Problem Wihtout a Legend //dp[i][j]:把第i个数转成第j小的数,最小花费 //此题与po ...

  5. 把一个序列转换成非严格递增序列的最小花费 POJ 3666

    //把一个序列转换成非严格递增序列的最小花费 POJ 3666 //dp[i][j]:把第i个数转成第j小的数,最小花费 #include <iostream> #include < ...

  6. 第29题:推断一个序列是否是还有一个push序列的pop序列

    github:https://github.com/frank-cq/MyTest 第29题:输入两个整数序列,当中一个序列表示栈的push顺序,推断还有一个序列有没有可能是相应的pop顺序.为了简单 ...

  7. range([start], stop[, step]):产生一个序列,默认从0开始

    range([start], stop[, step]):产生一个序列,默认从0开始 >>> l = range(10) >>> l [0, 1, 2, 3, 4, ...

  8. hdu 1394 求一个序列的最小逆序数 单点增 区间求和

    题目的意思就好比给出一个序列 如:0 3 4 1 2 设逆序数初始n = 0: 由于0后面没有比它小的,n = 0 3后面有1,2 n = 2 4后面有1,2,n = 2+2 = 4: 所以该序列逆序 ...

  9. python的reduce函数的使用方法详解以及使用案例,相加,相乘(处理一个序列,然后把序列进程合并操作)

    1.求列表的数字相加之和,还是之前的习惯,写for循环来实现 num_1=[1,2,3,4,5,6,7,8,9] a=0 for n in num_1: #a=a+n a+=n print (a) C ...

随机推荐

  1. Oh My Life~

    作者:HChan 链接:https://zhuanlan.zhihu.com/p/47084162 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. Part 1 那年 ...

  2. How to Create DLL(Dynamic link library)

    该文章属于在YouTube视频上看到的,链接如下: https://www.youtube.com/watch?v=EmDJsl7C9-k&t=3s 1.创建一个工程并建立一个控制台程序 2. ...

  3. vivo互联网机器学习平台的建设与实践

    vivo 互联网产品团队 - Wang xiao 随着广告和内容等推荐场景的扩展,算法模型也在不断演进迭代中.业务的不断增长,模型的训练.产出迫切需要进行平台化管理.vivo互联网机器学习平台主要业务 ...

  4. git记不住用户名跟密码,每次提交拉取都需要再次输入

    问题:之前为了测试git提交的一个问题,选择不记住用户名跟密码,输入如下命令即可不记住 git credential-manager uninstall git update-git-for-wind ...

  5. MyBatis(入参的类型和日志记录)

    入参的类型是对象 1. 新增的参数是对象 2. 空值的处理,占位符 字段,jdbcType=VARCHAR          字符串 字段,jdbcType=DATE                  ...

  6. Python凯撒密码加解密

    #凯撒密码第一个版本 #加密 pxpt=input("请输入明文文本:") for p in pxpt: if 'a'<=p<='z': print(chr(ord(' ...

  7. JS---HelloWorld

    1.功能效果图 2.代码实现 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  8. CompareTest

    一.说明:Java中的对象,正常情况下,只能进行比较:== 或 != .不能使用 > 或 < 的 但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小. 如何实现? ...

  9. IDEA& Android Studio 配置

    1.配置环境 首先要安装好JDK,但不需要单独下载SDK,只需在IDEA或AS的"设置->外观与行为->->系统设置->Android SDK"中下载相应版 ...

  10. python关于Django搭建简单博客项目 详解二-setting.py

    这一篇我们来讲解setting.py,具体内容以注释形式写入到下面的setting.py代码中,篇幅所限已把官方所给英文注释删除. 全部源代码和详解请参看http://github.com/Cheng ...