在Python中,计算字典中的唯一键/值对

时间:2014-01-06 07:33:43

标签: python dictionary replace pattern-matching

我有一个用值列表制作的字典。其中一些值也是字典中其他键/值对中的键或值。我只想计算字典中有多少这些独特的对。

实施例。 dict = {'dog':['milo','otis','laurel','hardy'],'cat':['bob','joe'],'milo':['otis','laurel','hardy','dog'],'bob':['cat','joe'],'hardy':['dog']}

我需要计算没有在dict中与另一个共享键/值的键/值对的数量。例如,上面应该只计算2,那些连接到狗和猫。尽管milo对于狗来说是独一无二的,但是狗也是'hardy'的关键/值对,因此这两者应该一起计算(即只有1)。 (见下面的评论) 我试图通过用'key B'替换另一个键(键B)的值中存在的键(键A)来实现它,但没有成功,因为我无法正确指定键B.

for keys, values in dict.iteritems():

    for key,value in dict.iteriterms():
            if key in values:
                dict[keys] = dict.pop(key)

有更简单的方法吗? 提前谢谢......

3 个答案:

答案 0 :(得分:2)

如果我正确理解了问题,您的词典就是图表的邻接地图,而您正试图找到connected components的集合。常规算法(使用深度或广度优先搜索)可能无法正常工作,因为您的图表不是无向的(例如,您有"bob""cat""joe"的边缘,但没有来自"joe")。

相反,我建议使用disjoint set data structure。使用字典构建一个来处理值到父级的映射并不困难。这是我为前一个问题写的一个实现:

class DisjointSet:
    def __init__(self):
        self.parent = {}
        self.rank = {}

    def find(self, element):
        if element not in self.parent: # leader elements are not in `parent` dict
            return element
        leader = self.find(self.parent[element]) # search recursively
        self.parent[element] = leader # compress path by saving leader as parent
        return leader

    def union(self, leader1, leader2):
        rank1 = self.rank.get(leader1,0)
        rank2 = self.rank.get(leader2,0)

        if rank1 > rank2: # union by rank
            self.parent[leader2] = leader1
        elif rank2 > rank1:
            self.parent[leader1] = leader2
        else: # ranks are equal
            self.parent[leader2] = leader1 # favor leader1 arbitrarily
            self.rank[leader1] = rank1+1 # increment rank

以下是您可以用它来解决问题的方法:

djs = DisjointSet()
all_values = set()
for key, values in my_dict.items():
    all_values.add(key)
    all_values.update(values)
    for val in values:
        l1 = djs.find(key)
        l2 = djs.find(val)
        if l1 != l2:
            djs.union(l1, l2)

roots = {djs.find(x) for x in all_values}
print("The number of disjoint sets is:", len(roots))

这段代码的第一部分做了两件事。首先,它构建一个集合,其中包含图形中任何位置的所有唯一节点。其次,它通过在任何有边缘的地方进行并集来将节点组合成不相交的集合。

第二步是从不相交的集合中构建一组“根”元素。

答案 1 :(得分:1)

以下是一种可能的解决方案:

values = {'dog':['milo','otis','laurel','hardy'],
          'cat':['bob','joe'],
          'milo':['otis','laurel','hardy','dog'],
          'bob':['cat','joe'],
          'hardy':['dog']}

result = []

for x in values.iteritems():
    y = set([x[0]] + x[1])
    if not any([z for z in result if z.intersection(y)]):
        result.append(y)

print len(result)

请注意,您不应该调用变量dict,因为您要隐藏内置类型dict

您的目标不明确,但您可以修改y set的构造以满足您的需求。

答案 2 :(得分:0)

如果我正确理解了您的问题,那么您正在尝试描述类似图形的结构,并且您正在查看键是否出现在值列表中。既然你只对count感兴趣,那么在迭代dict时你不必担心将来的值列表,所以这应该有效:

d = {'dog': ['milo','otis','laurel','hardy'],'cat': ['bob','joe'],'milo': 'otis','laurel','hardy','dog'], 'bob': ['cat','joe'], 'hardy': ['dog']}
seen = set()
unique = []
for key, values in d.iteritems():
    if key not in seen:
        unique.append(key)
    seen = seen.union(values)
print(len(unique))

请注意,unique中包含的实际值取决于字典顺序,只是键,而不是值。如果您实际上尝试进行某种网络或图形分析,我建议您使用networkx

等库。