2013-02-28 106 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。在我写了这个答案后,我很难学会这一点。这个问题困扰了我好几个月,我甚至不认为我是最后弄清楚的人,因为我不了解内存分配的任何内容。