webentwicklung-frage-antwort-db.com.de

Zwei Wörterbücher in Python schneiden

Ich arbeite an einem Suchprogramm über einen invertierten Index. Der Index selbst ist ein Wörterbuch, dessen Schlüssel Begriffe sind und dessen Werte selbst Wörterbücher kurzer Dokumente sind, mit ID-Nummern als Schlüssel und deren Textinhalt als Werte. 

Um eine UND-Suche nach zwei Begriffen durchzuführen, muss ich deren Buchungslisten (Wörterbücher) schneiden. Was ist ein klarer (nicht unbedingt zu schlauer) Weg, dies in Python zu tun? Ich habe es mit iter ausprobiert:

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

Sie können die Schnittmenge von Mengen einfach berechnen, erstellen Sie also aus den Schlüsseln Mengen und verwenden Sie sie für die Schnittmenge:

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

Eine wenig bekannte Tatsache ist, dass Sie nicht sets erstellen müssen, um 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 Sie viewkeys durch 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 dicts ist dies auch etwas schneller als das Erstellen von sets und das Überschneiden dieser:

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

Wir vergleichen hier Nanosekunden, die Ihnen vielleicht egal sind. In jedem Fall erhalten Sie eine set zurück, so dass die Verwendung von viewkeys/keys etwas Unordnung beseitigt.

101
Phillip Cloud
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}
60
emnoor

In Python 3 können Sie verwenden

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

Okay, hier ist eine verallgemeinerte Version von Code in Python3 .. _, die so optimiert ist, dass sie Verständnisfunktionen und satzähnliche Ansichten verwendet, die schnell genug sind.

Die Funktion schneidet beliebig viele Diktate und gibt ein Dikt mit allgemeinen Schlüsseln und einem Satz allgemeiner Werte für jeden gemeinsamen Schlüssel zurück:

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 müssen die Dict-Werte hashierbar sein, andernfalls können Sie set-Klammern {} einfach in list [] ändern:

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

Umschließen Sie die Wörterbuchinstanzen einfach mit einer einfachen Klasse, die beide gewünschten Werte abruft

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']
2
Eric Urban

Ihre Frage ist nicht präzise genug, um eine einzige Antwort zu geben.

1. Schlüsselkreuzung

Wenn Sie IDs von Beiträgen ( Credits zu James ) schneiden möchten, machen Sie Folgendes:

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

Wenn Sie jedoch Dokumente durchlaufen möchten, müssen Sie berücksichtigen, welcher Beitrag eine Priorität hat. Ich gehe davon aus, dass es p1 ist. Um Dokumente für common_ids zu durchlaufen, ist collections.ChainMap am nützlichsten:

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

Oder wenn Sie kein separates intersection-Wörterbuch erstellen möchten:

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

2. Schnittpunkt der Elemente

Wenn Sie items beider Posts überschneiden möchten, dh IDs und Dokumente abgleichen möchten, verwenden Sie den nachstehenden Code ( credits zu DCPY ). Dies ist jedoch nur nützlich, wenn Sie nach Duplikaten suchen.

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

3. Iteriere über p1 'AND' p2.

Falls Sie durch "'AND' Suche" und mit iterbeide Beiträge durchsuchen wollten, ist collections.ChainMap am besten, um (fast) alle Elemente in mehreren Beiträgen zu durchlaufen:

from collections import ChainMap
for id, document in ChainMap(p1, p2):
    ...
0
WloHu
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'}

Ich würde empfehlen, Ihren Index von zu ändern

index = {term: {doc_id: doc}}

auf zwei Indizes eins für die Begriffe und dann einen separaten Index für die Werte

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

auf diese Weise speichern Sie nicht mehrere Kopien derselben Daten

0
Aaron Goldman