在Python中构建嵌套字典从文件中逐行读取

时间:2013-10-05 23:12:16

标签: python dictionary nested

我对嵌套字典的看法是:

dicty = dict()
tmp = dict()
tmp["a"] = 1
tmp["b"] = 2
dicty["A"] = tmp

dicty == {"A" : {"a" : 1, "b" : 1}}

当我尝试在一个大文件上实现这个问题时,问题就开始了,逐行阅读。 这是在列表中每行打印内容:

['proA', 'macbook', '0.666667']
['proA', 'smart', '0.666667']
['proA', 'ssd', '0.666667']
['FrontPage', 'frontpage', '0.710145']
['FrontPage', 'troubleshooting', '0.971014']

我想最终得到一个嵌套字典(忽略小数):

{'FrontPage': {'frontpage': '0.710145', 'troubleshooting': '0.971014'},
 'proA': {'macbook': '0.666667', 'smart': '0.666667', 'ssd': '0.666667'}}

当我逐行阅读时,我必须先检查文件中是否仍然找到第一个单词(它们都已分组),然后再将其作为完整字典添加到较高的字典中。

这是我的实施:

def doubleDict(filename):
    dicty = dict()
    with open(filename, "r") as f:
        row = 0
        tmp = dict()
        oldword = ""
        for line in f:
            values = line.rstrip().split(" ")
            print(values)
            if oldword == values[0]:
                tmp[values[1]] = values[2]
            else:
                if oldword is not "":
                    dicty[oldword] = tmp
                tmp.clear()
                oldword = values[0]
                tmp[values[1]] = values[2]
            row += 1
            if row % 25 == 0:
                print(dicty)
                break #print(row)
    return(dicty)

我真的希望在大熊猫中拥有这个,但是现在如果这可以作为一个词典,我会很高兴。出于某种原因,在阅读前5行后,我最终得到:

{'proA': {'frontpage': '0.710145', 'troubleshooting': '0.971014'}},

这显然是不正确的。有什么问题?

2 个答案:

答案 0 :(得分:2)

使用collections.defaultdict() object自动实例化嵌套词典:

from collections import defaultdict

def doubleDict(filename):
    dicty = defaultdict(dict)
    with open(filename, "r") as f:
        for i, line in enumerate(f):
            outer, inner, value = line.split()
            dicty[outer][inner] = value
            if i % 25 == 0:
                print(dicty)
                break #print(row)
    return(dicty)

我在这里使用enumerate()来生成行数;比单独的计数器更简单。

即使没有defaultdict,您也可以让外部字典保留对嵌套字典的引用,并使用values[0]再次检索它;没有必要保持temp参考:

>>> dicty = {}
>>> dicty['A'] = {}
>>> dicty['A']['a'] = 1
>>> dicty['A']['b'] = 2
>>> dicty
{'A': {'a': 1, 'b': 1}}

所有defaultdict然后做的是让我们不必测试我们是否已经创建了嵌套字典。而不是:

if outer not in dicty:
    dicty[outer] = {}
dicty[outer][inner] = value

我们只是省略了if测试,因为如果密钥尚未出现,defaultdict将为我们创建一个新字典。

答案 1 :(得分:1)

虽然这不是理想的做事方式,但你已经非常接近于让它发挥作用了。

您的主要问题是您正在重复使用相同的tmp词典。在第一个键下将其插入dicty后,然后clear,然后开始用新值填充它。将tmp.clear()替换为tmp = {}以修复此问题,因此每个密钥都有一个不同的字典,而不是所有密钥的字典。

您的第二个问题是,当您到达结尾时,您永远不会在字典中存储最后的tmp值,因此请在dicty[oldword] = tmp循环后添加另一个for

您的第三个问题是您正在检查if oldword is not "":。这可能是真的,即使它是一个空字符串,因为你是比较身份,而不是平等。只需将其更改为if oldword:即可。 (这个,你通常可以逃脱,因为小字符串通常被实习并且通常会共享身份......但你不应该依赖它。)

如果你解决了这两个问题,你就明白了:

{'FrontPage': {'frontpage': '0.710145', 'troubleshooting': '0.971014'},
 'proA': {'macbook': '0.666667', 'smart': '0.666667', 'ssd': '0.666667'}}

我不确定如何将其转换为您声称需要的格式,因为该格式甚至不是有效的字典。但希望这能让你接近。


有两种更简单的方法可以做到:

  • 使用例如itertools.groupby对值进行分组,然后将每个组转换为dict并将其全部插入一步。与现有代码一样,这需要输入已由values[0]批量处理。
  • 将字典用作字典。您可以查找每个密钥,如果找到则添加到该值,如果没有,则创建一个新密钥。一个defaultdictsetdefault方法会使这个简洁,但即使你不知道这些,明确地写出它也很简单,而且它仍然不如你的那么冗长现在有了。

在Martijn Pieters的回答中已经很好地解释了第二个版本。

第一个可以这样写:

def doubleDict(s):
    with open(filename, "r") as f:
        rows = (line.rstrip().split(" ") for line in f)
        return {k: {values[1]: values[2] for values in g}
                for k, g in itertools.groupby(rows, key=operator.itemgetter(0))}

当然,在每25行之后到目前为止还没有打印出dict,但是通过将理解转换为显式循环(理想情况下使用enumerate而不是保持显式{{1},这很容易添加计数器)。