1. 程式人生 > >python 學習彙總55:序列字典排序彙總sorted,heapq(查詢最小值),bisect ,numpy.searchsorted(二分法排序)(- tcy)

python 學習彙總55:序列字典排序彙總sorted,heapq(查詢最小值),bisect ,numpy.searchsorted(二分法排序)(- tcy)

 排序 sorted                                                  建立日期:2018/6/29   修改日期:2017/11/20

1.1.函式:sorted(iterable, /, *, key= None, reverse= False)是一個高階函式

sorted函式用途:普通排序,自定義排序
heapq模組用途:獲取最小元素
bisect 模組:python二分法模組
numpy.searchsorted二分查詢的函式
bisect 模組用途:二分法排序
引數:
key是函式,實現自定義的排序
reverse=True 反向排序
區別:
列表list.sort()方法 就地修改列表
內建函式sorted() 從迭代器中構建一個新的排序列表  

1.2.例項:

# 例項1:
a=[1,4,2,-3,6,5]
a.sort() #就地修改列表並返回None; 升序
sorted(a,key=abs,reverse=True) #返回一個新的排序列表; [6, 5, 4, -3, 2, 1]
      
# 例項2:
lst=[1, 2, 3, 4, 5, 6, 7, 8, 9]
sorted(lst, key=lambda x: abs(5-x))
    # 按照元素與5距離從小到大進行排序[5, 4, 6, 3, 7, 2, 8, 1, 9]
        
# 例項3:
lsts=[(13, 'A'), (14, 'A'), (11, 'D'), (11, 'F')]
lsts.sort()
lsts       # [(11, 'D'), (11, 'F'), (13, 'A'), (14, 'A')]

lsts.sort(key=lambda lst: lst[0])
lsts       # [(11, 'D'), (11, 'F'), (13, 'A'), (14, 'A')]

lsts.sort(key=lambda lst: lst[1])
lsts       # [(13, 'A'), (14, 'A'), (11, 'D'), (11, 'F')]
        
# 例項4:
lsts = [(2, 103, "C"), (1, 111, "G"), (1, 103, "B"), (3, 104, "C")]

sorted(lsts)                                                  #預設按元素1排序
    # [(1, 103, 'B'), (1, 111, 'G'), (2, 103, 'C'), (3, 104, 'C')]

sorted(lsts,key=lambda lst: lst[2])                #按元素3排序
    # [(1, 103, 'B'), (2, 103, 'C'), (3, 104, 'C'), (1, 111, 'G')]

sorted(lsts,key=lambda lst: (lst[1], lst[2]))    #先按元素2排序,再按元素3排序
sorted(lsts,key=lambda lst: lst[1:3])             #效果同上
    # [(1, 103, 'B'), (2, 103, 'C'), (3, 104, 'C'), (1, 111, 'G')]

lsts.sort(key=lambda lst: (lst[2].lower(), lst[1]))#先按元素3小寫排序,再按元素2排序
    # [(1, 103, 'B'), (2, 103, 'C'), (3, 104, 'C'), (1, 111, 'G')]
            
# 例項5:
a = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
    return t[0]
s1 = sorted(a, key=by_name)                        #按名字排序

def by_name_score(t):
    return (t[0],t[1])
s1 = sorted(a, key=by_name_score)             #名字相同,按照分數排序(預設升序)
# 例項6:
class Student:
     def __init__(self, name, grade, age):
         self.name = name
         self.grade = grade
         self.age = age
     def __repr__(self):
         return repr((self.name, self.grade, self.age))

student_objects = [Student('Tom', 'A', 15),Student('Mark', 'B', 10),Student('John', 'B', 12)]
b=sorted(student_objects, key=lambda s=a: s.age)            # sort by age用物件屬性
# 例項7:
import random
random.seed(0)
rand_list = random.sample(range(100), 8)#[49, 97, 53, 5, 33, 65, 62, 51]
sorted(rand_list)                                        #[5, 33, 49, 51, 53, 62, 65, 97]

2. 運算子operator模組函式  更簡單和更快 

from operator import itemgetter, attrgetter
student = [ ('john', 'A', 15), ('jane', 'B', 12),('dave', 'B', 10),]

sorted(student, key=itemgetter(2) , reverse=True) # sort by age
sorted(student, key=itemgetter(0,2)) #多層次排序
sorted(student_objects, key=attrgetter('grade', 'age')) #多層次排序

3.__it__
Student.__lt__ = lambda self, other: self.age < other.age
b1=sorted(student_objects)

4.字典進行排序 2018/9/13 

d={'k1':2,'k6':3,'k5':4,'k2':6,'k3':3}
#例項0.sorted
sorted(d.keys())  #按key值對字典排序      # ['k1', 'k2', 'k3', 'k5', 'k6']
sorted(d.values())#按value值對字典排序  # [2, 3, 3, 4, 6]
sorted(d)                                                   # ['k1', 'k2', 'k3', 'k5', 'k6']
sorted(d.items())                                       # [('k1', 2), ('k2', 6), ('k3', 3), ('k5', 4), ('k6', 3)]
          
# 例項1.最簡單的方法,排列元素(key/value對),然後挑出值
def sortedDict(iDict):
    items = sorted(iDict.items())
    return [value  for key, value  in items]
# 例項2:使用排列鍵(key)的方式,挑出值,速度比方法1快。
def sortedDict(iDict):
    keys = sorted(iDict.keys())
    return [iDict[key]  for key  in keys]
# 例項3:通過對映的方法去更有效的執行最後一步
def sortedDict(iDict):
    keys = sorted(iDict.keys())
    return map(iDict.get,keys )
 
# 例項4.對字典按鍵排序,用元組列表的形式返回,同時使用lambda函式來進行

    sorted(d.items(), key=lambda e:e[0], reverse=True)
        # 按鍵排序# [('k6', 3), ('k5', 4), ('k3', 3), ('k2', 6), ('k1', 2)]
    sorted(d.items(), key=lambda e:e[1], reverse=True)
        # 按值排序# [('k2', 6), ('k5', 4), ('k6', 3), ('k3', 3), ('k1', 2)]
          
# 例項5.operator
    import operator

    sorted_x = sorted(d.items(), key=operator.itemgetter(0))
                    # 按照item中的第一個字元進行排序,即按照key排序
    sorted_x   # [('k1', 2), ('k2', 6), ('k3', 3), ('k5', 4), ('k6', 3)]

    sorted_x = sorted(d.items(), key=operator.itemgetter(1))
                    # 這裡改為按照item的第二個字元排序,即value排序
    sorted_x   # [('k1', 2), ('k6', 3), ('k3', 3), ('k5', 4), ('k2', 6)]
heapq-堆     列表訪問最小元素 2018/11/20
    
==================================================================
1.用途
       提供了基於常規列表實現堆的功能。
       最低價值條目始終保持在零位。
       重複訪問最小元素但不想執行完整列表排序的應用程式非常有用:
==================================================================
2.例項: 
    
from heapq import heapify, heappop, heappush
    
data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
heapify(data)                                  # 將列表重新排列為堆順序
heappush(data, -5)                        # 新增新值
[heappop(data) for i in range(3)]  # 獲取三個最小值[-5, 0, 1]
===================================================================
Python  二分查詢  bisect 模組  2018/6/30
    
==================================================================
    
1.概念
列表:
    # list內部實現是一個數組一個線性表。列表查詢list.index() 方法,其時間複雜度O(n)
二分查詢:
    # 也稱折半查詢,演算法每一次比較都使搜尋範圍縮小一半, 時間複雜度為 O(logn)
基本原理:
    # 從陣列中間元素開始,如果中間元素正好是要查詢的元素,則搜素過程結束;
    # 如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢, 而且跟開始一樣從中間元素開始比較。
    # 如果在某一步驟陣列為空,則代表找不到。
==================================================================
2.二分方法查詢
    
# 例項1:遞迴實現二分查詢
def binary_search_recursion(lst, value, low, high):
    if high < low:
        return None
    mid = int((low + high) / 2 )
    if lst[mid] > value:
        return binary_search_recursion(lst, value, low, mid -1)
    elif lst[mid] < value:
        return binary_search_recursion(lst, value, mid +1, high)
    else:
        return mid
    
#例項2:
def search(sequence, number, lower=0, upper=None):
    ''''序列是已經排序好的'''
    if upper is None:
        upper = len(sequence) - 1
    if lower == upper:
        assert number == sequence[upper]  # assert語句用來宣告某個條件是真的
        return upper
    else:
        middle = (lower + upper) // 2
    if number > sequence[middle]:
        return search(sequence, number, middle + 1, upper)
    else:
        return search(sequence, number, lower, middle)
    
# 例項3:迴圈實現二分查詢
def binary_search_loop(lst ,value):
    low, high = 0, len(lst ) -1
    while low <= high:
        mid = int((low + high) / 2 )
        if lst[mid] < value:
            low = mid + 1
        elif lst[mid] > value:
            high = mid - 1
        else:
            return mid
        low+=1
    return None
    
# 效能測試:
import random
lst = [random.randint(0, 10000) for i in range(100000)]
lst.sort()
    
def test_recursion():binary_search_recursion(lst, 999, 0, len(lst ) -1)
def test_loop():binary_search_loop(lst, 999)
    
import timeit
t1 = timeit.Timer("test_recursion()", setup="from __main__ import test_recursion")
t2 = timeit.Timer("test_loop()", setup="from __main__ import test_loop")
    
print( "Recursion:", t1.timeit())#Recursion: 10.576744321
print( "Loop:", t2.timeit() )        #Loop: 8.540910507  迴圈方式比遞迴效率高。
=====================================================================
    
3.bisect 模組
# 用於維護有序列表。bisect 模組實現了一個演算法用於插入元素到有序列表。
    
在一些情況下,這比反覆排序列表或構造一個大的列表再排序的效率更高。
Bisect 是二分法的意思,這裡使用二分法來排序,它會將一個元素插入到一個有序列表的合適位置,
這使得不需要每次呼叫 sort 的方式維護有序列表。
======================================================================
3.1.函式:
    
bisect.bisect_left(a ,x, lo=0, hi=len(a)) :# 查詢在有序列表a中插入x的index。
    # lo 和 hi 用於指定列表的區間,預設是使用整個列表。
    # 如果 x 已經存在,在其左邊插入。返回值為 index。
bisect.bisect(a, x ,lo=0, hi=len(a))
bisect.bisect_right(a ,x, lo=0, hi=len(a))# 這2個函式和 bisect_left 類似,但如果 x 已經存在,在其右邊插入。
    
bisect.insort_left(a ,x, lo=0, hi=len(a)) :# 在有序列表 a 中插入 x。
    # 等效:a.insert(bisect.bisect_left(a ,x, lo, hi), x)
    
bisect.insort(a, x ,lo=0, hi=len(a))
bisect.insort_right(a ,x, lo=0, hi=len(a))
    # 和 insort_left 類似,但如果 x 已經存在,在其右邊插入。
    
說明:
    bisect* 只用於查詢 index, 不進行實際的插入;
    insort* 則用於實際插入。該模組比較典型的應用是計算分數等級:
=======================================================================
# 3.2.示例1:
    
import bisect
import random
    
random.seed(1)
lst = []
for i in range(0, 6):
    r = random.randint(1, 100)
    position = bisect.bisect(lst, r) #右邊插入
    bisect.insort(lst, r)                  #排序
    print('%3d  %3d' % (i, position),lst)
    
#輸出結果:
#  0    0 [18]
#  1    1 [18, 73]
#  2    2 [18, 73, 98]
#  3    0 [9, 18, 73, 98]
#  4    2 [9, 18, 33, 73, 98]
#  5    1 [9, 16, 18, 33, 73, 98]
    
# 3.2.例項2:典型的應用是計算分數等級
def grade(score ,lst=[60, 70, 80, 90], grades='FDCBA'):
    i = bisect.bisect(lst, score)
    return grades[i],i
    
print( [grade(score) for score in [100, 55, 77, 70, 89, 90, 65,60,80]])
#執行結果:
#[('A',4),('F',0),('C',2),('C',2),('B',3),('A',4),('D',1), ('D',1), ('B',3)]
    
# 3.2.例項3:用 bisect 模組實現二分查詢
def binary_search_bisect(lst, x):
    from bisect import bisect_left
    i = bisect_left(lst, x)
    if i != len(lst) and lst[i] == x:
        return i
    return None
    
# 3.2.例項4:-列表排序
import bisect
scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')]
bisect.insort(scores, (300, 'ruby'))
scores#[(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]
    
====================================================================
4.numpy.searchsorted二分查詢的函式 
    # 用法與 bisect 基本相同,如要右邊插入時,需要設定引數 side ='right'
    
# 例項1:
import numpy as np
from bisect import bisect_left, bisect_right
    
data = [2, 4, 7, 9]
bisect_left(data, 4)                               #1
np.searchsorted(data, 4)                     #1
    
bisect_right(data, 4)                             #2
np.searchsorted(data, 4, side='right') #2
    
# 例項2:numpy.searchsorted 可以同時搜尋多個值
np.searchsorted([1,2,3,4,5], 3)                     #2
np.searchsorted([1,2,3,4,5], 3, side='right') #3
np.searchsorted([1,2,3,4,5], [-10, 10, 2, 3]) #array([0, 5, 1, 2])
    
注意:
np.searchsorted 搜尋 np.ndarray 是相當快;搜尋普通的陣列效率很低
====================================================================