476 votes

Python - Intersection de deux listes

Je sais comment obtenir une intersection de deux listes plates :

b1 = [1,2,3,4,5,9,11,15]
b2 = [4,5,6,7,8]
b3 = [val for val in b1 if val in b2]

ou

def intersect(a, b):
     return list(set(a) & set(b))

print intersect(b1, b2)

Mais lorsque je dois trouver l'intersection pour des listes imbriquées, mes problèmes commencent :

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

A la fin, je voudrais recevoir :

c3 = [[13,32],[7,13,28],[1,6]]

Vous pouvez me donner un coup de main ?

Liens connexes

0 votes

Quelle serait votre intersection pour que c1 croise c2 ? Voulez-vous simplement trouver si c1 est dans c2 ? Ou voulez-vous trouver tous les éléments de c1 qui apparaissent n'importe où dans c2 ?

0 votes

Lire ce et jouer dans l'interpréteur.

897voto

S.Lott Points 207588

Vous n'avez pas besoin de définir l'intersection. C'est déjà une partie de première classe de l'ensemble.

>>> b1 = [1,2,3,4,5,9,11,15]
>>> b2 = [4,5,6,7,8]
>>> set(b1).intersection( set(b2) )
set([4, 5])

3 votes

Est-ce que cela sera plus lent que lambda à cause de la conversion en set ?

34 votes

@S.Lott, quelque chose de mal avec set(b1) & set(b2) ? Il est plus propre d'utiliser l'opérateur.

0 votes

Neat. Je ne savais pas que tu pouvais utiliser le & sur les plateaux. Apparemment, vous pouvez aussi utiliser ^

179voto

Brian R. Bondy Points 141769

Si tu veux :

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]
c3 = [[13, 32], [7, 13, 28], [1,6]]

Alors voici votre solution :

c3 = [filter(lambda x: x in c1, sublist) for sublist in c2]

Explication :

La partie filtre prend l'élément de chaque sous-liste et vérifie s'il se trouve dans la liste source c1. La compréhension de la liste est exécutée pour chaque sous-liste de c2.

35 votes

Vous pouvez utiliser filter(set(c1).__contains__, sublist) pour plus d'efficacité. En fait, l'avantage de cette solution est que filter() préserve les types de chaînes et de tuples.

3 votes

j'aime cette méthode, mais j'obtiens des '' vides dans ma liste de résultats

0 votes

J'ai ajouté la compatibilité avec Python 3 ici, puisque je l'utilise comme cible de duplication pour une duplication d'une question sur Python 3.

61voto

Zachary Burt Points 1252

Pour les personnes qui cherchent simplement à trouver l'intersection de deux listes, l'Asker a fourni deux méthodes :

b1 = [1,2,3,4,5,9,11,15]
b2 = [4,5,6,7,8]
b3 = [val for val in b1 if val in b2]

et

def intersect(a, b):
     return list(set(a) & set(b))

print intersect(b1, b2)

Mais il existe une méthode hybride qui est plus efficace, car vous ne devez effectuer qu'une seule conversion entre liste et ensemble, au lieu de trois :

b1 = [1,2,3,4,5]
b2 = [3,4,5,6]
s2 = set(b2)
b3 = [val for val in b1 if val in s2]

Cette méthode s'exécutera en O(n), alors que sa méthode originale impliquant la compréhension des listes s'exécutera en O(n^2).

0 votes

Comme "if val in s2" s'exécute en O(N), la complexité de l'extrait de code proposé est également O(n^2).

8 votes

Le cas moyen de " val dans s2 " est O(1) selon wiki.python.org/moin/TimeComplexity#set - Ainsi, sur n opérations, le temps attendu est O(n) (que le temps le plus défavorable soit O(n) ou O(n^2) dépend du fait que ce cas moyen représente un temps amorti ou non, mais ce n'est pas très important en pratique).

2 votes

Le temps d'exécution est O(N) non pas parce qu'il est amorti mais parce que l'appartenance à un ensemble est en moyenne O(1) (par exemple lorsqu'on utilise une table de hachage), c'est une grande différence, par exemple parce que le temps amorti est garanti.

30voto

pufferfish Points 3512

L'approche fonctionnelle :

input_list = [[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7]]

result = reduce(set.intersection,map(set,input_list))

et on peut l'appliquer au cas plus général des listes 1+.

0 votes

pour permettre une liste d'entrée vide : set(*input_list[:1]).intersection(*input_list[1:]) . Version de l'itérateur ( it = iter(input_list) ) : reduce(set.intersection, it, set(next(it, []))) . Les deux versions ne nécessitent pas de convertir toutes les listes d'entrée en ensembles. La dernière version est plus efficace en termes de mémoire.

0 votes

Utilisez from functools import reduce pour l'utiliser dans Python 3. Ou mieux encore, utilisez un for boucle.

27voto

J.F. Sebastian Points 102961

Version pure de la compréhension des listes

>>> c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
>>> c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]
>>> c1set = frozenset(c1)

Aplatir la variante :

>>> [n for lst in c2 for n in lst if n in c1set]
[13, 32, 7, 13, 28, 1, 6]

Variante emboîtée :

>>> [[n for n in lst if n in c1set] for lst in c2]
[[13, 32], [7, 13, 28], [1, 6]]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X