如何合并字典并从匹配的键中收集值?

2024-12-02 08:41:00
admin
原创
268
摘要:问题描述:我有多个这样的字典(或键值对序列):d1 = {key1: x1, key2: y1} d2 = {key1: x2, key2: y2} 作为一个新的字典,我怎样才能有效地获得这样的结果?d = {key1: (x1, x2), key2: (y1, y2)} 另请参阅:如何在 Python 中创...

问题描述:

我有多个这样的字典(或键值对序列):

d1 = {key1: x1, key2: y1}
d2 = {key1: x2, key2: y2}

作为一个新的字典,我怎样才能有效地获得这样的结果?

d = {key1: (x1, x2), key2: (y1, y2)}

另请参阅:如何在 Python 中创建具有重复键的字典?。


解决方案 1:

这是一个通用的解决方案,可以处理任意数量的字典,但有时键仅存在于部分字典中:

from collections import defaultdict

d1 = {1: 2, 3: 4}
d2 = {1: 6, 3: 7}

dd = defaultdict(list)

for d in (d1, d2): # you can list as many input dicts as you want here
    for key, value in d.items():
        dd[key].append(value)
    
print(dd) # result: defaultdict(<type 'list'>, {1: [2, 6], 3: [4, 7]})

解决方案 2:

假设所有键始终存在于所有字典中:

ds = [d1, d2]
d = {}
for k in d1.iterkeys():
    d[k] = tuple(d[k] for d in ds)

注意:在 Python 3.x 中使用以下代码:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = tuple(d[k] for d in ds)

如果 dic 包含 numpy 数组:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = np.concatenate(list(d[k] for d in ds))

解决方案 3:

即使两个字典中的键不同,此函数也会合并两个字典:

def combine_dict(d1, d2):
    return {
        k: tuple(d[k] for d in (d1, d2) if k in d)
        for k in set(d1.keys()) | set(d2.keys())
    }

例子:

d1 = {
    'a': 1,
    'b': 2,
}
d2 = {
    'b': 'boat',
    'c': 'car',
    'd': 'donkey',
}
combine_dict(d1, d2)
# Returns: {
#    'a': (1,),
#    'b': (2, 'boat'),
#    'c': ('car',),
#    'd': ('donkey'),
# }

解决方案 4:

dict1 = {'m': 2, 'n': 4}
dict2 = {'n': 3, 'm': 1}

确保键的顺序相同:

dict2_sorted = {i:dict2[i] for i in dict1.keys()}

keys = dict1.keys()
values = zip(dict1.values(), dict2_sorted.values())
dictionary = dict(zip(keys, values))

给出:

{'m': (2, 1), 'n': (4, 3)}

解决方案 5:

如果你只有 d1 和 d2,

from collections import defaultdict

d = defaultdict(list)
for a, b in d1.items() + d2.items():
    d[a].append(b)

解决方案 6:

您可以使用以下一种方法,即使两个字典没有相同的键,它也能起作用:

d1 = {'a':'test','b':'btest','d':'dreg'}
d2 = {'a':'cool','b':'main','c':'clear'}

d = {}

for key in set(list(d1.keys()) + list(d2.keys())):
    try:
        d.setdefault(key,[]).append(d1[key])        
    except KeyError:
        pass

    try:
        d.setdefault(key,[]).append(d2[key])          
    except KeyError:
        pass

print(d)

这将生成以下输入:

{'a': ['test', 'cool'], 'c': ['clear'], 'b': ['btest', 'main'], 'd': ['dreg']}

解决方案 7:

使用预计算密钥

def merge(dicts):
    # First, figure out which keys are present.
    keys = set().union(*dicts)
    # Build a dict with those keys, using a list comprehension to
    # pull the values from the source dicts.
    return {
        k: [d[k] for d in dicts if k in d]
        for k in keys
    }

这本质上是 Flux 的答案,针对输入字典列表进行了概括。

这个set().union技巧的工作原理是将所有源字典中的键合并为一个集合。uniona 上的方法set(我们从一个空字典开始)可以接受任意数量的参数,并将每个输入与原始集合合并;并且它可以接受其他可迭代对象(它不需要其他set参数) - 它将对它们进行迭代并查找所有唯一元素。由于对 a 进行迭代dict会产生其键,因此可以直接将它们传递给该union方法。

在已知所有输入的键都相同的情况下,可以简化这一点:keys可以进行硬编码(或从其中一个输入推断),并且if列表推导中的检查变得没有必要:

def merge(dicts):
    return {
        k: [d[k] for d in dicts]
        for k in dicts[0].keys()
    }

这类似于 blubb 的答案,但使用字典理解而不是显式循环来构建最终结果。

我们也可以尝试类似 Mahdi Ghelichi 的回答:

def merge(dicts):
    values = zip(*(d.values() for d in ds))
    return dict(zip(dicts[0].keys(), values))

这应该在 Python 3.5 及以下版本中有效:在程序的同一运行期间,具有相同键的字典将以相同的顺序存储它们(如果再次运行程序,可能会得到不同的顺序,但仍然是一致的)。在 3.6 及更高版本中,字典保留其插入顺序(尽管它们仅在 3.7 及更高版本的规范中保证这样做)。因此,输入字典可以以不同的顺序具有相同的键,这将导致第一个组合错误的值。我们可以通过“排序”输入字典(使用一致顺序的键重新创建它们,例如)来解决这个问题。(在旧版本中,这将是额外的工作,没有净效果。)但是,这增加了复杂性,并且这种双重压缩方法实际上并没有比使用字典理解的先前方法提供任何优势。zip`[{k:d[k] for k in dicts[0].keys()} for d in dicts]`

明确构建结果,动态发现密钥

就像 Eli Bendersky 的回答一样,但作为一个函数:

from collections import defaultdict

def merge(dicts):
    result = defaultdict(list)
    for d in dicts:
        for key, value in d.items():
            result[key].append(value)
    return result

这将生成一个defaultdict,即标准库定义的子类dict。仅使用内置字典的等效代码可能如下所示:

def merge(dicts):
    result = {}
    for d in dicts:
        for key, value in d.items():
            result.setdefault(key, []).append(value)
    return result

使用列表以外的其他容器类型

预计算键方法可以很好地生成元组;[d[k] for d in dicts if k in d]用替换列表理解tuple(d[k] for d in dicts if k in d)。这会将生成器表达式传递给tuple构造函数。(没有“元组理解”。)

由于元组是不可变的并且没有方法,因此应通过将替换append为 来修改显式循环方法。但是,如果存在大量键重复,则此方法的性能可能会很差,因为它每次都必须创建一个新的元组。最好先生成列表,然后使用 之类的方法转换最终结果。.append(value)`+= (value,)`{k: tuple(v) for (k, v) in merged.items()}

可以进行类似的修改来获取集合(尽管有一个集合理解,使用{}),Numpy 数组等。例如,我们可以用容器类型概括这两种方法,如下所示:

def merge(dicts, value_type=list):
    # First, figure out which keys are present.
    keys = set().union(*dicts)
    # Build a dict with those keys, using a list comprehension to
    # pull the values from the source dicts.
    return {
        k: value_type(d[k] for d in dicts if k in d)
        for k in keys
    }

from collections import defaultdict

def merge(dicts, value_type=list):
    # We stick with hard-coded `list` for the first part,
    # because even other mutable types will offer different interfaces.
    result = defaultdict(list)
    for d in dicts:
        for key, value in d.items():
            result[key].append(value)
    # This is redundant for the default case, of course.
    return {k:value_type(v) for (k, v) in result}

如果输入值已经是序列

人们通常不想将源中的值包装到新列表中,而是希望获取所有值都已经是列表的输入,然后在输出中连接这些列表(或连接元组或一维 Numpy 数组、组合集合等)。

这仍然是一个微不足道的修改。对于预先计算的键,使用嵌套列表推导,按顺序获得平坦结果:

def merge(dicts):
    keys = set().union(*dicts)
    return {
        k: [v for d in dicts if k in d for v in d[k]]
        # Alternately:
        # k: [v for d in dicts for v in d.get(k, [])]
        for k in keys
    }

你可能会想到使用 连接sum原始列表推导的结果。不要这样做 - 当有大量重复键时,它的性能会很差。内置函数sum未针对序列进行优化(并且将明确禁止“求和”字符串),并且会尝试在内部通过每次添加创建一个新列表。

使用显式循环方法,使用.extend而不是.append

from collections import defaultdict

def merge(dicts):
    result = defaultdict(list)
    for d in dicts:
        for key, value in d.items():
            result[key].extend(value)
    return result

列表的方法extend接受任何可迭代的对象,因此它将适用于具有值元组的输入 - 当然,它仍然在输出中使用列表;当然,这些可以转换回来,如前所示。

如果输入每个都有一个项目

此问题的一个常见版本涉及输入字典,每个字典都有一个键值对。或者,输入可能是(key, value)元组(或列表)。

当然,上述方法仍然有效。对于元组输入,首先将它们转换为字典,例如[{k:v} for (k, v) in tuples],允许直接使用。或者,可以修改显式迭代方法以直接接受元组,例如 Victoria Stuart 的回答:

from collections import defaultdict

def merge(pairs):
    result = defaultdict(list)
    for key, value in pairs:
        result[key].extend(value)
    return result

(代码被简化了,因为当只有一个键值对并且已经直接提供时,不需要迭代键值对。)

但是,对于这些单项情况,按键对值进行排序然后使用可能会更好itertools.groupby在这种情况下,使用元组会更容易。看起来像:

from itertools import groupby

def merge(tuples):
    grouped = groupby(tuples, key=lambda t: t[0])
    return {k: [kv[1] for kv in ts] for k, ts in grouped}

这里,t用作输入中一个元组的名称。grouped迭代器将提供“键”值k(被分组的元组共有的第一个元素)和ts该组中元组的迭代器对。然后我们从中的键值kv对中提取值,从中创建一个列表,并将其用作结果字典中键ts的值。k

当然,要以这种方式合并单项字典,首先要将它们转换为元组。对于单项字典列表,一种简单的方法是[next(iter(d.items())) for d in dicts]

解决方案 8:

如果你安装了 pandas 并且所有字典中的所有键都相同,那么你可以在一行中完成:

import pandas as pd
d1 = {key1: x1, key2: y1}
d2 = {key1: x2, key2: y2}
new_dict = pd.DataFrame([d1,d2]).to_dict('list')

解决方案 9:

假设有两个具有完全相同键的字典,下面是最简洁的做法(两种解决方案都应该使用 python3)。


d1 = {'a': 1, 'b': 2, 'c':3}
d2 = {'a': 5, 'b': 6, 'c':7} 

# get keys from one of the dictionary
ks = [k for k in d1.keys()]

print(ks)
['a', 'b', 'c']

# call values from each dictionary on available keys
d_merged = {k: (d1[k], d2[k]) for k in ks}

print(d_merged)
{'a': (1, 5), 'b': (2, 6), 'c': (3, 7)}

# to merge values as list
d_merged = {k: [d1[k], d2[k]] for k in ks}
print(d_merged)
{'a': [1, 5], 'b': [2, 6], 'c': [3, 7]}

如果有两本字典有一些共同的键,但有几个不同的键,则应该准备所有键的列表。


d1 = {'a': 1, 'b': 2, 'c':3, 'd': 9}
d2 = {'a': 5, 'b': 6, 'c':7, 'e': 4} 

# get keys from one of the dictionary
d1_ks = [k for k in d1.keys()]
d2_ks = [k for k in d2.keys()]

all_ks = set(d1_ks + d2_ks)

print(all_ks)
['a', 'b', 'c', 'd', 'e']

# call values from each dictionary on available keys
d_merged = {k: [d1.get(k), d2.get(k)] for k in all_ks}

print(d_merged)
{'d': [9, None], 'a': [1, 5], 'b': [2, 6], 'c': [3, 7], 'e': [None, 4]}

解决方案 10:

有一个很棒的图书馆,funcy只需一行简短的指令就能满足您的需求。

from funcy import join_with
from pprint import pprint

d1 = {"key1": "x1", "key2": "y1"}
d2 = {"key1": "x2", "key2": "y2"}

list_of_dicts = [d1, d2]

merged_dict = join_with(tuple, list_of_dicts)

pprint(merged_dict)

输出:

{'key1': ('x1', 'x2'), 'key2': ('y1', 'y2')}

更多信息请点击这里:funcy -> join_with。

解决方案 11:

def merge(d1, d2, merge):
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge(result[k], v)
        else:
            result[k] = v
    return result

d1 = {'a': 1, 'b': 2}
d2 = {'a': 1, 'b': 3, 'c': 2}
print merge(d1, d2, lambda x, y:(x,y))

{'a': (1, 1), 'c': 2, 'b': (2, 3)}

解决方案 12:

如果键是嵌套的:

d1 = { 'key1': { 'nkey1': 'x1' }, 'key2': { 'nkey2': 'y1' } } 
d2 = { 'key1': { 'nkey1': 'x2' }, 'key2': { 'nkey2': 'y2' } }
ds = [d1, d2]
d = {}
for k in d1.keys():
    for k2 in d1[k].keys():
        d.setdefault(k, {})
        d[k].setdefault(k2, [])
        d[k][k2] = tuple(d[k][k2] for d in ds)

产量:

{'key1': {'nkey1': ('x1', 'x2')}, 'key2': {'nkey2': ('y1', 'y2')}}

解决方案 13:

修改这个答案来创建一个元组字典(OP要求的),而不是列表字典:

from collections import defaultdict

d1 = {1: 2, 3: 4}
d2 = {1: 6, 3: 7}

dd = defaultdict(tuple)

for d in (d1, d2): # you can list as many input dicts as you want here
    for key, value in d.items():
        dd[key] += (value,)

print(dd)

以上内容打印如下:

defaultdict(<class 'tuple'>, {1: (2, 6), 3: (4, 7)})

解决方案 14:

来自 blubb 的回答:

您还可以直接使用每个列表中的值形成元组

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = (d1[k], d2[k])

如果你对元组有特定的排序,这可能会很有用

ds = [d1, d2, d3, d4]
d = {}
for k in d1.keys():
  d[k] = (d3[k], d1[k], d4[k], d2[k]) #if you wanted tuple in order of d3, d1, d4, d2

解决方案 15:

使用以下方法我们可以合并两个具有相同键的字典。

def update_dict(dict1: dict, dict2: dict) -> dict:
output_dict = {}
for key in dict1.keys():
    output_dict.update({key: []})
    if type(dict1[key]) != str:
        for value in dict1[key]:
            output_dict[key].append(value)
    else:
        output_dict[key].append(dict1[key])
    if type(dict2[key]) != str:
        for value in dict2[key]:
            output_dict[key].append(value)
    else:
        output_dict[key].append(dict2[key])

return output_dict

输入:d1 = {key1:x1,key2:y1} d2 = {key1:x2,key2:y2}

输出:{'key1':['x1','x2'],'key2':['y1','y2']}

解决方案 16:

dicts = [dict1,dict2,dict3]
out   = dict(zip(dicts[0].keys(),[[dic[list(dic.keys())[key]] for dic in dicts] for key in range(0,len(dicts[0]))]))

解决方案 17:

紧凑的可能性

d1={'a':1,'b':2}
d2={'c':3,'d':4}
context={**d1, **d2}
context
{'b': 2, 'c': 3, 'd': 4, 'a': 1}
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2389  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1514  
  PLM(产品生命周期管理)系统在企业项目管理中扮演着至关重要的角色,它能够整合产品从概念设计到退役的全流程信息,提升协同效率,降低成本。然而,项目范围蔓延是项目管理过程中常见且棘手的问题,在PLM系统环境下也不例外。范围蔓延可能导致项目进度延迟、成本超支、质量下降等一系列不良后果,严重影响项目的成功交付。因此,如何在P...
plm项目经理是做什么   27  
  PLM(产品生命周期管理)系统在现代企业的产品研发与管理过程中扮演着至关重要的角色。它不仅仅是一个管理产品数据的工具,更能在利益相关者分析以及沟通矩阵设计方面提供强大的支持。通过合理运用PLM系统,企业能够更好地识别、理解和管理与产品相关的各类利益相关者,构建高效的沟通机制,从而提升产品开发的效率与质量,增强企业的市场...
plm是什么   29  
  PLM(产品生命周期管理)项目管理对于企业产品的全生命周期规划、执行与监控至关重要。在项目推进过程中,监控进度偏差是确保项目按时、按质量完成的关键环节。五维健康检查指标体系为有效监控PLM项目进度偏差提供了全面且系统的方法,涵盖了项目的多个关键维度,有助于及时发现问题并采取针对性措施。需求维度:精准把握项目基石需求维度...
plm项目管理软件   28  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用