Je travaille sur un programme de recherche sur un index inversé. L'index lui-même est un dictionnaire dont les clés sont des termes et dont les valeurs sont elles-mêmes des dictionnaires de documents courts, avec les numéros d'identification comme clés et leur contenu textuel comme valeurs.
Pour effectuer une recherche 'AND' de deux termes, il me faut donc croiser leurs listes d’affichage (dictionnaires). Qu'est-ce qu'un moyen clair (pas nécessairement trop intelligent) de faire cela en Python? J'ai commencé par essayer le long chemin avec iter
:
p1 = index[term1]
p2 = index[term2]
i1 = iter(p1)
i2 = iter(p2)
while ... # not sure of the 'iter != end 'syntax in this case
...
Vous pouvez facilement calculer l'intersection d'ensembles, créez donc des ensembles à partir des clés et utilisez-les pour l'intersection:
keys_a = set(dict_a.keys())
keys_b = set(dict_b.keys())
intersection = keys_a & keys_b # '&' operator is used for set intersection
Un fait peu connu est qu'il n'est pas nécessaire de construire set
s pour faire ceci:
En 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'}
En Python 3, remplacez viewkeys
par keys
; il en va de même pour viewvalues
et viewitems
.
De la documentation de 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
Pour les variables dict
s plus grandes, cette opération est également légèrement plus rapide que la construction de set
s et leur intersection:
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
Nous comparons ici des nanosecondes, qui peuvent vous intéresser ou non. Dans tous les cas, vous récupérez une set
, donc utiliser viewkeys
/keys
élimine un peu l'encombrement.
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}
En Python 3, vous pouvez utiliser
intersection = dict(dict1.items() & dict2.items())
union = dict(dict1.items() | dict2.items())
difference = dict(dict1.items() ^ dict2.items())
Ok, voici une version généralisée du code ci-dessus dans Python3 . Elle est optimisée pour utiliser des compréhensions et des vues dict-set assez rapides.
Function intersecte de nombreux dictionnaires de manière arbitraire et renvoie un dict avec des clés communes et un ensemble de valeurs communes pour chaque clé commune:
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
Exemple d'utilisation:
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'}}
Ici, les valeurs dict doivent être haschibles. Sinon, vous pouvez simplement changer les parenthèses {} en listant []:
result = {key:[d[key] for d in dicts] for key in comm_keys}
Entourez simplement les instances du dictionnaire avec une classe simple qui obtient les deux valeurs souhaitées
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']
Votre question n'est pas assez précise pour donner une réponse unique.
Si vous voulez intersecter ID
s de posts ( crédits à James ), faites:
common_ids = p1.keys() & p2.keys()
Toutefois, si vous souhaitez parcourir les documents, vous devez déterminer quelle publication est prioritaire. Je suppose que c'est p1
. Pour itérer des documents pour common_ids
, collections.ChainMap
sera le plus utile:
from collections import ChainMap
intersection = {id: document
for id, document in ChainMap(p1, p2)
if id in common_ids}
for id, document in intersection:
...
Ou si vous ne souhaitez pas créer un dictionnaire intersection
séparé:
from collections import ChainMap
posts = ChainMap(p1, p2)
for id in common_ids:
document = posts[id]
Si vous souhaitez intersecter éléments des deux publications, ce qui signifie que vous devez faire correspondre les noms ID
s et les documents, utilisez le code ci-dessous ( crédits de DCPY ). Toutefois, cela n’est utile que si vous recherchez des doublons en termes.
duplicates = dict(p1.items() & p2.items())
for id, document in duplicates:
...
p1
'AND' p2
.Dans le cas où par "'AND' search" et en utilisant iter
, vous vouliez effectuer une recherche dans les deux posts puis à nouveau collections.ChainMap
est le meilleur moyen de parcourir tous les éléments de plusieurs posts:
from collections import ChainMap
for id, document in ChainMap(p1, p2):
...
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'}
Je recommanderais de changer votre index de
index = {term: {doc_id: doc}}
à deux index un pour les termes, puis un index séparé pour contenir les valeurs
term_index = {term: set([doc_id])}
doc_store = {doc_id: doc}
de cette façon, vous ne stockez pas plusieurs copies des mêmes données.