2013-09-01 20 views
42

Ich arbeite an einem Suchprogramm über einen invertierten Index. Der Index selbst ist ein Wörterbuch, dessen Schlüssel Begriffe sind und deren Werte selbst Wörterbücher von kurzen Dokumenten sind, mit ID-Nummern als Schlüssel und deren Textinhalt als Werte.Schneiden von zwei Wörterbüchern in Python

Um eine UND-Suche nach zwei Begriffen durchzuführen, muss ich ihre Buchungslisten (Wörterbücher) durchschneiden. Was ist eine klare (nicht unbedingt übertrieben schlaue) Art, dies in Python zu tun? Ich begann, indem es den langen Weg mit iter versuchen:

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]) für i in p1 wenn ich in p2} – mtadd

+0

Mein obiger Kommentar wird Ihre Wörterbücher durchschneiden, aber Ihre Buchungslisten zusammenführen ... wenn Sie Ihre Buchungslisten auch auf Ihren Dokument-ID-Nummern schneiden möchten , Sie können '{term: {doc_id: p1 [term] [doc_id] für doc_id in p1 [term] verwenden doc_id in p2 [term]} für term in p1 wenn term in p2}' – mtadd

Antwort

43

Sie den Durchschnitt von Mengen leicht berechnen kann, so erstellen Sets von den Tasten und für die Kreuzung verwenden:

keys_a = set(dict_a.keys()) 
keys_b = set(dict_b.keys()) 
intersection = keys_a & keys_b # '&' operator is used for set intersection 
2

einfach wickeln die Wörterbuch-Instanzen mit einer einfachen Klasse, die Sie

beide Werte bekommt wollen
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

Warum sollte ich schreiben wollen all dieser Code?Wenn ich das täte, würde ich nicht in Python, sondern Java schreiben! :) –

79

eine wenig bekannte Tatsache ist, dass Sie nichtkonstruieren müssens, dies zu tun:

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

In Python 3 ersetzen viewkeys mit keys; Gleiches gilt für viewvalues und viewitems.

Aus der Dokumentation von 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 

Für größere dict s dies auch etwas schneller als set s konstruieren und sie dann sich schneid:

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 

hier, Wir vergleichen ns EVENTUELL oder ist dir egal. In jedem Fall erhalten Sie eine set zurück, so dass die Verwendung von viewkeys/keys ein bisschen Unordnung beseitigt.

+4

'viewkeys()' ist "Neu in Version 2.7" –

+0

Irgendwie ist das * * so leicht * (~ 12%) langsamer zu berechnen als das 'set (d1.keys()) & set (d2 .keys()) Methode. Ich verstehe jedoch nicht, warum das so wäre. – Dan

+0

@Dan: auch wenn es (wahrscheinlich) langsamer ist sieht es mehr aus * Pythonic * für mich –

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

Dies sollte die Antwort sein, da dies die einzige ist, die zeigt, wie man ein Schnittstellendiktat, nicht Listen von Schlüsseln, auf einfache Weise erhält. – Rafe

1

Okay, hier ist eine verallgemeinerte Version des obigen Codes in Python3. Es ist optimiert für die Verwendung von Comprehensions und set-like dict views, die schnell genug sind.

Funktion schneidet beliebig viele dicts und gibt einen dict mit gemeinsamen Schlüssel und eine Reihe gemeinsamer Werte für jeden gemeinsamen Schlüssel:

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 

Anwendungsbeispiel:

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

Hier werden die dict Werte sein müssen hashable, wenn sie nicht sind, können Sie einfach die gesetzten Klammern {} ändern, um [] aufzulisten:

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

Ich übergebe eine Liste von Diktat an die Funktion, aber es gibt Fehler. Wie kann ich über Funktion bearbeiten, so dass Alist von Diktat übergeben wird, und Schlüssel: Wert-Paar mit gemeinsamen Schlüsseln sowie Wert erhalten? – learnningprogramming

+0

@learnningprogramming, ich hoffe, Sie haben bereits herausgefunden, wie Sie das Problem lösen können, aber für andere ist es seltsam: '* dicts' als Funktionsargumente bedeutet, dass Sie zahlreiche Argumente übergeben müssen, nicht eine Liste davon. Wenn Sie 'lst = [dict1, dict2, dict3, ...]' haben, benutzen Sie 'dict_intersect (dict1, dict2, dict3, ...)' 'oder entpacken Sie die Liste' dict_intersect (* lst) ' – thodnev

Verwandte Themen