合并共享值和键的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
,因为这应该表现得更好。