web-dev-qa-db-fra.com

Le meilleur moyen de trouver l'intersection de plusieurs ensembles?

J'ai une liste d'ensembles:

setlist = [s1,s2,s3...]

Je veux s1 ∩ s2 s3 ...

Je peux écrire une fonction pour le faire en effectuant une série de s1.intersection(s2) par paire, etc.

Existe-t-il un moyen recommandé, meilleur ou intégré?

225
user116293

À partir de Python version 2.6, vous pouvez utiliser plusieurs arguments pour set.intersection() , comme

u = set.intersection(s1, s2, s3)

Si les ensembles sont dans une liste, cela se traduit par:

u = set.intersection(*setlist)

*a_list est extension de la liste

393
sth

À partir de la version 2.6, set.intersection prend arbitrairement plusieurs itérables.

>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s3 = set([2, 4, 6])
>>> s1 & s2 & s3
set([2])
>>> s1.intersection(s2, s3)
set([2])
>>> sets = [s1, s2, s3]
>>> set.intersection(*sets)
set([2])
62
Mike Graham

Clairement, set.intersection est ce que vous voulez ici, mais au cas où vous auriez besoin d’une généralisation de "prenez la somme de tous ces", "prenez le produit de tous", "prenez le xor de tous ces", recherchons est la fonction reduce:

from operator import and_
from functools import reduce
print(reduce(and_, [{1,2,3},{2,3,4},{3,4,5}])) # = {3}

ou

print(reduce((lambda x,y: x&y), [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
18
Thomas Ahle

Si vous n'avez pas Python 2.6 ou supérieur, l'alternative consiste à écrire une boucle for explicite:

def set_list_intersection(set_list):
  if not set_list:
    return set()
  result = set_list[0]
  for s in set_list[1:]:
    result &= s
  return result

set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print set_list_intersection(set_list)
# Output: set([1])

Vous pouvez également utiliser reduce:

set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print reduce(lambda s1, s2: s1 & s2, set_list)
# Output: set([1])

Cependant, de nombreux Python programmeurs ne l'aiment pas, y compris Guido lui-même :

Il y a environ 12 ans, Python a acquis lambda, reduction (), filter () et map (), avec la permission de (je crois) un pirate informatique LISP qui les avait manqués et avait soumis des correctifs de travail. Mais, malgré la valeur de PR, je pense que ces fonctionnalités devraient être supprimées de Python 3000.

Alors maintenant, réduisez (). C’est en fait celui que j’ai le plus détesté, car, mis à part quelques exemples impliquant + ou *, presque chaque fois que je vois un appel de reduction () avec un argument de fonction non trivial, il me faut un stylo et du papier pour diagramme ce qui est réellement introduit dans cette fonction avant que je comprenne ce que la réduction () est censé faire. Donc, dans mon esprit, l'applicabilité de reduction () est plutôt limitée aux opérateurs associatifs, et dans tous les autres cas, il est préférable d'écrire explicitement la boucle d'accumulation.

11
Ayman Hourieh

Ici, je propose une fonction générique pour l'intersection de plusieurs ensembles en essayant de tirer parti de la meilleure méthode disponible:

def multiple_set_intersection(*sets):
    """Return multiple set intersection."""
    try:
        return set.intersection(*sets)
    except TypeError: # this is Python < 2.6 or no arguments
        pass

    try: a_set= sets[0]
    except IndexError: # no arguments
        return set() # return empty set

    return reduce(a_set.intersection, sets[1:])

Guido pourrait ne pas aimer reduce, mais je l'aime bien :)

1
tzot