用 Python 排序數據的多種方法


用 Python 排序數據的多種方法

目錄

Python HOWTOs系列】排序

用 Python 排序數據的多種方法

Python 列表有內置就地排序的方法 list.sort(),此外還有一個內置的 sorted() 函數將一個可迭代對象(iterable)排序為一個新的有序列表。

本文我們將去探索用 Python 做數據排序的多種方法。

排序基礎

簡單的升序排序非常容易:只需調用 sorted() 函數,就得到一個有序的新列表:

用 Python 排序數據的多種方法

你也可以使用 list.sort() 方法,此方法為就地排序(並且返回 None 來避免混淆)。通常來說這不如 sorted() 方便——但是當你不需要保留原始列表的時候,這種方式略高效一些。

用 Python 排序數據的多種方法

另外一個區別是 list.sort() 方法只可以供列表使用,而 sorted() 函數可以接受任意可迭代對象(iterable)。

用 Python 排序數據的多種方法

Key 函數

list.sort() 和 sorted() 都有一個 key 參數,用於指定在作比較之前,調用何種函數對列表元素進行處理。 For example, here’s a case-insensitive string comparison: 例如,忽略大小寫的字符串比較:

用 Python 排序數據的多種方法

key 參數的值應該是一個函數,該函數接收一個參數,並且返回一個 key 為排序時所用。這種方法速度很快,因為每個輸入項僅調用一次 key 函數。

一種常見模式是使用對象的下標作為 key 來排序復雜對象。例如:

用 Python 排序數據的多種方法

同樣的技巧也可以用在帶有命名屬性(named attributes)的對象上。例如:

用 Python 排序數據的多種方法

用 Python 排序數據的多種方法

上述的 key 函數模式是非常常見的,所以 Python 提供了一些更簡單快速的訪問屬性的函數。operator 模塊有 itemgetter()、attrgetter() 和 methodcaller() 函數。 Using those functions, the above examples become simpler and faster: 使用這些函數,可以使上述的示例更加簡潔高效:

用 Python 排序數據的多種方法

用 Python 排序數據的多種方法

用 Python 排序數據的多種方法

operator 模塊方法允許多級排序。例如,可以先按 grade 排序,然后再按 age 排序:

用 Python 排序數據的多種方法

用 Python 排序數據的多種方法

list.sort() 和 sorted() 都有布爾型的 reverse 參數,用來指定是否降序。例如,按 age 的降序來對學生數據進行排序:

用 Python 排序數據的多種方法

用 Python 排序數據的多種方法

排序是保證為穩定的,也就是說,當多條記錄擁有相同的 key 時,原始的順序會被保留下來。

用 Python 排序數據的多種方法

注意到兩條 blue 記錄保持了原來的順序, 所以 (‘blue’, 1) 一定在 (‘blue’, 2) 之前。

這個非常棒的屬性允許你通過一系列排序來進行復雜排序。例如,學生數據先按 grade 升序,然后按 age 降序,優先排序 age,然后再按 grade 排序:

用 Python 排序數據的多種方法

Python 使用的 Timsort 算法由於可以有效利用數據集中已有的順序,因而可以高效地進行多級排序。

使用 Decorate-Sort-Undecorate 的舊方法

Decorate-Sort-Undecorate 的名稱來源於這種方法的三個步驟:

  • 第一步,初始的列表進行轉換,獲得用於排序的新值。
  • 第二步,將轉換為新值的列表進行排序。
  • 最后,還原數據並得到一個排序后僅包含原始值的列表。

例如,使用 DSU(譯注:Decorate-Sort-Undecorate的簡寫)方法,按 grade 來排序學生數據:

>>> decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)]

>>> decorated.sort()

>>> [student for grade, i, student in decorated] # undecorate

[(‘john’, ‘A’, 15), (‘jane’, ‘B’, 12), (‘dave’, ‘B’, 10)]

這一方法利用了元組按字典序 (lexicographically) 比較的特性;先比較第一項;如果第一項相同,則比較第二項,以此類推。

在很多情況下是不需要在處理后的列表(decorated list)包含原始下標 i,但是包含原始下標有兩個好處:

排序是穩定的——如果有兩項有相同的 key,排序后的列表會保留他們的順序。

原始項不需要是可比較的,因為處理后的元組最多使用前面兩項就可以決定排序。例如,原始列表中包含無法直接比較的復數。

這個方法還有另外一個名字,是以 Randal L. Schwartz 的名字來命名的 Schwartzian 變換,因為他使得這個變換在 Perl 程序員中得以流行。

在 Python 排序提供 key 函數之后,這個技巧已經不常用了。

使用 cmp 參數的舊方法

本篇指南中給出的方法都假設 Python 2.4 或更新版本。在 2.4 之前,sorted() 和 list.sort() 是沒有 key 參數的。但是,在所有的 Py2.x 版本都支持 cmp 參數來處理用戶自定義排序函數。

在 Py3.0 中,cmp 參數已經被完全移除(作為簡化和統一語言的一部分,去除排序和 cmp() 魔法方法之間的沖突)。

在 Py2.x 中,sort 允許傳入一個可選函數,會在進行比較的時候調用。函數必須接受兩個參數進行比較,然后返回負數表示小於,返回 0 表示相等,返回正數表示大於。例如,我們可以這樣:

用 Python 排序數據的多種方法

或者你也可以反轉比較順序:

用 Python 排序數據的多種方法

當從 Python 2.x 移植代碼到 3.x 時,可能會出現需要將用戶提供的排序函數轉換為 key 函數的情況。下面的包裝器可以輕松做到:

用 Python 排序數據的多種方法

轉換為 key 函數,僅需要包裝舊的比較函數即可:

用 Python 排序數據的多種方法

在 Python 3.2 中,functools.cmp_to_key() 函數已經添加到標准庫的 functools 模塊中。

其他要點

針對時區相關排序,使用 locale.strxfrm() 作為 key 函數,或者使用 locale.strcoll() 作為比較函數。

reverse 參數仍然保持排序穩定性(以便相同 key 的項保留原順序)。有趣的是,無需傳入參數,通過兩次調用內置的 reversed() 函數,可以模擬出相同的效果:

用 Python 排序數據的多種方法

在兩個對象進行比較時,sort 使用的是 lt() 方法。所以,只需要為類添加 lt() 方法,就可以為類加入排序順序:

用 Python 排序數據的多種方法

key 函數不需要直接依賴於排序的對象。key 函數可以訪問外部資源。例如,如果學生的成績保存在字典中,字典中的數據可以給單獨的一個學生名字排序:

用 Python 排序數據的多種方法

英文出處:Andrew Dalke,Raymond Hettinger

http://blog.csdn.net/bestlove12345/article/details/51802755


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM