【问题标题】:Creating dynamic nested dictionary of counts创建计数的动态嵌套字典
【发布时间】:2015-02-18 01:17:24
【问题描述】:

我有一个文本文件abc.txt:

abc/pqr/lmn/xyz:pass
abc/pqr/lmn/bcd:pass

我需要解析这些语句,输出应该在嵌套字典中,如下所示:

{'abc':{'pqr':{'lmn':{'xyz':{'pass':1},{'bcd':{'pass':1}}}}}}

其中 1 是 'pass' 计数。

我能做到这么多:

import re

d={}
p=re.compile('[a-zA-z]+')
for line in open('abc.txt'):
    for key in p.findall(line):
        d['key']={}

【问题讨论】:

  • 对不起。我是 python 新手,但是这个输出的解决方案是什么。
  • 是的。对此感到抱歉。
  • 如果每行的最后一部分不是":pass"怎么办?
  • 我们必须为该键添加新计数
  • 不确定我理解你的意思。不管怎样,你觉得你得到的答案怎么样?也许你对他们的反应会澄清。

标签: python dictionary nested


【解决方案1】:

查看字典中的setdefault 方法。

d = {}
d.setdefault('pqr', {}).setdefault('lmn', {}).setdefault('xyz', {})['pass'] = 1
d.setdefault('pqr', {}).setdefault('lmn', {}).setdefault('bcd', {})['pass'] = 1
d

给予

{'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'pass': 1}}}}

【讨论】:

  • 我会检查的。谢谢。
  • 是否可以动态添加键而不是硬编码。
  • @sachin27:硬编码的键可以用你的p.findall()中的变量或值替换。
【解决方案2】:

这是我的答案的更新版本,其中树数据结构的叶子现在与其余部分不同。树不再是严格意义上的 dict-of-nested-dicts,而是每个分支上的“叶子”现在是 dict 的不同子类的实例,名为 collections.Counter,这对于计算每个键出现的次数。我这样做是因为您回答了我的问题,即如果每行的最后一部分不是 ":pass"(即“我们必须为该键添加新计数”)会发生什么。

嵌套字典通常称为 Tree 数据结构,可以递归定义——根是字典,分支也是。以下使用dict 子类而不是普通的dict,因为它使构造它们更容易,因为您不需要特殊情况下创建下一级的第一个分支(除非我在添加“叶子”时仍然这样做" 因为它们是不同的子类,collections.Counter)。

from collections import Counter
from functools import reduce
import re


# (Optional) trick to make Counter subclass print like a regular dict.
class Counter(Counter):
    def __repr__(self):
        return dict(self).__repr__()


# Borrowed from answer @ https://stackoverflow.com/a/19829714/355230
class Tree(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value


# Utility functions based on answer @ https://stackoverflow.com/a/14692747/355230
def nested_dict_get(nested_dict, keys):
    return reduce(lambda d, k: d[k], keys, nested_dict)

def nested_dict_set(nested_dict, keys, value):
    nested_dict_get(nested_dict, keys[:-1])[keys[-1]] = value

def nested_dict_update_count(nested_dict, keys):
    counter = nested_dict_get(nested_dict, keys[:-1])
    if counter:  # Update existing Counter.
        counter.update([keys[-1]])
    else:  # Create a new  Counter.
        nested_dict_set(nested_dict, keys[:-1], Counter([keys[-1]]))


d = Tree()
pat = re.compile(r'[a-zA-z]+')
with open('abc.txt') as file:
    for line in file:
        nested_dict_update_count(d, [w for w in pat.findall(line.rstrip())])

print(d)  # Prints like a regular dict.

为了测试修改后代码的叶子计数能力,我使用了以下测试文件,其中两次包含同一行,一次以:pass 结尾,另一次以:fail 结尾。

扩展abc.txt测试文件:

abc/pqr/lmn/xyz:pass
abc/pqr/lmn/bcd:pass
abc/pqr/lmn/xyz:fail
abc/pqr/lmn/xyz:pass

输出:

{'abc': {'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'fail': 1, 'pass': 2}}}}}

【讨论】:

    【解决方案3】:

    如果我理解你的问题:

    sources = ["abc/pqr/lmn/xyz:pass", "abc/pqr/lmn/bcd:pass", "abc/pqr/lmn/xyz:pass"]
    
    
    def prepare_source(source):
        path, value = source.split(':')
        elements = path.split('/')
        return elements, value
    
    
    def add_key(elements, value):
        result = dict()
        if len(elements) > 1:
            result[elements[0]] = add_key(elements[1:], value)
    
        else:
            result[elements[0]] = {value: 1}
    
        return result
    
    
    # base merge function get from here:
    # http://stackoverflow.com/questions/7204805/dictionaries-of-dictionaries-merge
    def merge(a, b, path=None):
        "merges b into a"
        if path is None: path = []
        for key in b:
            if key in a:
                if isinstance(a[key], dict) and isinstance(b[key], dict):
                    merge(a[key], b[key], path + [str(key)])
                elif isinstance(a[key], int) and isinstance(b[key], int):
                    a[key] += b[key]
                else:
                    raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
            else:
                a[key] = b[key]
        return a
    
    
    result = dict()
    
    for source in sources:
        result = merge(result, add_key(*prepare_source(source)))
    
    print result
    

    输出将是:

    {'abc': {'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'pass': 2}}}}}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-04
      • 1970-01-01
      • 2017-02-08
      • 1970-01-01
      • 2021-08-25
      • 1970-01-01
      • 2023-01-18
      • 1970-01-01
      相关资源
      最近更新 更多