2013-02-28 104 views
4

我以前在python中使用過字典,但我仍然是python的新手。這次我正在使用字典字典的字典......即三層字典,並且在編程之前要檢查它。在Python中初始化/創建/填充字典的字典的字典

我想在這個三層字典中存儲所有的數據,並想知道什麼是一種很好的pythonic方式來初始化,然後讀取一個文件並寫入這樣的數據結構。

我想這本字典是以下類型:

{'geneid': 
{'transcript_id': 
{col_name1:col_value1, col_name2:col_value2} 
} 
} 

的數據是這樣的類型:

geneid\ttx_id\tcolname1\tcolname2\n 
hello\tNR432\t4.5\t6.7 
bye\tNR439\t4.5\t6.7 

如何做到這一點的一個好辦法的任何想法?

謝謝!

+0

您的意思是在您的時間編碼和維護方面「有效」,還是根據CPU時間執行它? – abarnert 2013-02-28 23:37:39

+0

對不起,好點,編碼和維護... – Dnaiel 2013-02-28 23:38:05

回答

4

首先,讓我們與csv模塊開始處理解析線:

import csv 
with open('mydata.txt', 'rb') as f: 
    for row in csv.DictReader(f, delimiter='\t'): 
     print row 

這將打印:

{'geneid': 'hello', 'tx_id': 'NR432', 'col_name1': '4.5', 'col_name2': 6.7} 
{'geneid': 'bye', 'tx_id': 'NR439', 'col_name1': '4.5', 'col_name2': 6.7} 

所以,現在你只需要重新組織到您的首選結構。這幾乎是微不足道的,除非你必須處理這樣一個事實,即當你第一次看到給定的geneid你必須爲它創建一個新的空dict,並且同樣你第一次看到給定的tx_idgeneid內。您可以解決與setdefault

import csv 
genes = {} 
with open('mydata.txt', 'rb') as f: 
    for row in csv.DictReader(f, delimiter='\t'): 
     gene = genes.setdefault(row['geneid'], {}) 
     transcript = gene.setdefault(row['tx_id'], {}) 
     transcript['colname1'] = row['colname1'] 
     transcript['colname2'] = row['colname2'] 

可以使這個有點更具可讀性與defaultdict

import csv 
from collections import defaultdict 
from functools import partial 
genes = defaultdict(partial(defaultdict, dict)) 
with open('mydata.txt', 'rb') as f: 
    for row in csv.DictReader(f, delimiter='\t'): 
     genes[row['geneid']][row['tx_id']]['colname1'] = row['colname1'] 
     genes[row['geneid']][row['tx_id']]['colname2'] = row['colname2'] 

這裏的竅門是頂級dict是一個特殊的一個,它返回一個空dict每當它第一次看到一個新的密鑰...並且它返回的空dict本身是一個空的dict。唯一困難的部分是defaultdict採用返回正確類型對象的函數,並且返回defaultdict(dict)的函數必須用partiallambda或顯式函數寫入。 (在ActiveState上有配方,PyPI上的模塊會給你一個更通用的版本,如果需要的話,可以根據需要創建新的字典。)

+1

對於更多數量的列,您可以對col_name中的col_names進行操作:genes [row ['geneid']] [row ['transcript_id']] [col_name] = row [ col_name]' – 2013-02-28 23:52:49

+0

不使用'functools.partial'並做'genes = defaultdict(lambda:defaultdict(dict))'有缺點嗎? – 2013-02-28 23:54:40

+0

@StevenRumbalski:'partial'和'lambda'之間沒有太大區別。我個人發現前者更易於閱讀,因爲我知道它沒有做任何事情,只是將參數綁定到函數中,而'lambda'可以做任何事情。但這是一個風格問題。當表達式是重要的時候,我使用'lambda';當中央職能是重要的時候「部分」;當它們都不夠明顯時,它們就是「def」。 – abarnert 2013-02-28 23:56:37

2

我也試圖找到替代方法並來了在計算器這也極大的答案:

What's the best way to initialize a dict of dicts in Python?

基本上在我的情況:

class AutoVivification(dict): 
    """Implementation of perl's autovivification feature.""" 
    def __getitem__(self, item): 
     try: 
      return dict.__getitem__(self, item) 
     except KeyError: 
      value = self[item] = type(self)() 
      return value 
+1

是的,當我說「在PyPI上有ActiveState和模塊的食譜,它會給你一個更通用的版本,如果你願意,可以根據需要創建新的字典」,這就是我的意思。有幾種不同的方法可以做到這一點(其中一些方法甚至神奇地嘗試根據索引的類型創建字典或列表,如perl和JS所做的那樣)。 – abarnert 2013-03-01 00:00:14

2

我必須在編碼我的研究經常這樣做。您將需要使用defaultdict包,因爲它允許您通過簡單賦值在任何級別添加鍵:值對。我會在回答你的問題後給你看。這源自我的一個程序。專注於去年4線(即不評論)和跟蹤變量備份過塊的其餘部分,看看它在做什麼:

from astropy.io import fits #this package handles the image data I work with 
import numpy as np 
import os 
from collections import defaultdict 

klist = ['hdr','F','Ferr','flag','lmda','sky','skyerr','tel','telerr','wco','lsf'] 
dtess = [] 

for file in os.listdir(os.getcwd()): 
    if file.startswith("apVisit"): 
     meff = fits.open(file, mode='readonly', ignore_missing_end=True) 
     hdr = meff[0].header 
     oid = str(hdr["OBJID"]) #object ID 
     mjd = int(hdr["MJD5"].strip(' ')) #5-digit observation date 
     for k,v in enumerate(klist): 
      if k==0: 
       dtess = dtess+[[oid,mjd,v,hdr]] 
      else: 
       dtess=dtess+[[oid,mjd,v,meff[k].data]] 
     #header extension works differently from the rest of the image cube 
     #it's not relevant to populating dictionaries 
#HDUs in order of extension no.: header, flux, flux error, flag mask, 
# wavelength, sky flux, error in sky flux, telluric flux, telluric flux errors, 
# wavelength solution coefficients, & line-spread function 
dtree = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) 
for s,t,u,v in dtess: 
    dtree[s][t][u].append(v) 
#once you've added all the keys you want to your dictionary, 
#set default_factory attribute to None 
dtree.default_factory = None 

這裏的摘要版本。

  1. 首先,對於n級字典,你必須進行排序和轉儲 一切都變成清單(N + 1)的形式元組[key_1,key_2, ...,key_n,值]。
  2. 然後,初始化n級詞典, 您只需鍵入「defaultdict(lambda:」(減去引號)n-1次, 粘住「defaultdict(list)」(或其他某種數據類型)結束, 關閉括號
  3. 追加到列表中有一個for循環*注意:當你去訪問在最低級別的數據值 ,你可能會輸入my_dict [key_1] [key_2] [0]獲得實際值,而不僅僅是 類型的數據的描述。
  4. *編輯:當你的字典和你想要的一樣大時,設置 default_fact ory屬性爲None。

如果您還沒有設置default_factory爲無,您可以通過鍵入類似my_dict [key_1] [key_2] [...] [new_key] = NEW_VALUE,或使用後添加到您的嵌套字典append()命令。只要您通過這些賦值形式添加的字符不是自己嵌套的,您甚至可以添加其他字典。

* 警告!該代碼片段中新添加的最後一行,您將default_factory屬性設置爲None,其中超級重要。你的電腦需要知道你什麼時候添加到你的字典,否則它可能會繼續在後臺分配內存,以防止buffer overflow,吃掉你的RAM,直到程序停止。這是一種memory leak。在我寫了這個答案後,我很難學會這一點。這個問題困擾了我好幾個月,我甚至不認爲我是最後弄清楚的人,因爲我不瞭解內存分配的任何內容。