web-dev-qa-db-ja.com

Pythonで2つの辞書を交差させる

私は転置インデックスを介して検索プログラムに取り組んでいます。インデックス自体は、キーが用語であり、値がそれ自体が短いドキュメントの辞書であり、ID番号をキーとして、そのテキストコンテンツを値として持つ辞書です。

したがって、2つの用語の「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
...
59
nicole

セットの交差を簡単に計算できるので、キーからセットを作成し、交差に使用します。

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

少し知られている事実は、これを行うためにsetsを構築する必要がないということです:

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 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を使用すると、少し混乱がなくなります。

104
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}
63
emnoor

Python 3では、使用できます

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

さて、ここに上記のPython3のコードの一般化バージョンがあります。十分に高速な内包表記とセットのような辞書ビューを使用するように最適化されています。

関数は任意の多くの辞書と交差し、共通キーと各共通キーの共通値のセットを持つ辞書を返します。

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}
2
thodnev

必要な値の両方を取得する単純なクラスで辞書インスタンスをラップするだけです

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

あなたの質問は、単一の答えを与えるほど正確ではありません。

1.キー交差点

投稿からIDsと交差したい場合( クレジットto James ):

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.アイテムの交差点

両方の投稿のitemsを交差させる場合、つまりIDsとドキュメントを一致させる場合は、以下のコードを使用します( credits DCPY )へ。ただし、これは用語の重複を探している場合にのみ役立ちます。

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

3. p1 'AND' p2を反復処理します。

'AND' search」でiterを使用して、bothもう一度投稿collections.ChainMapは、複数の投稿の(ほぼ)すべてのアイテムを反復処理するのに最適です。

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

からインデックスを変更することをお勧めします

index = {term: {doc_id: doc}}

用語用の2つのインデックスと、値を保持するための別のインデックス

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

そうすれば、同じデータの複数のコピーを保存しません

0
Aaron Goldman