對乙個序列雙重argsort的含義

2022-10-09 20:39:13 字數 4322 閱讀 6376

首先函式的定義比較簡潔:

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()函式的定義了。

我們在資料處理的時候時常會碰到一些需要得到乙個序列中最大值或最小值的問題,或者我們有時候希望能夠得到序列中第\(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大的值的序號

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

7

上述**演示了訪問序列\(a\)中第二大的值及其索引。

之所以要研究這個問題是因為本人曾經參加過某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

下面我將從理論上證明該結論。

設\(\\)為乙個序列,\(\mu(\)=\)為一種變換,記作\(\=\^1\),它將原序列按照從小到大的方式進行排序,並提取其原索引值生成乙個新的序列. 令\(\=\^1\),則易知\(\\)為集合\(\\)的乙個排列. 令\(\\}=\^\),代表對\(\\}\)做\(k\)次\(\mu\)變換後的序列,可知\(\forall k \in n^+, \\} \in a(n)\),其中\(a(n)\)代表由集合\(\\)中所有的元素進行排列所得到的所有序列的集合。

\[\forall k \in n^+ and\space\space\forall l,m \in[0,n)\cap n^+,

\\a_l^=m\space\space if \space and \space only \space if \space a_m^k = l,

\\a_m^=l\space\space if \space and \space only \space if \space a_l^ = m,

\\\rightarrow a_m^=l\iff a_l^ = m \iff a_m^k = l,

\\\rightarrow \forall k \in n^+ and\space\space\forall m \in[0,n)\cap n^+,a_m^=a_m^k,

\\\rightarrow \forall k \in n^+,\\}=\\}

\\\rightarrow \forall k \in n^+, \\}=\\},\\}=\\}

\]因此,我在理論上證明了上述結論是正確的。所以到這裡,我至少有一種寫法與a.argsort().argsort()等價,即在後面加上\(2k\)個argsort()。但對於這個」傷敵一千,自損一千二「的方法,我並不是很滿意,因為儘管,我已經把使用一次argsort()後,繼續迭代的所有情況都已經分析得十分透徹了,但我依然沒能找到兩次argsort()後的序列與原序列之間的關係,即上述定義下的\(\\)與\(\\)的關係。

由於\(\\)和\(\\)之間關係我已經十分清楚了,即\(a_m^=l\iff a_l^ = m\),因此問題的關鍵在於如何數量化\(\\)與\(\\)之間的關係。在第一節的例子中我已經提到了原序列中最大值的序號將被安置在一次argsort()變換後序列的末尾,而最小值的序號將被排列在變換後序列的開頭。因此我得到了這樣的數量關係:如果原序列\(\\)的索引為\(m\)的值在原序列中是第\(l\)小,其中\(m\in[0,n-1]\cap n^+\),且第0小為最小,那麼\(a_l^1=m, a_m^2=l\).即\(\\)的各個索引所對應的值為原序列相同索引所對應的值的從小到大位次。

接下來我只需要知道如何得到原序列各個值的從小到大位次即可。我只需要通過兩次對值排序便可以達到這一目的,**如下:

b = pd.series(pd.series(a.sort_values().index).sort_values().index)

b0 3

1 0

2 2

3 1

4 4

dtype: int64

非常可惜,我是在面試之後才徹底想清楚這一變化的含義,即\(\\)的各個索引所對應的值為原序列相同索引所對應的值的從小到大位次。不過也不虧啦,學會了一種函式,對今後的學習工作都有很大的幫助。

本文簡要分析了numpy.argsort()函式的定義和用途,並較為深入的**了一些簡單的用法,其中包含最大值、最小值訪問問題、以及雙重呼叫、多重迭代問題。總體來說,argsort()函式的功能是十分強大的,它成為了溝通序列值和其序號之間的一道橋梁,使得一些對序列索引的呼叫得以簡化。

判斷乙個序列是否是另外乙個序列的子串行的演算法

思路一 從子串行中挨個找,找到乙個之後,擷取子串行和母序列之前的,然後繼續遞迴自身 1.先分析下找不到的情況,如果母序列從頭找到尾,也沒有找到,則視為不是其子序列 2.繼續分析找到的情況,如果找到之後,並且子串行的長度為1,則代表都找完了。3.如果這時子串行仍然還沒有找完,則擷取後繼續呼叫自身。pu...

乙個簡單的雙重for迴圈的彙編語句解釋

includevoid show if j i printf d i show lfb24 pushl ebx 將 ebx壓入棧,ebx作為變數i的暫存器 subl 24,esp 棧頂指標減24 movl 0,ebx 先把i 0存到 ebx暫存器 jmp l2 無條件,直接跳轉到l2 l4 執行第二...

反序迭代乙個序列

在列表中,如果我們要將列表反向迭代通常使用reverse 但這個方法有個缺陷就是會改變列表。因此,我們推薦使用reversed 它會返回乙個迭代器。這裡,我們可以實現 reversed 解決反向 迭代問題。class floatrange def init self,start,end,step s...