Python的1001種騷操作——基礎篇(1)
* 計數(統計):
在我們日常編程中,計數操作是比較常用的。比如:我們要尋找一段list或數組中哪些元素出現次數比較多,用於統計的時候,怕不是我們會寫出以下代碼(當然,可能只有我會寫成這樣。。)
def count(datalist): #datalist為數據集, t為元組, cnt為list data = list(set(datalist)) for i in data: tmp = 0 for j in datalist: if j==i: tmp += 1 t = (i,tmp) cnt.append(t) print(cnt)
上述代碼可以對一個亂序的數據列表進行統計其中各個元素出現的次數。雖然只有10行,但是我們使用line_profiler來對這段代碼進行性能檢測的時候(數據集使用randint來生成1000個0至9的數據數列),我們可以發現:
10行代碼在測試性能過程中總耗時0.0171224s(不慢的對吧)。其中,count函數中第二個循環體佔了總耗時的93.9%,可以說是耗時大戶。第二層for及其if判斷各執行了10000次,如果我們可以改進這段循環結構會不會對count函數帶來性能的優化?來,我們試一下:
我對第二個循環做了調整,使用了列表推導式對元素進行統計計算。(可能你會有更好的方式來做著操作,若有,請在評論區分享一下你的操作吧。)在性能測試報告中可以看到,新的count函數只有8行,但是運行時間卻達到了0.00174387s!對比前一個count函數的運行時間,新count函數提高了10倍。但是這種寫法還是不好,因為每個元素的總數是需要通過計算數組長度來確定,而且 tp = [tmp+1 for j in datalist if j == i]還是佔用了過多的時間。那麼我們繼續優化,這次我們使用字典結構加字典推導式方法。
# 騷操作1def count(datalist): data = list(set(datalist)) t = {i:datalist.count(i) for i in data} print(t)
當我們使用字典結構再配合推導式時,在代碼行數大幅減少的情況下,我們的第三種count函數的性能對比第二種count函數又提高了5倍,總共提高了50倍的性能。那麼當我們還不是那麼習慣使用推導式編程時,我們又有那些方法可以簡化我們的操作呢?這裡我們繼續沿用上期所提到的標準庫collections中Counter方法。
from collections import Counterdef count(datalist): num_count = Counter(datalist) print(num_count)
* 通過名稱來訪問元素
說到標準庫collections,我們就再說一種使用名稱來訪問元素的方法。先拋個磚,有時我們創建的數據集中每個元素都是有特定的意義的比如:
stock_list = [[AAPL,10.30,11.90],[YAHO,9.23,8.19],[SINA,22.80,25.80]]
上面的二維數組stock_list的元素含義依次就是每個股票的股票名稱,開盤價,以及收盤價。但是如果元素一旦很多的時候,我們要選取其中某一列的數據時,我們會比較頭疼需要精確找到它的下標。現在我們可以使用collections中的namedtuple()方法為我們的數據元素進行命名。
# 騷操作2>>>from collections import namedtuple>>>stock_info = namedtuple(stock_info,[name,open,end])>>>stock_list = stock_info(AAPL,10.30,11.90)>>>stock_list.name AAPL>>>name , *oped = stock_list>>>oped [10.30,11.90]>>>stock_list = stock_list._replace(open = 0)>>>stock_list stock_info(AAPL,0,11.90)
這樣我們就定義了一個命名元組,提供了一個類型名稱以及相對應的欄位:股票名,開盤價,收盤價。當我們需要尋找股票名時就可以寫shock_list.name,這樣是不是相對於shock_list[0]來得直觀的多。而且namedtuple具有索引和分解功能。如果我們需要修改欄位值,可以使用 ._replace()方法來實現。
* collection中的ChainMap()類
ChainMap類是用來將多個字典或映射合併成一個單獨的映射,使用原始的字典結構。
>>>from collections import ChainMap
>>>f = {a:1,b:2}>>>s = {b:3,c:3}>>>e = ChainMap(f,s)>>>print(e[b])2
ChainMap會先檢查f的元素,如果f中不存在再去s中尋找,這樣所以e[b]的值是f中的值而不是s中的。與單純的字典update不同,如果使用update方法,e[b]就會等於3。總結
今天講述了3塊內容:計數統計,命名數組和ChainMap的使用。在性能上,我們可以適當使用list comprehension和dictionary comprehension來簡化我們的代碼,提高性能。我們也可以使用namedtuple來直觀地尋找(檢索)信息。使用ChainMap來對多個字典合併到一個字典中,需要注意先後順序對最終結果的影響。Finally,歡迎各位同好多提提建議,多多批評,謝謝。
參考文獻
《Python Cookbook 中文版》第三版;
《Python性能分析與優化》
只求同好,無關浮名
推薦閱讀:
※反反爬蟲利器!教你怎麼用代理,撥號換IP……
※python3機器學習經典實例-第五章構建推薦引擎26
※這或許是對小白最友好的python入門了吧——11,if語句初體驗
※在Python應用中使用MongoDB
TAG:Python |