在Python中相交两个字典


74

我正在研究一个反向索引的搜索程序。索引本身是一个字典,其键是术语,其值本身是短文档的字典,ID号是键,其文本内容是值。

为了对两个词执行“与”搜索,因此我需要与他们的发布列表(字典)相交。在Python中有什么明确的方法(不一定太聪明)?我首先尝试了以下方法iter

p1 = index[term1]  
p2 = index[term2]
i1 = iter(p1)
i2 = iter(p2)
while ...  # not sure of the 'iter != end 'syntax in this case
...

{i:dict(p1 [i],** p2 [i])for i in p1 if i in p2}
mtadd 2013年

我上面的评论将与您的术语词典相交,但是合并合并您的过帐清单....如果您还希望在文档ID号上与您的过帐清单相交,则可以使用{term:{doc_id:p1[term][doc_id] for doc_id in p1[term] if doc_id in p2[term]} for term in p1 if term in p2}
mtadd

2
现在还不清楚您想要的输出是什么(甚至您的输入是什么)。从对问题的描述来看,这听起来像是您想要嵌套的词典……“相交”,无论这意味着什么。但是您接受的答案不仅不会与嵌套字典相交,甚至不会与字典相交。它所做的只是将两个字典的相交。请澄清您的问题,并以最小的可复制示例进行更新。
阿兰·菲

Answers:


92

在Python中,您可以使用&运算符来计算集合的交集,而字典键是类似于集合的对象(在Python 3中):

dict_a = {"a": 1, "b": 2}
dict_b = {"a": 2, "c": 3} 

intersection = dict_a.keys() & dict_b.keys()  # {'a'}

在Python 2上,您必须将字典键转换为自己设置:

keys_a = set(dict_a.keys())
keys_b = set(dict_b.keys())
intersection = keys_a & keys_b

115

一个鲜为人知的事实是,您无需构造sets就可以做到这一点:

在Python 2中:

In [78]: d1 = {'a': 1, 'b': 2}

In [79]: d2 = {'b': 2, 'c': 3}

In [80]: d1.viewkeys() & d2.viewkeys()
Out[80]: {'b'}

在Python 3中替换viewkeyskeys; 这同样适用于viewvaluesviewitems

从以下文档中viewitems

In [113]: d1.viewitems??
Type:       builtin_function_or_method
String Form:<built-in method viewitems of dict object at 0x64a61b0>
Docstring:  D.viewitems() -> a set-like object providing a view on D's items

对于更大的dicts,这也比构造sets然后相交的速度稍快:

In [122]: d1 = {i: rand() for i in range(10000)}

In [123]: d2 = {i: rand() for i in range(10000)}

In [124]: timeit d1.viewkeys() & d2.viewkeys()
1000 loops, best of 3: 714 µs per loop

In [125]: %%timeit
s1 = set(d1)
s2 = set(d2)
res = s1 & s2

1000 loops, best of 3: 805 µs per loop

For smaller `dict`s `set` construction is faster:

In [126]: d1 = {'a': 1, 'b': 2}

In [127]: d2 = {'b': 2, 'c': 3}

In [128]: timeit d1.viewkeys() & d2.viewkeys()
1000000 loops, best of 3: 591 ns per loop

In [129]: %%timeit
s1 = set(d1)
s2 = set(d2)
res = s1 & s2

1000000 loops, best of 3: 477 ns per loop

我们在这里比较纳秒,这可能对您无关紧要。无论如何,您都会得到一个set,因此使用viewkeys/可以keys消除一些混乱。


4
viewkeys()是“ 2.7版的新功能”
Dennis Williamson

2
以某种方式,这最终是非常轻微(〜12%)要慢来计算比set(d1.keys()) & set(d2.keys())方法。但是,我不知道为什么会这样。

@Dan:即使(可能)速度较慢,它对我来说也更像Python风格
Azat Ibrakov

使用future(pip install future)包,以下代码在python 2和3中from future.utils import viewkeys; viewkeys(d1) & viewkeys(d2)
起作用

78
In [1]: d1 = {'a':1, 'b':4, 'f':3}

In [2]: d2 = {'a':1, 'b':4, 'd':2}

In [3]: d = {x:d1[x] for x in d1 if x in d2}

In [4]: d
Out[4]: {'a': 1, 'b': 4}

16
这应该是答案,因为这是唯一显示如何以简单方式获取交集字典而不是键列表的答案。
拉菲2014年

20

在Python 3中,您可以使用

intersection = dict(dict1.items() & dict2.items())
union = dict(dict1.items() | dict2.items())
difference = dict(dict1.items() ^ dict2.items())

2
对于dict1 = {1: 1, 2:2}dict2 = {2:4, 3:3}intersection == set()。可能这不是OP想要的。
WloHu

@JCode我不确定我是否理解。intersection = dict(...)将其转换回dict。此外,我只是测试它dict1 = {1: 1, 2: 2}dict2 = {2: 4, 3: 3}(字典同上),并且intersection == set()False。这是一本空字典。
dccsillag

@DCPY抱歉,我错过了dict(...)。关键是给定我的示例,结果为空。.items()除非您要查找原义重复项,否则考虑OP接受什么和更常见的用例交叉是没有意义的。
WloHu

@JCode在这种情况下,可以安全地假设所有值都是唯一的。然后,dict1使用{v: k for k, v in dict1.items()}和代替dict2use {v: k for k, v in dict2.items()}。而且,如果您希望使用键字典,请使用答案中给出的交集和此注释中给出的交集的并集。
dccsillag

2

只需用一个简单的类包装字典实例,该类即可获取您想要的两个值

class DictionaryIntersection(object):
    def __init__(self,dictA,dictB):
        self.dictA = dictA
        self.dictB = dictB

    def __getitem__(self,attr):
        if attr not in self.dictA or attr not in self.dictB:
            raise KeyError('Not in both dictionaries,key: %s' % attr)

        return self.dictA[attr],self.dictB[attr]

x = {'foo' : 5, 'bar' :6}
y = {'bar' : 'meow' , 'qux' : 8}

z = DictionaryIntersection(x,y)

print z['bar']

11
我为什么要编写所有这些代码?如果我这样做了,那我就不会用python编码,而是用java编码!:)
罗伯特·莫斯卡尔

2

好的,这是上面Python3中代码的通用版本。它经过优化,可以使用足够快的理解力和类似集合的字典视图。

函数与任意许多dict相交,并返回一个包含公共键和每个公共键的一组公共值的dict:

def dict_intersect(*dicts):
    comm_keys = dicts[0].keys()
    for d in dicts[1:]:
        # intersect keys first
        comm_keys &= d.keys()
    # then build a result dict with nested comprehension
    result = {key:{d[key] for d in dicts} for key in comm_keys}
    return result

用法示例:

a = {1: 'ba', 2: 'boon', 3: 'spam', 4:'eggs'}
b = {1: 'ham', 2:'baboon', 3: 'sausages'}
c = {1: 'more eggs', 3: 'cabbage'}

res = dict_intersect(a, b, c)
# Here is res (the order of values may vary) :
# {1: {'ham', 'more eggs', 'ba'}, 3: {'spam', 'sausages', 'cabbage'}}

这里的dict值必须是可哈希的,如果不是,则可以简单地将集合括号{}更改为list []:

result = {key:[d[key] for d in dicts] for key in comm_keys}

我将字典列表传递给函数,但给出错误。我如何编辑上面的函数,以便传递字典列表,并获得具有公用键和值的key:value对?
学习programmingprogramming

@learnningprogramming,希望您已经找到了解决问题的方法,但对其他好奇的人:*dicts函数参数意味着您需要传递大量参数,而不是它们的列表。如果您lst = [dict1, dict2, dict3, ...]使用dict_intersect(dict1, dict2, dict3, ...)过清单或将清单拆开dict_intersect(*lst)
thodnev

0

您的问题不够精确,无法给出一个答案。

1.关键交集

如果您想与ID帖子相交(致谢James),请执行以下操作:

common_ids = p1.keys() & p2.keys()

但是,如果您要遍历文档,则必须考虑哪个帖子具有优先级,我认为是p1。要迭代的文档common_idscollections.ChainMap将最有用:

from collections import ChainMap
intersection = {id: document
                for id, document in ChainMap(p1, p2)
                if id in common_ids}
for id, document in intersection:
    ...

或者,如果您不想创建单独的intersection字典:

from collections import ChainMap
posts = ChainMap(p1, p2)
for id in common_ids:
    document = posts[id]

2.项目交集

如果要与两个帖子的项目相交,这意味着要匹配IDs和文档,请使用下面的代码(感谢DCPY)。但是,这仅在您要查找术语重复项时才有用。

duplicates = dict(p1.items() & p2.items())
for id, document in duplicates:
    ...

3.遍历p1“与” p2

如果通过“与”搜索并使用iter您来搜索两个帖子,那么collections.ChainMap最好再遍历(几乎)多个帖子中的所有项目:

from collections import ChainMap
for id, document in ChainMap(p1, p2):
    ...

0
def two_keys(term_a, term_b, index):
    doc_ids = set(index[term_a].keys()) & set(index[term_b].keys())
    doc_store = index[term_a] # index[term_b] would work also
    return {doc_id: doc_store[doc_id] for doc_id in doc_ids}

def n_keys(terms, index):
    doc_ids = set.intersection(*[set(index[term].keys()) for term in terms])
    doc_store = index[term[0]]
    return {doc_id: doc_store[doc_id] for doc_id in doc_ids}

In [0]: index = {'a': {1: 'a b'}, 
                 'b': {1: 'a b'}}

In [1]: two_keys('a','b', index)
Out[1]: {1: 'a b'}

In [2]: n_keys(['a','b'], index)
Out[2]: {1: 'a b'}

我建议您将索引从

index = {term: {doc_id: doc}}

到两个索引,一个用于术语,然后一个单独的索引,用于保存值

term_index = {term: set([doc_id])}
doc_store = {doc_id: doc}

这样,您就不会存储同一数据的多个副本

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.