- 對於數據分析來說,排序和篩選數據是不可或缺的一部分內容。NumPy 也提供了多種排序和篩選函數,本文就來介紹一下 NumPy 常見的排序和篩選函數。
-
排序函數
NumPy 中提供了排序相關的函數。排序函數已經幫助我們實現了不同的排序算法,我們只需要拿來直接使用就行。每個排序算法的執行速度,時間復雜度,空間復雜度和算法的穩定性都不相同,我們來看看常見的幾種排序算法的比較。
-
排序算法 速度 時間復雜度 空間復雜度 穩定性 quicksort(快速排序)1 o(n^2)0 否 mergesort(歸並排序)2 O(n*log(n))~n/2 是 heapsort(堆排序)3 O(n*log(n))0 否 -
numpy.sort(a, axis, kind, order)
這個排序函數有4個參數,我們來看看參數的說明:
-
參數 說明 a 要排序的數組 axis 排序數組的軸,如果沒有數組會被展開,沿着最后的軸排序。axis=0 按列排序,axis=1 按行排序 kind 排序類型,有 quicksort、mergesort、heapsort、stable幾種,默認為quicksort(快速排序)order 排序的字段,針對包含字段的數組 - 我們來看看實例:
-
import numpy as np import time a = np.array([[3, 7, 12, 45], [9, 1, 0, 34]]) print("初始數組:") print(a) print('\n') print(np.char.center('調用 sort() 函數,默認快速排序', 15, '*')) print(np.sort(a)) print('\n') print(np.char.center('按列排序', 15, '*')) print(np.sort(a, axis=0)) print('\n') b = np.random.randint(1, 1000, size=[10000, 10000]) print(np.char.center('快速排序時間', 15, '*')) t1 = time.time() np.sort(b) t2 = time.time() print(t2 - t1) print('\n') print(np.char.center('堆排序時間', 15, '*')) t3 = time.time() np.sort(b, -1, 'heapsort') t4 = time.time() print(t4 - t3) print('\n') print(np.char.center('歸並排序時間', 15, '*')) t5 = time.time() np.sort(b, -1, 'mergesort') t6 = time.time() print(t6 - t5) print('\n') # 根據字段排序 dt = np.dtype([('name', 'S10'), ('age', int)]) c = np.array([("raju", 21), ("anil", 25), ("ravi", 17), ("amar", 27)], dtype=dt) print(np.char.center('根據字段排序的數組', 15, '*')) print(c) print('\n') print(np.char.center('按 name 排序', 15, '*')) print(np.sort(c, order='name')) # 返回 初始數組: [[ 3 7 12 45] [ 9 1 0 34]] 調用 sort() 函數,默認 [[ 3 7 12 45] [ 0 1 9 34]] ******按列排序***** [[ 3 1 0 34] [ 9 7 12 45]] *****快速排序時間**** 5.470074892044067 *****堆排序時間***** 6.988600015640259 *****歸並排序時間**** 5.784327983856201 ***根據字段排序的數組*** [(b'raju', 21) (b'anil', 25) (b'ravi', 17) (b'amar', 27)] ***按 name 排序*** [(b'amar', 27) (b'anil', 25) (b'raju', 21) (b'ravi', 17)]
-
在例子中,我們首先使用了默認的按橫軸的快速排序算法,可以看到每個數組都是橫向排序的。
接下來,我們多加了一個排序的參數,表示按縱軸排序,我們可以從結果中看到,兩個數組中對應位置的元素都按照升序排列了。
接着我們隨機生成了一個數據量大的多維數組,然后使用三種排序方式,打印了它們排序的時間,從結果中我們可以看到快速排序最快,其次是歸並排序,最后是堆排序。需要注意一點的是,有些排序算法不穩定,可能會導致每次運行的結果不一樣。另外,數據量也可能會影響不同排序算法排序的效率。
最后我們創建了一個帶字段的數組,然后按照
name字段排序。 -
numpy.argsort()
函數對輸入數組沿給定軸執行間接排序,並使用指定排序類型返回數據的索引數組。這個索引數組用於構造排序后的數組。
我們來看實例:
-
import numpy as np a = np.array([3, 4, 2]) print("初始數組:") print(a) print('\n') print(np.char.center('調用 argsort() 函數', 15, '*')) b = np.argsort(a) print(b) print('\n') print(np.char.center('以排序后的順序重構原數組', 15, '*')) print(a[b]) print('\n') # 返回 初始數組: [3 4 2] 調用 argsort() 函數 [2 0 1] **以排序后的順序重構原數組* [2 3 4]
- 在上面例子中,我們調用
argsort()函數后,返回了初始數組的排序后的索引。然后我們用排序后的索引數組重構原數組,得到排序后的數組。 -
numpy.lexsort()
函數使用鍵序列執行間接排序。鍵可以看作是電子表格中的一列。該函數返回一個索引數組,使用它可以獲得排序數據。注意,最后一個鍵恰好是 sort 的主鍵。
對於這個函數,我們假設一種場景:現在有語文和數學考試成績以及總成績,我們需要對成績做個排序,排序原則為總分優先,總分相同則語文高的排前面。
實現的代碼如下:
-
import numpy as np print(np.char.center('lexsort() 函數', 15, '*')) # 錄入了四位同學的成績 math = (10, 20, 50, 10) chinese = (30, 50, 40, 60) total = (40, 70, 90, 70) # 將優先級高的項放在后面 ind = np.lexsort((math, chinese, total)) for i in ind: print(total[i], chinese[i], math[i]) # 返回 **lexsort() 函數* 40 30 10 70 50 20 70 60 10 90 40 50
- 例子中我們將參數由優先級從低到高傳入,優先級最高的放在最后。最后得到4個同學的成績排序。
-
numpy.msort()
數組按第一個軸排序,返回排序后的數組副本。
這個排序相當於 numpy.sort(a, axis=0)。很好理解。我們直接來看實例:
-
import numpy as np print(np.char.center('msort() 函數', 20, '*')) msa = np.array([[3, 7, 12, 45], [9, 1, 0, 34]]) print(np.msort(msa)) # 返回 *****msort() 函數***** [[ 3 1 0 34] [ 9 7 12 45]]
-
numpy.partition()
指定一個數,對數組進行分區。
通俗點說,就是指定一個數,以這個數為中心,將其他數分別放在這個數的兩邊。
我們來看實例:
-
import numpy as np print(np.char.center('partition() 函數', 20, '*')) pta = np.array([3, 7, 12, 45, 15, 0]) print(np.partition(pta, 2)) print('\n') print(np.partition(pta, (2, 4))) print('\n') # 返回 ***partition() 函數*** [ 0 3 7 45 15 12] [ 0 3 7 12 15 45]
-
在第一次排序時,我們選中了索引為2的數字7作為中心,將小於7的數放在左邊,大於7的數放在右邊。在第二次排序時,我們選擇了索引為2的數字7和索引為4的數字45,將小於7的數放在左邊,大於7小於45的數放在中間,大於45的數放在右邊。
查找函數
下面我們來看幾個常見的查找函數,這些函數用於在數組中查找特定條件的元素。
numpy.argmax()
返回沿給定軸的最大值索引。
注意,索引的值是從0開始計算的。
我們來看實例:
-
import numpy as np a = np.array([[30, 40, 70], [80, 20, 10], [50, 90, 60]]) print(np.char.center('初始數組', 20, '*')) print(a) print('\n') print(np.char.center('調用 argmax() 函數', 20, '*')) print(np.argmax(a)) print('\n') print(np.char.center('展開數組', 20, '*')) print(a.flatten()) print('\n') print(np.char.center('沿0軸的最大索引', 20, '*')) print(np.argmax(a, 0)) print('\n') print(np.char.center('沿1軸的最大索引', 20, '*')) print(np.argmax(a, 1)) print('\n') # 返回 ********初始數組******** [[30 40 70] [80 20 10] [50 90 60]] ***調用 argmax() 函數*** 7 ********展開數組******** [30 40 70 80 20 10 50 90 60] ******沿0軸的最大索引****** [1 2 0] ******沿1軸的最大索引****** [2 0 1]
-
numpy.argmin()
返回沿給定軸的最小值索引。
注意,索引的值是從0開始計算的。
我們來看實例:
-
import numpy as np a = np.array([[30, 40, 70], [80, 20, 10], [50, 90, 60]]) print(np.char.center('初始數組', 20, '*')) print(a) print('\n') print(np.char.center('調用 argmin() 函數', 20, '*')) print(np.argmin(a)) print('\n') print(np.char.center('沿0軸的最小索引', 20, '*')) print(np.argmin(a, 0)) print('\n') print(np.char.center('沿1軸的最小索引', 20, '*')) print(np.argmin(a, 1)) print('\n') # 返回 ********初始數組******** [[30 40 70] [80 20 10] [50 90 60]] ***調用 argmin() 函數*** 5 ******沿0軸的最小索引****** [0 1 1] ******沿1軸的最小索引****** [0 2 0]
-
numpy.nonzero()
返回輸入數組中非零元素的索引。
- 只有a中非零元素才會有索引值,那些零值元素沒有索引值;
- 返回的索引值數組是一個2維tuple數組,該tuple數組中包含一維的array數組。其中,一維array向量的個數與a的維數是一致的。
- 索引值數組的每一個array均是從一個維度上來描述其索引值。比如,如果a是一個二維數組,則索引值數組有兩個array,第一個 array 從行維度來描述索引值;第二個array從列維度來描述索引值。
- 該np.transpose(np.nonzero(x)) 函數能夠描述出每一個非零元素在不同維度的索引值。
我們來看實例:
-
import numpy as np b = np.array([[30, 40, 0], [0, 20, 10], [50, 0, 60]]) print(np.char.center('我們的數組是', 20, '*')) print(b) print(np.char.center('調用 nonzero() 函數', 20, '*')) c = np.nonzero(b) print(c) print(np.transpose(np.nonzero(b))) # 返回 *******我們的數組是******* [[30 40 0] [ 0 20 10] [50 0 60]] **調用 nonzero() 函數*** (array([0, 0, 1, 1, 2, 2]), array([0, 1, 1, 2, 0, 2])) [[0 0] [0 1] [1 1] [1 2] [2 0] [2 2]]
- 我們通過
np.transpose()方法轉換后看起來比較直觀,注意這里的索引是從0開始算的。 -
numpy.where()
返回輸入數組中滿足給定條件的元素的索引。
我們來看實例:
-
import numpy as np b = np.array([[30, 40, 0], [0, 20, 10], [50, 0, 60]]) print(np.char.center('調用 where() 函數', 20, '*')) print(np.where(b > 20)) print(np.transpose(np.where(b > 20))) # 返回 ***調用 where() 函數**** (array([0, 0, 2, 2]), array([0, 1, 0, 2])) [[0 0] [0 1] [2 0] [2 2]]
-
這里面我們輸入的條件是大於20,數組中大於20的數的索引都被查找出來了。
numpy.extract()
根據某個條件從數組中抽取元素,返回滿條件的元素。
我們來看實例:
-
import numpy as np x = np.arange(9.).reshape(3, 3) print(np.char.center('我們的數組是', 20, '*')) print(x) # 定義條件, 選擇偶數元素 condition = np.mod(x, 2) == 0 print(np.char.center('按元素的條件值', 20, '*')) print(condition) print(np.char.center('使用條件提取元素', 20, '*')) print(np.extract(condition, x)) # 返回 *******我們的數組是******* [[0. 1. 2.] [3. 4. 5.] [6. 7. 8.]] ******按元素的條件值******* [[ True False True] [False True False] [ True False True]] ******使用條件提取元素****** [0. 2. 4. 6. 8.]
- 例子中,我們先定義了一個條件,就是選擇偶數。然后我們可以打印這個數組每個元素是否滿足條件。最后我們調用
extract()方法返回滿足條件的元素。注意這里返回的是元素,而不是元素的索引。
