我以前在python中使用过字典但是我还是python的新手。这次我使用字典词典的字典...即三层字典,并想在编程之前检查。
我想将所有数据存储在这个三层dict中,并且想知道什么是一个很好的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 :(得分: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_id
内的给定geneid
。您可以使用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)
的函数必须使用partial
,lambda
编写或显式函数。 (如果你愿意的话,ActiveState上的配方和PyPI上的模块将为你提供更加通用的版本,根据需要创建新的词典。)
答案 1 :(得分:2)
我也在尝试寻找替代方案,并在stackoverflow中提出了这个很好的答案:
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
答案 2 :(得分: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
这是摘要版本。
如果您尚未将default_factory设置为None,则可以稍后通过键入my_dict [key_1] [key_2] [...] [new_key] = new_value或使用append()等内容添加到嵌套字典中命令。您甚至可以添加其他词典,只要您通过这些分配形式添加的词典不会自己嵌套。
* 警告!新添加的代码段的最后一行是超级重要,其中default_factory属性设置为None。你的PC需要知道你什么时候添加到你的字典,否则它可能会继续在后台分配内存以防止buffer overflow,吃掉你的RAM,直到程序停止运行。这是一种memory leak。在我写完这个答案后的一段时间里,我学到了很多东西。这个问题困扰了我几个月,我甚至不认为我最终会弄明白,因为我对内存分配一无所知。