(Python)-初學 Class-10 — 排序

(Python)-初學 Class-10 — 排序

(Python)-初學 Class-10 — 排序

依照預設或自訂的規則,實現 Python 中的資料排序

   

有時候我們需要對我們所擁有的資料進行排序,以便後續的查找以及使用。

和其他語言可能需要自己寫一個排序演算法不同,Python 提供了幾個便捷的函式供大家使用。在 Python 初學第六講 — 串列的更多操作當中,曾經簡單介紹過 lst.sort() 以及 sorted() 的使用。

為了避免初學者產生混淆,本篇將會以 sorted() 為主。

 

10-1 函式 sorted 的使用

讓我們從 sorted() 的用法開始吧!官方文件上面的使用方法介紹如下:

 

sorted(串列名稱, reverse=False, key=rule_fn)

 

sorted() 有三個參數,第一個參數是要排序的串列名稱,要注意的地方是,電腦無法判斷整數與字串之間的優先順序,因此,我們要用來排序的串列內容必須都是相同型態的元素。

第二個參數則是是否要反轉 reverse,預設是 False。簡單來說,假如今天我們要排序的是一個整數串列, reverse=False 的意思就是由小到大排,反之,當 reverse=True 時則代表這個串列要由大到小排。

而最後一個參數則是 key ,就是指我們所要用來排序的規則。當串列是一個數字串列時(意即元素型態皆為 int 或是 float 這類的數值型態),預設的規則就是依照數值大小進行排序;而當串列的元素都是字串 str 時,預設的規則是字母順序,也就是英文字典的排序規則。

reverse 以及 key 都不是必要的參數,再不設定的情況底下都會有內建的預設值,而是當有需要使用到時再設定即可。

除此之外需要注意的事情是, sorted() 這個函式會回傳一個經過排序以後的 list ,並不會改變那個 list 本身的值。因此,如果需要使用排序過後的串列,需要另外使用一個變數來接著這個回傳的有序串列。

 

串列、字典的排序

接下來來看幾個範例,以便更加了解 sorted() 這個 function 的使用方式。

 

一、整數串列排列

舉例來說,假如我們今天有一個 list 叫做 mylist :

mylist = [11, 6, 3, 9, 2, 1]

我們直接來對 mylist 進行排序,並且排序以後印出,用以確認排序後的串列內容,程式碼如下:

 

Learn 1 :

mylist = [11, 6, 3, 9, 2, 1]

mylist = sorted(mylist)

print(mylist)

而執行結果如下:

[1, 2, 3, 6, 9, 11]

 

由此可見,在經過 sorted() 以後,我們得到了一個數字順序由小到大的新的串列。那麼,如果我們這時將 reverse 設為 True 呢?

 

Learn 2 :

mylist = [11, 6, 3, 9, 2, 1]

mylist = sorted(mylist, reverse=True)

print(mylist)

執行結果如下:

[11, 9, 6, 3, 2, 1]

 

從執行結果可以看出,當我們將 reverse 設為 True ,排序規則就會反過來,變成由大到小進行排序。

 

二、字串串列排序

再來看看字串排序的例子吧!假如我們今天有一個 list 叫做 movieList :

 

Learn 3 :

movieList = ["The Shape of Water", "Moonlight", "Superman", "Birdman", "Argo", "Spotlight"]

print(movieList)

movieList = sorted(movieList)

print(movieList)

如果我們這時對其進行排序,則會得到如下的執行結果:

 

[‘The Shape of Water’, ‘Moonlight’, ‘Superman’, ‘Birdman’, ‘Argo’, ‘Spotlight’] [‘Argo’, ‘Birdman’, ‘Moonlight’, ‘Spotlight’, ‘Superman’, ‘The Shape of Water’]

 

從 A 開頭的 Argo 開始,依照字典順序 (A to Z) 依序往下排列,當第一個字母相同時,則會繼續往下比較,直到分出先後為止。而在 reverse=True 的情況底下,我們會得到完全相反的串列:

 

Learn 4 :

movieList = ["The Shape of Water", "Moonlight", "Superman", "Birdman", "Argo", "Spotlight"]

print(movieList)

movieList = sorted(movieList, reverse = True)

print(movieList)

[‘The Shape of Water’, ‘Moonlight’, ‘Superman’, ‘Birdman’, ‘Argo’, ‘Spotlight’] [‘The Shape of Water’, ‘Superman’, ‘Spotlight’, ‘Moonlight’, ‘Birdman’, ‘Argo’]

 

三、字典排序

除了串列以外, sorted() 也可以應用在字典的排序上面。字典裡的元素本身是無序的,只使用 key 值來作為 index 以便大家取用,但是有時在輸出或是運算時,我們會希望依照某個特定的順序來取用字典當中的內容。

舉例來說,有一個字典 dic ,內容如下:

 

Learn 5 :

dic = {"2017": "The Shape of Water", "2016": "Moonlight", "2014": "Birdman", "2015": "Spotlight"}

print(sorted(dic))

 

[‘2014’, ‘2015’, ‘2016’, ‘2017’]

 

結果會印出 [2014, 2015, 2016, 2017] 這樣的串列。由此可見,當我們直接對 dic 這個字典進行 sorted() ,我們所排序到的,只有這個字典所有的 key 值,而不是將 key 以及 value 一同下去排序,並且回傳。

那麼,如果我們想要一次將依照 key排序好的 key 以及 value 一同印出的話,可能就需要以 for 搭配 sorted() 去取值並且輸出了。

 

Learn 6 :

dic = {"2017": "The Shape of Water", "2016": "Moonlight", "2014": "Birdman", "2015": "Spotlight"}

for year in sorted(dic):

    print(year + ":" + dic[year])

2014:Birdman 2015:Spotlight 2016:Moonlight 2017:The Shape of Water

 

當然,還有其他對於字典可以指定排序內容的方法。

在 Python 初學第九講—字典 當中,曾經介紹過存取一個字典時,可以利用以下三個方法來取得不同的東西:

 

1. 存取字典當中的所有 key 值,會得到一個裝著所有 key 值的 list :

dict_name.keys()

2. 存取字典當中的所有 value 值,會得到一個裝著所有 value 的 list :

dict_name.values()

3. 存取字典當中的所有 key-value 元素,會得到一個裝著所有 key-value 元素的 list ,內容物皆為形式 (key, value) 的 tuple:

dict_name.items()

如果我們分別取得上面三種不同的內容,再進行排序的話,將會得到如下的結果:

 

Learn 7 :

dic = {"2017": "The Shape of Water", "2016": "Moonlight", "2014": "Birdman", "2015": "Spotlight"}

print(sorted(dic.keys()))

print(sorted(dic.values()))

print(sorted(dic.items()))

[‘2014’, ‘2015’, ‘2016’, ‘2017’] [‘Birdman’, ‘Moonlight’, ‘Spotlight’, ‘The Shape of Water’] [(‘2014’, ‘Birdman’), (‘2015’, ‘Spotlight’), (‘2016’, ‘Moonlight’), (‘2017’, ‘The Shape of Water’)]

 

很顯然地,當我們將 keys 以及 values 丟進 sorted() 時,就像是把字典當中的 key 或是 value 通通拿出來,丟進一個串列以後進行排序。

而當我們使用 items() 並對其進行排序時,等同於將字典當中的每個元素都拿出來,每一個 key 以及自己所對上的 value 會一起被放進一個tuple 以後,再用這個裝滿 tuple 的 list 進行排序。當我們需要進行 tuple 的排序, sorted會從每個 tuple 的第一個元素開始判斷,若相同才會繼續進行第二個元素的比較,因此我們所得到的結果就像是將這個字典當中的內容以 key 值進行排序。

那麼,如果我們想要得到和方才相似,但是是以 value 為依據進行排序的結果呢?我們留待下一節,自訂排序規則之後再來討論。

 

10-2 自訂排序規則串列

接下來我們要介紹的是,自訂排序規則。當我們今天想要對一個串列 / 字典進行排序,卻不想要使用 sorted() 預設的數值順序、字典順序來當成排序的方法而是自己想的規則時,我們就需要利用以下的方法。

 

一、串列自訂排序規則

如上所述, sorted() 的參數有 list name 、 reverse 、 key 三個不同的參數,前兩個已經簡單介紹過了,而最後的重頭戲就是 key 。

先舉個簡單的例子吧!假如今天我們想要排序一個字串串列,內容如下:

 

movieList = [“The Shape of Water”, “Moonlight”, “Superman”, “Birdman”, “Argo”, “Spotlight”]

 

這一次我們不想要依照名字字母順序排列,而是希望能得到一個依照字串長度排序的串列。這時候我們可能就會寫下如下的程式碼:

 

movieList = sorted(movieList, key=len)

 

利用 Python 內建的 len 來計算並回傳字串長度以後, sorted() 會接住每個回傳的長度整數,並且據此來對這個串列進行排序,因此執行結果如下:

 

Learn 8 :

movieList = ["The Shape of Water", "Moonlight", "Superman", "Birdman", "Argo", "Spotlight"]

movieList = sorted(movieList, key=len)

print(movieList)

[‘Argo’, ‘Birdman’, ‘Superman’, ‘Moonlight’, ‘Spotlight’, ‘The Shape of Water’]

 

我們得到了一個由小到大,依照字串長度進行排序的串列。在 sorted() 函數當中,我們會稱呼 key 後面的函數為 key function ,也就是排序依據的函數。當 sorted() 與 key function 搭配使用, sorted() 將會把所要排序的標的串列當中的元素一個一個丟進 key function 當中,然後得到和串列元素一樣多的回傳值,再根據這些回傳值,對原串列進行排序。

就像上面範例的 len 一樣:在 sorted() 當中,作為 len 參數的,是串列當中的元素,也就是字串,而不是整個串列;回傳的也都是單個元素的長度,而不是串列中所有元素的長度。

或許我們再舉個其他例子會比較容易理解:假如今天我們一樣想針對一個字串串列進行排序,但是排序的依據是不分大小寫,將字母 A 到 Z 轉換為 1 到 26 的數字以後,將整個字串的這些數字加總,由小到大進行排序。

 

那麼我們要做的,可能是先寫下如下的函式:

def word2num(s):

    total = 0

    for character in s.lower():

        total += ord(character) - 96

    return(total)

Note:”Cat” 丟進函式後回傳值為 3+1+20=24。

因為不分大小寫,所以我們將字串轉成全小寫字母,然後再結合 ASCII 的編碼系統,進行字母轉數字的轉換,並且加總。最後將這個字串所代表的數字回傳。

然後使用 sorted() 來對字串串列進行排序,並且將 key設為word2num,這時我們的程式碼可能如下:

 

Learn 9 :

def word2num(s):

    total = 0

    for character in s.lower():

        total += ord(character) - 96

    return(total)

mylist = ["pen", "pineapplepen", "pineapple", "applepen", "apple"]

print(sorted(mylist, key=word2num))

 

而執行結果如下:

[‘pen’, ‘apple’, ‘applepen’, ‘pineapple’, ‘pineapplepen’]

 

再次提醒, sorted() 自行設定 key function 以後,會根據此 function 的回傳值進行排序,而且此 function 的參數也只是串列當中的元素,而不是整個串列。在這個範例當中, function 的參數是一個字串,而不是整個字串串列。

 

二、字典自訂排序

上一節討論到字典的排序時,曾經提及如果直接將字典拿去排序、或是利用 items() 當作 sorted() 的目標串列時,將會以 key 的值進行排序。那麼,當我們想要依照 value 而非 key 值進行排列時,該怎麼做呢?

讓我們直接來看做法,再進行講解吧!

 

sorted(dic_name.items(), key = lambda d: d[1])

 

我們要排序的目標串列一樣是 items ,因為可以取得所有 key-value 的 tuple 。和方才不同的地方是是 key 後面的 key function :這裡我們使用了一種十分特別的 function ,叫做 lambda function 。

lambda function是一種十分簡化的 function 定義方法,寫在 lambda 後面的 d 是這個 function 的參數,而在冒號 : 後方的則是這個function 的回傳值,在這裡是 d[1] ,意思即是當這個 key function 每次拿到一個 tuple 作為參數,就取出 index 為 1 的值回傳。

我們當然也可以寫成我們所熟悉的 def 宣告函式的方法,程式碼可能如下:

 

def dic_value(d): return d[1] sorted(dic.items(), key = dic_value)

執行結果完全相同

 

Learn 10 :

dic = {"2017": "The Shape of Water", "2016": "Moonlight", "2014": "Birdman", "2015": "Spotlight"}

print(sorted(dic.items(), key = lambda d: d[1]))

def dic_value(d):

    return d[1]

print(sorted(dic.items(), key = dic_value))

[(‘2014’, ‘Birdman’), (‘2016’, ‘Moonlight’), (‘2015’, ‘Spotlight’), (‘2017’, ‘The Shape of Water’)] [(‘2014’, ‘Birdman’), (‘2016’, ‘Moonlight’), (‘2015’, ‘Spotlight’), (‘2017’, ‘The Shape of Water’)]

至此,我們已經完成了基本的排序操作。

 

10-3 複數條件的排序

Python 內建的 sorted() 函式在遇到條件相同的兩筆資料時,會保留原先輸入的順序而不會進行更動。舉例來說,當我們今天想要排序的是一組字串串列,而且我們想要依照字串的長度進行排序。

那麼,如同前面相同的範例,我們可能會寫出如下的程式碼:

 

Learn 11 :

mylist = ["pen", "pineapple", "pineapple", "applepens", "apple"]

print(sorted(mylist, key = len))

 

而我們所得到的執行結果如下:

[‘pen’, ‘apple’, ‘pineapple’, ‘pineapple’, ‘applepens’]

 

可以看得出來,由於我們是依照字串長度進行排序,所以得到了一個字串由短到長排序的串列。但是, pineapple 和 applepens 兩個長度相同的字串卻是依照著輸入時的順序排列的,如果我們希望長度相同的字串應該要依照字母的順序進行排列,我們該怎麼做呢?

此時,我們最好的方法就是利用 sorted() 排序 tuple 時的規則,當函數 sorted() 要排序一個 tuple 串列時,會先判斷 tuple 當中第一個元素的優先順序,如果相同,會接著判斷下一個元素的優先順序,所以可能會寫出一個 key function 如下:

def multi_sort(s):

    return (len(s), s)

這個函式會回傳字串的長度以及字串本身,因此 sorted() 得到這個函式的回傳值以後,他會先比較字串的長度,若長度相同,會接著依照字母順序比較兩個字串。而執行結果如下:

 

Learn 12 :

def multi_sort(s):

    return (len(s), s)

mylist = ["pen", "pineapple", "pineapple", "applepens", "apple"]

print(sorted(mylist, key = multi_sort))

[‘pen’, ‘apple’, ‘applepens’, ‘pineapple’, ‘pineapple’]

 

和方才不同,即使 pineapple 和 applepens 兩個字串長度相同,他們仍然會依照字母順序進行排序。

 

如上所述,當我們今天要進行複數條件的排序,最好的方法就是運用 tuple 的性質,如此自然可以運用任意數量的條件來進行排序。

 

10-4 sorted() v.s. lst.sort()

最後,讓我們來簡單討論一下關於 sorted() 以及 lst.sort() 的不同吧!

就像曾經在第六講當中提及的, sorted() 函式是將目標串列當作參數,進行排序以後,回傳一個經過排序的串列;相反的, lst.sort() 則是必須透過目標串列本身進行呼叫,因此會直接對該串列排序,沒有回傳值,而是改變該串列本身。

或許透過例子會比較清楚地表達兩者的差異:首先,我們有一個字串串列叫做 mylist ,然後我們要對其進行排序。

當我們使用 sorted() 進行排序時,得到的執行結果如下:

 

Learn 13 :

mylist = ["pen", "pineapple", "pineapple", "applepens", "apple"]

print(sorted(mylist))

print(mylist)

[‘apple’, ‘applepens’, ‘pen’, ‘pineapple’, ‘pineapple’] [‘pen’, ‘pineapple’, ‘pineapple’, ‘applepens’, ‘apple’]

 

由此可見, sorted() 是有回傳值的,而且是將排序好的串列回傳,而且並不會改變原本串列的長相。 而當我們使用 lst.sort() 呢?執行結果如下:

 

Learn 14 :

mylist = ["pen", "pineapple", "pineapple", "applepens", "apple"]

print(mylist.sort())

print(mylist)

 

None [‘apple’, ‘applepens’, ‘pen’, ‘pineapple’, ‘pineapple’]

 

當我們想要將 lst.sort() 的回傳值印出時,得到 None ,意即沒有回傳值。而當我們印出此時的 mylist 時,得到了一個已經經過排序的串列。

至此,Python 排序的介紹已經告一段落,善用這些排序資料的方法,將會對於資料的處理更有想法,而且更加方便。

 

免責聲明:

1.本影像檔案皆從網上搜集轉載,不承擔任何技術及版權問題

2.如有下載連結僅供寬頻測試研究用途,請下載後在24小時內刪除,請勿用於商業

3.若侵犯了您的合法權益,請來信通知我們,我們會及時刪除,給您帶來的不便,深表歉意。

 



發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *