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

在Python中构建嵌套字典从文件逐行读取,python,dictionary,nested,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.6

我使用嵌套字典的方式如下:

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']
我希望最终得到一个嵌套字典(忽略小数):

当我逐行阅读时,在我将第一个单词作为完整的dict添加到更高的dict之前,我必须检查是否仍在文件中找到第一个单词(它们都已分组)

这是我的实现:

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'}},
这显然是错误的。怎么了?

使用自动实例化嵌套字典:

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
,您也可以让外部字典保留对嵌套字典的引用,并使用
值[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
将在密钥不存在的情况下为我们创建一个新词典。

虽然这不是一种理想的方法,但您已经非常接近于让它工作了

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

第二个问题是,当到达末尾时,您从未在字典中存储最后一个
tmp
值,因此在
for
循环后添加另一个
dicty[oldword]=tmp

第三个问题是,如果oldword不是“”,您正在检查
。即使它是一个空字符串,这也可能是真的,因为您正在比较的是标识,而不是相等。如果旧单词:
,只需将其更改为
。(这一个,你通常会侥幸逃脱,因为小字符串通常会被拘留,并且通常会共享身份……但你不应该指望这一点。)

如果您同时修复这两个问题,您将得到以下结果:

{'FrontPage': {'frontpage': '0.710145', 'troubleshooting': '0.971014'},
 'proA': {'macbook': '0.666667', 'smart': '0.666667', 'ssd': '0.666667'}}
我不知道如何将它转换成您声称想要的格式,因为这种格式甚至不是有效的字典。但希望这能让你接近


有两种更简单的方法:

  • 将值分组,例如,
    itertools.groupby
    ,然后将每个组转换为一个dict并在一个步骤中全部插入。这与您现有的代码一样,要求输入已经通过
    值[0]
    进行批处理
  • 把字典当作字典用。您可以在每个键进入时查找它,如果找到,则将其添加到值中,如果没有,则创建一个新键。一个
    defaultdict
    setdefault
    方法将使其简洁,但即使您不知道这些,也可以非常简单地将其显式地写出来,并且仍然不会像现在这样冗长
第二个版本已经在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
而不是保留一个显式
row
计数器),这很容易添加。

{frontpage:0.7,{troubleshooting:0.97}
不是一个有效的字典。字典只能有键值对;您有一个键值对加上一个无键值。(如果你想要
{'frontpage':(0.7,{'troubleshooting':0.97})}
,你需要元组周围的paren。)我想我解决了,卷发不见了。此外,它们当然是字符串。此外,将字符串与
is
进行比较是一件冒险的事情。你通常会侥幸逃脱,特别是因为
没有被实习的机会很小,但你不应该指望这一点。@abarnert我之所以做这个测试,是因为忽略了在第一个循环中将{”“}添加到字典中,但是如果行>0
,也可能是
(根据你的建议,我会这样做)。在您编辑的版本中,您现在将
{troubleshooting:0.9}
作为顶级词典中的无键值,而不是
frontpage
词典;这同样是错误的。谢谢,这个解决方案非常好/简洁/快速。是的,我在过去尝试过很多事情,我记得不存在的关键是一个问题。是的,我认为这只是我的一个格式化失败。我将复制您的输出,因为这是我寻求的格式。感谢您展示了我的代码有什么问题,而不是提出了一些现有的东西。
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))}