【问题标题】:Combine python dictionaries that share values and keys组合共享值和键的python字典
【发布时间】:2018-03-16 15:28:00
【问题描述】:

我正在根据字符串编辑距离进行一些实体匹配,我的结果是一个字典,其中包含基于某些评分标准的键(查询字符串)和值 [相似字符串列表]。

例如:

results = {
  'ben' : ['benj', 'benjamin', 'benyamin'],
  'benj': ['ben', 'beny', 'benjamin'],
  'benjamin': ['benyamin'],
  'benyamin': ['benjamin'],
  'carl': ['karl'],
  'karl': ['carl'],
}

每个值也有一个对应的字典项,它是它的键(例如'carl'和'karl')。

我需要组合具有共同价值的元素。选择一个值作为新键(假设是最长的字符串)。在上面的例子中,我希望得到:

results = {
  'benjamin': ['ben', 'benj', 'benyamin', 'beny', 'benjamin', 'benyamin'],
  'carl': ['carl','karl']
}

我尝试过使用键迭代字典,但我无法理解如何迭代和比较每个字典项及其值列表(或单个值)。

【问题讨论】:

  • 应该仅在值重叠时合并列表,还是应该每个值查找匹配的键,然后根据这些值合并?编辑:如果我理解你的例子,我猜是后者。
  • 我应该说,每个值也将是一个带有自己字典项的键。
  • 有点不相关,但你不应该混合字符串和列表作为你的字典的值——即使你有一个字符串,它也应该包含在一个列表中。通过避免特殊情况,这将使您的代码更简单、更健壮。
  • 你能展示你的尝试吗?
  • 评分标准是否对称?如果similarity(a, b) 是提供评分标准的函数,那么similarity(a, b) 是否返回与similarity(b, a) 相同的结果?

标签: python dictionary


【解决方案1】:

这是使用collections.defaultdict 和集合的一种解决方案。

所需的输出与您所拥有的非常相似,并且可以轻松操作以对齐。

from collections import defaultdict

results = {
  'ben' : ['benj', 'benjamin', 'benyamin'],
  'benj': ['ben', 'beny', 'benjamin'],
  'benjamin': 'benyamin',
  'benyamin': 'benjamin',
  'carl': 'karl',
  'karl': 'carl',
}

d = defaultdict(set)

for i, (k, v) in enumerate(results.items()):
    w = {k} | (set(v) if isinstance(v, list) else {v})
    for m, n in d.items():
        if not n.isdisjoint(w):
            d[m].update(w)
            break
    else:
        d[i] = w

result = {max(v, key=len): v for k, v in d.items()}

# {'benjamin': {'ben', 'benj', 'benjamin', 'beny', 'benyamin'},
#  'carl': {'carl', 'karl'}}

感谢@IMCoins 在第二个循环中将v 操作为w 的想法。

说明

有3个主要步骤:

  1. 将值转换为一致的集合格式,包括原始字典中的键和值。
  2. 循环浏览此字典并将值添加到新字典。如果与某个键 [i.e.集合不是不相交的],然后使用该键。否则,添加到通过枚举确定的新密钥。
  3. 通过将最大长度键映射到值来在最终转换中创建结果字典。

【讨论】:

  • (1) 是否希望keys 在结果值中?是的。 (2) 如果两个或多个值具有相同的最大长度,则使用哪个键。缺少使用最常见名称的语料库,我不知道您将如何优先考虑其中一个(对建议或示例持开放态度)。谢谢。
  • 好的,我已经更新了解决方案以满足包含键的值(不是您想要的 'carl': 'karl' 的输出)。
  • 感谢 jpp,我已经在我的实际数据上运行了它并且它有效。为了我和其他人的利益,请您添加一些基本的代码 cmets。
  • @BenP 我相信您可能想将他的答案和我的答案结合起来。在我看来,他的回答是浪费不必要的时间来计算一个新的字典 set(),而你可以在第二个循环中通过将 d[i] = v 更改为 d[i] = set(v)
  • @IMCoins,我现在明白了——包括你的改进。我也使用isdisjoint 而不是intersection,因为这应该会更好。
【解决方案2】:

编辑:尽管性能不是这里的问题,但我冒昧地在 jpp 的答案和我的答案之间进行了一些测试......这是完整的脚本。我的脚本在 17.79 秒内执行测试,而他在 23.5 秒内执行测试。

import timeit

results = {
  'ben' : ['benj', 'benjamin', 'benyamin'],
  'benj': ['ben', 'beny', 'benjamin'],
  'benjamin': ['benyamin'],
  'benyamin': ['benjamin'],
  'carl': ['karl'],
  'karl': ['carl'],
}

def imcoins(result):
    new_dict = {}
    # .items() for python3x
    for k, v in results.iteritems():
        flag = False
        #   Checking if key exists...
        if k not in new_dict.keys():
            #   But then, we also need to check its values.
            for item in v:
                if item in new_dict.keys():
                    #   If we update, set the flag to True, so we don't create a new value.
                    new_dict[item].update(v)
                    flag = True
            if flag == False:
                new_dict[k] = set(v)

    #   Now, to sort our newly created dict...
    sorted_dict = {}
    for k, v in new_dict.iteritems():
        max_string = max(v)
        if len(max_string) > len(k):
            sorted_dict[max(v, key=len)] = set(v)
        else:
            sorted_dict[k] =  v

    return sorted_dict

def jpp(result):
    from collections import defaultdict

    res = {i: {k} | (set(v) if isinstance(v, list) else {v}) \
          for i, (k, v) in enumerate(results.items())}

    d = defaultdict(set)

    for i, (k, v) in enumerate(res.items()):
        for m, n in d.items():
            if n & v:
                d[m].update(v)
                break
        else:
            d[i] = v

    result = {max(v, key=len): v for k, v in d.items()}
    return result

iterations = 1000000
time1 = timeit.timeit(stmt='imcoins(results)', setup='from __main__ import imcoins, results', number=iterations)
time2 = timeit.timeit(stmt='jpp(results)', setup='from __main__ import jpp, results', number=iterations)

print time1 # Outputs : 17.7903265883
print time2 # Outputs : 23.5605850732

如果我将导入从他的函数移动到全局范围,它会给出......

imcoins : 13.4129249463 秒

jpp : 21.8191823393 秒

【讨论】:

    猜你喜欢
    • 2016-06-20
    • 1970-01-01
    • 1970-01-01
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-12
    • 1970-01-01
    相关资源
    最近更新 更多