[[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]
J'ai une liste de listes. Mon objectif est de vérifier si une sous-liste a quelque chose en commun avec d'autres sous-listes (à l'exclusion du premier objet d'index à comparer). S'il a quelque chose en commun, unifiez ces sous-listes.
Par exemple, pour cet exemple, ma réponse finale devrait être quelque chose comme:
[[1, '34, '44', '40' '30', '41', '42', '43']]
Je peux comprendre que je devrais convertir les sous-listes en ensembles, puis utiliser les opérations union () et intersection (). Mais ce qui me pose problème, c'est de comparer chaque ensemble/sous-liste. Je ne peux pas exécuter une boucle sur la liste et comparer chaque sous-liste une par une car le contenu de la liste serait modifié et cela entraînerait une erreur.
Ce que je veux savoir, est-ce qu'il existe une méthode efficace pour comparer toutes les sous-listes (converties en ensembles) et en obtenir l'union?
Le module itertools résout ce problème:
>>> from itertools import chain
>>> list(set(chain.from_iterable(d)))
[1, '41', '42', '43', '40', '34', '30', '44']
Une autre façon de le faire est de décompresser la liste en arguments séparés pour union () :
>>> list(set().union(*d))
[1, '41', '42', '43', '40', '34', '30', '44']
Cette dernière façon élimine tous les doublons et ne nécessite pas que les entrées soient d'abord converties en ensembles. De plus, il ne nécessite pas d'importation.
Utilisation de l'opérateur déballage *
:
>> list(set.union(*map(set, a)))
[1, '44', '30', '42', '43', '40', '41', '34']
(Merci Raymond Hettinger pour le commentaire!)
(Notez que
set.union(*tup)
déballera à
set.union(tup[0], tup[1], ... tup[n - 1])
)
Personnellement, j'aime la lisibilité de reduce
, associée à une simple fonction conditionnelle, quelque chose comme
somelists = [[1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']] # your original lists
somesets = map(set,somelists) #your lists as sets
def condition(s1,s2): # condition to apply recursively to the sets
if s1.intersection(s2):
return s1.union(s2)
reduce( condition,somesets)
#{1, '30', '34', '40', '41', '42', '43', '44'}
Bien sûr, vous pouvez lancer ce résultat dans une liste 2D si vous le souhaitez list([reduce(...
Je noterai que c'est quelque chose comme 3 fois plus lent que le chain.fromiterable
répondre.
In [20]: s
Out[20]:
[[1, '34', '44'],
[1, '40', '30', '41'],
[1, '41', '40', '42'],
[1, '42', '41', '43'],
[1, '43', '42', '44'],
[1, '44', '34', '43']]
In [31]: list({x for _list in s for x in _list})
Out[31]: [1, '44', '30', '42', '43', '40', '41', '34']
Mettre à jour:
Merci pour les commentaires
Vous pouvez utiliser itertools pour effectuer cette action. Supposons que votre liste ait un nom de variable A
import itertools
single_list_with_all_values = list(itertools.chain(*A))
single_list_with_all_values.sort()
print set(single_list_with_all_values)
>>> big = [[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]
>>> set(reduce ( lambda l,a : l + a, big))
set([1, '44', '30', '42', '43', '40', '41', '34'])
Et si vous voulez vraiment une liste d'une liste comme résultat final
>>>>[list(set(reduce ( lambda l,a : l + a, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]
Et si vous n'aimez pas recoder une fonction lambda pour l'ajout de liste:
>>>>[list(set(reduce ( list.__add__, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]
MODIFIER : après votre recommandation d'utiliser itertools.chain au lieu de list .__ add__ J'ai exécuté un timeit pour les deux avec la variable d'origine utilisée par l'affiche originale.
Il semble que timeit times list .__ add__ environ 2,8 s et itertools.chain environ 3,5 secondes.
J'ai vérifié sur cette page et oui, vous aviez raison avec l'itertools.chain contient une méthode from_iterable qui accorde une énorme amélioration des performances. voir ci-dessous avec la liste .__ add__, itertools.chain et itertools.chain.from_iterable.
>>> timeit.timeit("[list(set(reduce ( list.__add__, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
16.051744650801993
>>> timeit.timeit("[list(set(reduce ( itertools.chain, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
54.721315866467194
>>> timeit.timeit("list(set(itertools.chain.from_iterable(big)))", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
0.040056066849501804
Merci beaucoup pour vos conseils :)