2013-10-23 56 views
1

我工作的一本字典結構,其中我的文件字典和每個文檔都有單詞的字典(其中每個鍵word_id(整數)和值數)這樣的:如何在Python中模仿dict的空間高效版本?

document_dict = { "doc1": {1:2, 2:10, 10:2, 100: 1}, "doc2": {10:2, 20:10, 30:2, 41: 19},...} 

注意內部字典很稀少,所以即使我有250K字,我也不希望每個文檔的密鑰超過1K。

在每次迭代中,我需要總結一個單詞的詞典:計數到其中一個文檔,例如,我需要將{1:2,2:10,10:2,120:1}的新字典合併爲「doc1」:{1:2,2:10,10:2,100:1}。

現在,我的實現運行得非常快,但2小時後,內存不足(我正在使用40GB服務器)。

的辦法,我的鑰匙總結是這樣的:

假設new_dict是新詞:算我要添加對到文檔1,如:

new_dict = {1:2, 2:10, 10:2, 120: 1} 
doc1 = {1:2, 2:10, 10:2, 100: 1} 

for item in new_dict: 
     doc1[item] = doc1.get(item, 0) + new_dict[item] 

然後自用字典運行代碼簡直是不可能的,因爲我的字典在很短的時間內變得非常大,我試圖將字典作爲2列表列出來:例如doc1 = [[],[]]其中第一個列表保留鍵,第二個鍵保留值。

現在,當我想要這樣的union 2結構時,我首先嚐試獲取doc1中每個new_dict項的索引。如果我成功獲取索引,這意味着密鑰已經在doc1中,因此我可以更新相應的值。否則,它不在doc1中,所以我將新的鍵和值附加到列表的末尾。然而,這種方法運行非常緩慢(在字典版本中,我能夠在2小時內處理多達600K個文檔,現在我只能在15個小時內處理250K個文檔)。

所以我的問題是:如果我想要使用字典結構(鍵,val)對,我需要2個字母的聯合鍵並在每次迭代中求和它們的值,是否有辦法有效地實現這個更多的空間?

+0

我不認爲它的數據結構,這就是問題所在;它試圖將X單位的東西裝入Y單元包中,其中X> Y. – duffymo

+0

您可能會遇到與dict()操作的內在方式無關的內存泄漏。你有沒有運行探查器?我發現這篇文章很有用http://mg.pov.lt/blog/hunting-python-memleaks.html – Vorsprung

+0

什麼是越來越大,文檔字典? – martineau

回答

1

這不一定更節省空間,但我建議使用shelve模塊切換到基於磁盤的字典,因此您不必一次將整個字典存儲在內存中。

他們是非常容易使用,因爲它們所支持的熟悉的字典界面,如下圖所示:

import shelve 

document_dict = shelve.open('document_dict', writeback=True) 

document_dict.update({"doc1": {1:2, 2:10, 10:2, 100: 1}, 
         "doc2": {10:2, 20:10, 30:2, 41: 19}, 
         "doc3": {1:2, 2:10, 10:2, 100: 1},}) 

new_dict = {1:2, 2:10, 10:2, 120: 1} 
doc = document_dict.get("doc3", {}) # get current value, if any 

for item in new_dict: 
    doc[item] = doc.get(item, 0) + new_dict[item] # update version in memory 

document_dict["doc3"] = doc # write modified (or new) entry to disk 
document_dict.sync() # clear cache 

print document_dict 

document_dict.close() 

輸出:

{'doc2': {41: 19, 10: 2, 20: 10, 30: 2}, 
'doc3': {120: 1, 1: 4, 2: 20, 100: 1, 10: 4}, 
'doc1': {1: 2, 2: 10, 100: 1, 10: 2}} 
+0

謝謝,貨架看起來很有前途。但是,我不太確定「你不必一次把所有的字典都寫在記憶中」。因爲根據我的理解,shelve會將所有對象保留在內存中(因爲writeback = True)*除非*我呼叫同步或關閉呼叫,對嗎?即使我偶爾會調用sync()(例如,在每100個文檔中,我可以調用sync並清除緩存),但是我不確定如何確保我訪問對象X的真實值。例如:http://stackoverflow.com/a/3410066 – user2779485

+0

是的,當使用'writeback'選項清除緩存時調用'sync()'。如更新的答案中所示,可以通過在更新文檔的計數後調用該對象來確保您具有該對象的真實值並將其緩存在內存中的數據量保持較小。 – martineau

+0

謝謝,它運行良好,我能夠運行我的程序沒有任何問題! – user2779485