Подтвердить что ты не робот

Пересечение двух словарей в Python

Я работаю над поисковой программой над инвертированным индексом. Сам индекс - это словарь, ключи которого являются терминами и значения которых сами являются словарями коротких документов, с идентификационными номерами в виде ключей и их текстовым содержимым в качестве значений.

Чтобы выполнить "И" поиск двух терминов, мне, таким образом, нужно пересечь их списки проводок (словари). Что такое явный (не обязательно чрезмерно умный) способ сделать это в 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
...
4b9b3361

Ответ 1

Вы можете легко вычислить пересечение множеств, поэтому создавайте наборы из ключей и используйте их для пересечения:

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

Ответ 2

Малоизвестный факт заключается в том, что для этого не нужно создавать set:

В 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 замените viewkeys на keys; то же самое относится к viewvalues и viewitems.

Из документации 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 items

При больших dict это также немного быстрее, чем построение 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 устраняет бит беспорядка.

Ответ 3

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}

Ответ 4

В Python 3 вы можете использовать

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

Ответ 5

Просто оберните экземпляры словаря простым классом, который получит оба значения, которые вы хотите

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

Ответ 6

Хорошо, вот обобщенная версия кода выше в Python3. Он оптимизирован для использования понятий и представлений типа-типа, которые достаточно быстр.

Функция пересекает произвольное множество dicts и возвращает 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 должны быть хешируемыми, если они не могут быть просто изменены скобки скобок {} на список []:

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

Ответ 7

Ваш вопрос не достаточно точен, чтобы дать один ответ.

1. Пересечение ключей

Если вы хотите пересечь ID из сообщений (кредитов Джеймсу), сделайте:

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

Однако, если вы хотите перебирать документы, вы должны учитывать, какой пост имеет приоритет, я предполагаю, что это p1. Для итерации документов для common_ids, collections.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. Пересечение предметов

Если вы хотите пересекать элементы обеих публикаций, что означает совпадение ID и документов, используйте код ниже (кредиты DCPY). Однако это полезно, только если вы ищете дубликаты в терминах.

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

3. Переберите p1 'AND' p2.

В случае, когда с помощью поиска '' AND 'и использованием iter вы имели в виду поиск по обеим публикациям, а затем снова по collections.ChainMap ChainMap лучше всего перебирать (почти) все элементы в нескольких публикациях:

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