合并共享值和键的python字典

合并共享值和键的python字典,python,dictionary,Python,Dictionary,我正在根据字符串编辑距离进行一些实体匹配,结果是一个字典,其中包含键(查询字符串)和基于一些评分标准的值[相似字符串列表] 例如: results = { 'ben' : ['benj', 'benjamin', 'benyamin'], 'benj': ['ben', 'beny', 'benjamin'], 'benjamin': ['benyamin'], 'benyamin': ['benjamin'], 'carl': ['karl'], 'karl': ['c

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

例如:

results = {
  'ben' : ['benj', 'benjamin', 'benyamin'],
  'benj': ['ben', 'beny', 'benjamin'],
  'benjamin': ['benyamin'],
  'benyamin': ['benjamin'],
  'carl': ['karl'],
  'karl': ['carl'],
}
每个值还有一个对应的字典项,它是该字典项的键(例如“卡尔”和“卡尔”)

我需要组合具有共享值的元素。选择一个值作为新键(比如说最长的字符串)。在上述示例中,我希望得到:

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

我尝试过使用键遍历字典,但我不知道如何遍历和比较每个字典项及其值列表(或单个值)

这是一个使用集合.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
的想法归功于@IMCoins

解释

主要有3个步骤:

  • 将值转换为一致的集合格式,包括原始字典中的键和值
  • 循环浏览此词典并向新词典添加值。如果与某些关键点相交[即集合不相交],则使用该关键点。否则,添加到通过枚举确定的新密钥
  • 通过将最大长度键映射到值,在最终转换中创建结果字典

  • 编辑:尽管性能不是这里的问题,我还是冒昧地在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秒

    是否仅当值重叠时才合并列表,还是每个值都应查找匹配的键,然后根据这些值合并?编辑:如果我理解你的例子,我会猜测后者。我应该说,每个值也将是一个键,有自己的字典项。稍微不相关,但你不应该混合字符串和列表作为dict的值-即使你只有一个字符串,它也应该包含在列表中。通过避免特殊情况,这将使您的代码更简单、更健壮。您能展示您尝试过的内容吗?评分标准是否对称?如果
    相似性(a,b)
    是提供评分标准的函数,那么
    相似性(a,b)
    是否返回与
    相似性(b,a)
    相同的结果?(1)是否需要在结果值中输入关键字?对(2) 如果两个或多个值具有相同的最大长度,则使用哪个键。除了使用最常见名字的语料库,我不知道你会如何选择其中一个(接受建议或示例)。谢谢。好的,我已经更新了解决方案以满足包含值的键(不完全是“carl”:“karl”所需的输出)。谢谢jpp,我已经在我的实际数据上运行了这个解决方案,它可以工作。为了我和其他人的利益,请您添加一些基本的代码注释。@BenP我相信您可能希望将他的答案和我的答案结合起来。我认为他的答案是,用
    set()
    计算一个新的dict会浪费不必要的时间,而你只需通过
    d[i]=v
    改变
    d[i]=set(v)
    @IMCoins就可以在第二个循环中完成,我现在明白了——包括你的改进。我还使用了
    isdisjoint
    而不是
    intersection
    ,因为这应该表现得更好。