4

我以前在 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

关于如何以一种好的方式做到这一点的任何想法?

谢谢!

4

3 回答 3

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接受一个返回正确类型对象的函数,并且返回 a 的函数defaultdict(dict)必须用 a partiallambda或显式函数编写。(ActiveState 上有一些配方,PyPI 上有模块,它们会给你一个更通用的版本,如果你愿意,可以根据需要创建新的字典。)

于 2013-02-28T23:46:19.490 回答
2

我也在尝试寻找替代方案,并在stackoverflow中提出了这个也很好的答案:

在 Python 中初始化 dicts 的最佳方法是什么?

基本上在我的情况下:

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
于 2013-02-28T23:55:58.270 回答
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 级字典,您必须以 [key_1, key_2, ... , key_n, value] 的形式将所有内容排序并转储到 (n+1) 元组列表中。
  2. 然后,要初始化 n 级字典,只需键入“defaultdict(lambda:”(减去引号) n-1 次,最后粘贴“defaultdict(list)”(或其他数据类型),然后关闭括弧。
  3. 使用 for 循环附加到列表中。*注意:当您访问最低级别的数据值时,您可能必须键入 my_dict[key_1][key_2] [...][key_n][0] 来获取实际值,而不仅仅是数据的描述在其中键入。
  4. *编辑:当你的字典和你想要的一样大时,将 default_factory 属性设置为 None。

如果您尚未将 default_factory 设置为 None,您可以稍后通过键入类似 my_dict[key_1][key_2][...][new_key]=new_value 或使用 append() 命令来添加到嵌套字典。您甚至可以添加其他词典,只要您通过这些分配形式添加的词典本身没有嵌套。

* 警告!该代码片段新添加的最后一行,将 default_factory 属性设置为 None,非常重要。您的 PC 需要知道您何时完成向字典的添加,否则它可能会继续在后台分配内存以防止缓冲区溢出,从而耗尽您的 RAM,直到程序停止运行。这是一种内存泄漏。写完这个答案后,我很难学到这一点。这个问题困扰了我好几个月,我什至不认为我是最终解决这个问题的人,因为我对内存分配一无所知。

于 2014-09-18T02:42:57.307 回答