用Python中的字典來(lái)處理索引統(tǒng)計(jì)的方法
最近折騰索引引擎以及數(shù)據(jù)統(tǒng)計(jì)方面的工作比較多, 與 Python 字典頻繁打交道, 至此整理一份此方面 API 的用法與坑法備案.
索引引擎的基本工作原理便是倒排索引, 即將一個(gè)文檔所包含的文字反過(guò)來(lái)映射至文檔; 這方面算法并沒(méi)有太多花樣可言, 為了增加效率, 索引數(shù)據(jù)盡可往內(nèi)存里面搬, 此法可效王獻(xiàn)之習(xí)書(shū)法之勢(shì), 只要把十八臺(tái)機(jī)器內(nèi)存全部塞滿, 那么基本也就功成名就了. 而基本思路舉個(gè)簡(jiǎn)單例子, 現(xiàn)在有以下文檔 (分詞已經(jīng)完成) 以及其包含的關(guān)鍵詞
doc_a: [word_w, word_x, word_y] doc_b: [word_x, word_z] doc_c: [word_y]
將其變換為
word_w -> [doc_a] word_x -> [doc_a, doc_b] word_y -> [doc_a, doc_c] word_z -> [doc_b]
寫(xiě)成 Python 代碼, 便是
doc_a = {'id': 'a', 'words': ['word_w', 'word_x', 'word_y']} doc_b = {'id': 'b', 'words': ['word_x', 'word_z']} doc_c = {'id': 'c', 'words': ['word_y']} docs = [doc_a, doc_b, doc_c] indices = dict() for doc in docs: for word in doc['words']: if word not in indices: indices[word] = [] indices[word].append(doc['id']) print indices
不過(guò)這里有個(gè)小技巧, 就是對(duì)于判斷當(dāng)前詞是否已經(jīng)在索引字典里的分支
if word not in indices: indices[word] = []
可以被 dict 的 setdefault(key, default=None) 接口替換. 此接口的作用是, 如果 key 在字典里, 那么好說(shuō), 拿出對(duì)應(yīng)的值來(lái); 否則, 新建此 key , 且設(shè)置默認(rèn)對(duì)應(yīng)值為 default . 但從設(shè)計(jì)上來(lái)說(shuō), 我不明白為何 default 有個(gè)默認(rèn)值 None , 看起來(lái)并無(wú)多大意義, 如果確要使用此接口, 大體都會(huì)自帶默認(rèn)值吧, 如下
for doc in docs: for word in doc['words']: indices. setdefault(word, []) .append(doc['id'])
這樣就省掉分支了, 代碼看起來(lái)少很多.
不過(guò)在某些情況下, setdefault 用起來(lái)并不順手: 當(dāng) default 值構(gòu)造很復(fù)雜時(shí), 或產(chǎn)生 default 值有副作用時(shí), 以及一個(gè)之后會(huì)說(shuō)到的情況; 前兩種情況一言以蔽之, 就是 setdefault 不適用于 default 需要惰性求值的場(chǎng)景. 換言之, 為了兼顧這種需求, setdefault 可能會(huì)設(shè)計(jì)成
def setdefault(self, key, default_factory): if key not in self: self[key] = default_factory() return self[key]
倘若真如此, 那么上面的代碼應(yīng)改成
for doc in docs: for word in doc['words']: indices.setdefault(word, list ).append(doc['id'])
不過(guò)實(shí)際上有其它替代方案, 這個(gè)最后會(huì)提到.
如果說(shuō)上面只是一個(gè)能預(yù)見(jiàn)但實(shí)際上可能根本不會(huì)遇到的 API 缺陷, 那么下面這個(gè)就略打臉了.
考慮現(xiàn)在要進(jìn)行詞頻統(tǒng)計(jì), 即一個(gè)詞在文章中出現(xiàn)了多少次, 如果直接拿 dict 來(lái)寫(xiě), 大致是
def word_count(words): count = dict() for word in words: count.setdefault(word, 0) += 1 return count print word_count(['hiiragi', 'kagami', 'hiiragi', 'tukasa', 'yosimizu', 'kagami'])
當(dāng)你興致勃勃地跑起上面代碼時(shí), 代碼會(huì)以迅雷不及掩臉之勢(shì)把異常甩到你鼻尖上 --- 因?yàn)槌霈F(xiàn)在 += 操作符左邊的 count.setdefault(word, 0) 在 Python 中不是一個(gè)左值. 怎樣, 現(xiàn)在開(kāi)始念叨 C艸 類(lèi)型體系的好了吧.
因?yàn)?Python 把默認(rèn)的字面常量 {} 等價(jià)于 dict() 就認(rèn)為 dict 是銀彈的思想是要不得的; Python 里面各種數(shù)據(jù)結(jié)構(gòu)不少, 解決統(tǒng)計(jì)問(wèn)題, 理想的方案是 collections.defaultdict 這個(gè)類(lèi). 下面的代碼想必看一眼就明白
from collections import defaultdict doc_a = {'id': 'a', 'words': ['word_w', 'word_x', 'word_y']} doc_b = {'id': 'b', 'words': ['word_x', 'word_z']} doc_c = {'id': 'c', 'words': ['word_y']} docs = [doc_a, doc_b, doc_c] indices = defaultdict(list) for doc in docs: for word in doc['words']: indices[word].append(doc['id']) print indices def word_count(words): count = defaultdict(int) for word in words: count[word] += 1 return count print word_count(['hiiragi', 'kagami', 'hiiragi', 'tukasa', 'yosimizu', 'kagami'])
完滿解決了之前遇到的那些破事.
此外 collections 里還有個(gè) Counter , 可以粗略認(rèn)為它是 defaultdict(int) 的擴(kuò)展.
相關(guān)文章
Python設(shè)計(jì)模式之外觀模式實(shí)例詳解
這篇文章主要介紹了Python設(shè)計(jì)模式之外觀模式,結(jié)合實(shí)例形式詳細(xì)分析了外觀模式的概念、原理、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-01-01Python數(shù)據(jù)可視化之用Matplotlib繪制常用圖形
Matplotlib能夠繪制折線圖、散點(diǎn)圖、柱狀圖、直方圖、餅圖. 我們需要知道不同的統(tǒng)計(jì)圖的意義,以此來(lái)決定選擇哪種統(tǒng)計(jì)圖來(lái)呈現(xiàn)我們的數(shù)據(jù),今天就帶大家詳細(xì)了解如何繪制這些常用圖形,需要的朋友可以參考下2021-06-06Python?socket之TCP通信及下載文件的實(shí)現(xiàn)
本文主要介紹了Python?socket之TCP通信及下載文件的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02python將時(shí)分秒轉(zhuǎn)換成秒的實(shí)例
今天小編就為大家分享一篇python將時(shí)分秒轉(zhuǎn)換成秒的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12Python自動(dòng)化測(cè)試pytest中fixtureAPI簡(jiǎn)單說(shuō)明
這篇文章主要為大家介紹了Python自動(dòng)化測(cè)試pytest中fixtureAPI的簡(jiǎn)單說(shuō)明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10使用python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的圖片爬蟲(chóng)
在本文中,我們將學(xué)習(xí)如何使用Python創(chuàng)建一個(gè)簡(jiǎn)單的圖片爬蟲(chóng),我們將利用requests庫(kù)來(lái)發(fā)送HTTP請(qǐng)求,BeautifulSoup庫(kù)來(lái)解析HTML頁(yè)面,以及os和shutil庫(kù)來(lái)下載和保存圖片,通過(guò)這個(gè)教程,你將學(xué)會(huì)如何爬取網(wǎng)頁(yè)上的圖片并保存到本地,需要的朋友可以參考下2024-02-02Python爬蟲(chóng)之爬取最新更新的小說(shuō)網(wǎng)站
這篇文章主要介紹了Python爬蟲(chóng)之爬取最新更新的小說(shuō)網(wǎng)站,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python爬蟲(chóng)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-05-05