【问题标题】:Looping thought dictionary elements and combining (also adding) on similar keys循环思想字典元素并在相似键上组合(也添加)
【发布时间】:2021-10-01 23:41:59
【问题描述】:

我会拥有多个“key1”和“key2”,我可以拥有 20 个或更多。

my_dict = {"key1": [{"A": 0}, {"B": 1}, {"C": 0}, {"D": 0},
                    {"A": 0}, {"B": 0}, {"C": 0}, {"D": 0},
                    {"A": 0}, {"B": 1}, {"C": 0}, {"D": 0}],
           "key2": [{"A": 0}, {"B": 0}, {"C": 0}, {"D": 0},
                    {"A": 1}, {"B": 0}, {"C": 0}, {"D": 0}, 
                    {"A": 0}, {"B": 0}, {"C": 0}, {"D": 0},
                    {"A": 1}, {"B": 0}, {"C": 0}, {"D": 0},
                    {"A": 0}, {"B": 0}, {"C": 0}, {"D": 0}],
            "key+:"..etc..}

我的最终输出如下:

new_dict = {"key1": [{"A": 0}, {"B": 2}, {"C": 0}, {"D": 0}],
            "key2": [{"A": 2}, {"B": 0}, {"C": 0}, {"D": 0}]}

我试过了:

new_dict = defaultdict(int)
f_dict = {}
for org in my_dict.keys():
    f_dict[str(org)] = []
    for key, dict_list in my_dict.items():
        for d in dict_list:
            for k, v in d.items():
                new_dict[k] += v
    f_dict[str(org)].append(dict(new_dict))

【问题讨论】:

    标签: python-3.x list dictionary


    【解决方案1】:

    这有几个层次:

    外层:具有两个唯一键的字典
    这些字典中的每一个都包含一个较小字典的列表(我称之为dict_list

    from collections import defaultdict
    
    # initialize a new default dictionary
    # this allows you to initialize each new key with a value of zero
    new_dict = defaultdict(int)
    
    for key, dict_list in my_dict.items():
        for d in dict_list:
            for k, v in d.items():
                new_dict[k] += v
    

    编辑 1:
    我不明白这个问题。看起来您想保留外部字典的键。

    from collections import defaultdict
    new_dict = {}
    for key, dict_list in my_dict.items():
        new_dict[key] = defaultdict(int)
        for d in dict_list:
            for k, v in d.items():
                new_dict[key][k] += v
    

    这将按以下格式输出 new_dict:
    new_dict = {'key1': {'A': 0, 'B': 2...} 'key2': {...}}

    如果你想让key1和key2包含一个dicts列表,在前面的代码之后,你可以添加:

    for key, d in new_dict.items():
        l = []
        for k, v in d.items():
            l.append({k: v})
        new_dict[key] = l
    

    这将按以下格式输出 new_dict:
    new_dict = {'key1': [{'A': 0}, {'B': 2},...], 'key2': [{...}, ...]}

    【讨论】:

    • 谢谢,有道理,我的问题是如何将它们分开,我更新了我的问题。
    • 啊,我明白了——我不明白这个问题。编辑了我的答案。
    【解决方案2】:

    TL;DR

    如果需要,请使用@Cat 的解决方案

    f: Dict[str, List[Dict[str, int]]] -> Dict[str, Dict[str, int]]
    

    如果你想使用我的

    f: Dict[str, List[Dict[str, int]]] -> Dict[str, List[Dict[str, int]]]
    

    解决方案

    可能有点矫枉过正,但我​​想弄乱一些我很少使用的功能。老实说,我不完全确定我正确地注释了这些类型。

    import itertools, functools
    from typing import Iterator, Iterable, List
    
    
    def tuple_sum(t1: tuple, t2: tuple) -> tuple:
        """Assumes both `t1` and `t2` are 2-tuples and that they match in the first element."""
        (key, x), (_, y) = t1, t2
        return (key, x + y)
    
    
    def tuple_union(tuple_group: itertools.groupby) -> tuple:
        """Unions a group of tuples with matching 'keys', with `tuple_sum` as aggregator."""
        return functools.reduce(tuple_sum, tuple_group)
    
    
    def group_tuples(tuples: Iterable[tuple]) -> itertools.groupby:
        """Yields tuples grouped by key. Assumes `tuples` are already sorted by key."""
        yield from (g for _, g in itertools.groupby(tuples, key=lambda t: t[0]))
    
    
    def package_tuples(tuple_iterator: Iterator[tuple]) -> List[dict]:
        """Packages each tuple in `tuple_iterator` as a standalone `dict`"""
        return [dict((t,)) for t in tuple_iterator]
    
    
    def consolidate(d_list: List[dict]) -> List[dict]:
        """Groups dictionaries with matching keys, then sums their values."""
        tuples = []
        for d in d_list:
            tuples.extend(d.items())
        sorted_tuples = sorted(tuples, key=lambda t: t[0])
        groups = group_tuples(sorted_tuples)
        return package_tuples(tuple_union(g) for g in groups)
    

    用法

    >>> {key: consolidate(d_list) for key, d_list in my_dict.items()}
    {'key1': [{'A': 0}, {'B': 2}, {'C': 0}, {'D': 0}],
     'key2': [{'A': 2}, {'B': 0}, {'C': 0}, {'D': 0}]}
    

    说明

    1. tuple_sum() 函数希望非常清晰,它需要两个 2 元组并将它们的第二个元素相加。它假定传递的两个元组具有匹配的“键”,其中“键”被视为第一个元素。
    2. tuple_union() 函数获取一组键匹配的元组,例如 [("a", 1), ("a", 5), ("a", 3)],并使用 tuple_sum() 对它们求和,返回一个元组,例如 ("a", 9)
    3. group_tuples() 函数接受一个 sorted 元组迭代,并返回一个生成组的生成器。当具有匹配键的所有元组相邻时,元组的可迭代被认为是“排序的”,例如:[("a", 1), ("a", 5), ("b", 3), ("b", 7), ("b" 3), ("c", 1)]itertools.groupby() 函数返回一个生成这些组的迭代器,例如("a", 1), ("a", 5),然后是("b", 3), ("b", 7), ("b" 3)
    4. package_tuples() 函数采用迭代器,该迭代器生成元组并将每个元组打包到字典中,例如 dict((("a", 9),)) -> {"a": 9}

    consolidate() 函数的工作方式是获取一个字典列表并将它们“融合”在一起作为一个大的元组列表。然后它对这些元组进行排序,然后将这些排序后的元组传递给group_tuples 生成器。

    替代输出结构

    如果您希望将其作为输出:

    {'key1': {'A': 0, 'B': 2, 'C': 0, 'D': 0},
     'key2': {'A': 2, 'B': 0, 'C': 0, 'D': 0}}
    

    你可以修改consolidate()函数如下:

    def consolidate(d_list: List[dict]) -> List[dict]:
        """Groups dictionaries with matching keys, then sums their values."""
        tuples = []
        for d in d_list:
            tuples.extend(d.items())
        sorted_tuples = sorted(tuples, key=lambda t: t[0])
        return dict(tuple_union(g) for g in group_tuples(sorted_tuples))
    

    这也意味着您可以完全摆脱package_tuples() 函数。不过,在这一点上,@Cat 的解决方案要简洁得多,因为这种类型的合并要简单得多。

    【讨论】:

    • 很高兴我能帮上忙。内置的 itertools 和 functools 模块非常有趣。
    • 使用上面的,是否也可以返回: new_dict = {'key1': {'A': 0, 'B': 2, 'C':..} 'key2': {...}}
    • @user1040259 是的!查看我的编辑。
    • 不客气!
    • @user1040259 如果dict 结构的dict 是您实际 需要的,那么@Cat 的解决方案可能会更好。我没有测试过速度或内存,但至少如果Dict[str, List[Dict[str, int]]] -> Dict[str, Dict[str, int]] 是您的最终目标,他们的测试会更简单。只有当你想保留输入的结构时,我的才是真正需要的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 1970-01-01
    相关资源
    最近更新 更多