2013-09-01 74 views
42

我正在通過倒排索引搜索程序。索引本身是一個字典,其中的鍵是術語,其值本身是短文檔的字典,ID編號爲鍵,文本內容爲值。在Python中相交兩個詞典

爲了對兩個詞進行'AND'搜索,我需要將他們的發佈列表(詞典)相交。什麼是明確的(不一定非常聰明)的方式在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 
... 
+0

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

+0

我的上面的評論會與您的詞典詞典相交,但是聯盟合併您的發佈列表....如果您還想將您的發帖列表與您的文檔ID號碼相交,你可以在p1 [term]中使用'{term:{doc_id:p1 [term] [doc_id] for doc_id in p2 [term]} for term in p1} – mtadd

回答

43

您可以輕鬆地計算出的集合的交集,所以創建密鑰集,並利用它們的交集:

keys_a = set(dict_a.keys()) 
keys_b = set(dict_b.keys()) 
intersection = keys_a & keys_b # '&' operator is used for set intersection 
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'] 
+5

爲什麼我要寫所有的代碼?如果我這樣做,我不會在python編碼,但Java! :) –

79

一個鮮爲人知的事實是,你並不需要構建set s到這樣做:

在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與keys替換viewkeys;這同樣適用於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 

對於較大dict s此也略快於構建set秒,然後交叉他們:

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版本中的新功能」 –

+0

不知何故,這最終會比* set(d1.keys())&set(d2 .keys())'方法。但是,我不明白爲什麼會這樣。 – Dan

+0

@Dan:即使它(可能)更慢,它看起來更多* Pythonic *對我來說 –

43
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} 
+8

這應該是答案,因爲這是唯一一個以簡單的方式顯示如何獲得交叉點字典而不是鍵列表的方法。 – Rafe

1

好的,這裏是Python3中以上代碼的通用版本。 它被優化使用足夠快的理解和set-like字典視圖。

功能相交任意許多類型的字典並返回與公共密鑰的字典和每個公用密鑰的一組共同的值:

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'}} 

這裏字典的值必須如果他們不是你可以簡單地將圓括號{}改爲列表[]:

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

我正在向函數傳遞一個字典列表,但它給出錯誤。我如何編輯上面的函數,以便字典的alist被傳遞,並且key:具有常用鍵的值對以及值被獲得? – learnningprogramming

+0

@learnningprogramming,希望你已經想出瞭如何解決這個問題,但是對於其他人來說好奇:'* dicts'作爲函數參數意味着你需要傳遞許多參數,而不是它們的列表。如果你有'lst = [dict1,dict2,dict3,...]'或者使用'dict_intersect(dict1,dict2,dict3,...)'或者解壓列表'dict_intersect(* lst)' – thodnev