Existe-t-il une fonction intégrée qui supprime les doublons de la liste en Python, tout en préservant l'ordre? Je sais que je peux utiliser un ensemble pour supprimer les doublons, mais cela détruit l'ordre d'origine. Je sais aussi que je peux rouler le mien comme ceci:
def uniq(input):
output = []
for x in input:
if x not in output:
output.append(x)
return output
(Merci de vous détendre pour cet exemple de code .)
Mais je voudrais me prévaloir d'un idiome intégré ou plus Pythonic si possible.
Question connexe: en Python, quel est l'algorithme le plus rapide pour supprimer les doublons d'une liste afin que tous les éléments soient uniques tout en préservant l'ordre ?
la source
seen.add
aurait pu changer entre les itérations, et le temps d'exécution n'est pas assez intelligent pour exclure cela. Pour jouer en toute sécurité, il doit vérifier l'objet à chaque fois. - Si vous regardez le bytecode avecdis.dis(f)
, vous pouvez voir qu'il s'exécuteLOAD_ATTR
pour leadd
membre à chaque itération. ideone.com/tz1Tllseen_add
est une amélioration, mais les délais peuvent être affectés par les ressources système à l'époque. Serait intéressé de voir les horaires completsseen_add = seen.add
ne produisent qu'une augmentation de 1% de la vitesse. Ce n'est guère significatif.Modifier 2016
Comme l'a souligné Raymond , en python 3.5+ où
OrderedDict
est implémenté en C, l'approche de compréhension de liste sera plus lente queOrderedDict
(sauf si vous avez réellement besoin de la liste à la fin - et même alors, seulement si l'entrée est très courte). La meilleure solution pour 3.5+ est doncOrderedDict
.Édition importante 2015
Comme le note @abarnert , la
more_itertools
bibliothèque (pip install more_itertools
) contient uneunique_everseen
fonction conçue pour résoudre ce problème sans aucune mutation illisible (not seen.add
) dans les compréhensions de liste. C'est aussi la solution la plus rapide:Une seule importation de bibliothèque simple et aucun piratage. Cela provient d'une implémentation de la recette itertools
unique_everseen
qui ressemble à:En Python,
2.7+
l'idiome commun accepté(qui fonctionne mais n'est pas optimisé pour la vitesse, je l'utiliserais maintenantunique_everseen
) pour cette utilisationcollections.OrderedDict
:Durée: O (N)
Cela semble beaucoup plus agréable que:
et n'utilise pas le hack laid :
qui s'appuie sur le fait qu'il
set.add
s'agit d'une méthode sur place qui renvoie toujoursNone
donc estnot None
évaluéeTrue
.Notez cependant que la solution de hack est plus rapide en vitesse brute bien qu'elle ait la même complexité d'exécution O (N).
la source
[seen.add(x) for x in seq if x not in seen]
, ou si vous n'aimez pas les effets secondaires de compréhension, utilisez simplement unefor
boucle:for x in seq: seen.add(x) if x not in seen else None
(toujours un one-liner, bien que dans ce cas je pense que le one-liner-ness est une propriété idiote à essayer d'avoir dans un solution.seen = set(seq)
.Dans Python 2.7 , la nouvelle façon de supprimer les doublons d'un itérable tout en le conservant dans l'ordre d'origine est:
Dans Python 3.5 , OrderedDict a une implémentation C. Mes synchronisations montrent que c'est maintenant à la fois la plus rapide et la plus courte des différentes approches pour Python 3.5.
En Python 3.6 , le dict régulier est devenu à la fois ordonné et compact. (Cette fonctionnalité est valable pour CPython et PyPy mais peut ne pas être présente dans d'autres implémentations). Cela nous donne un nouveau moyen de déduplication le plus rapide tout en conservant l'ordre:
Dans Python 3.7 , le dict régulier est garanti à la fois ordonné dans toutes les implémentations. Ainsi, la solution la plus courte et la plus rapide est:
Réponse à @max: Une fois que vous passez à 3.6 ou 3.7 et utilisez le dict normal au lieu de OrderedDict , vous ne pouvez pas vraiment battre les performances d'une autre manière. Le dictionnaire est dense et se convertit facilement en une liste avec presque aucun frais généraux. La liste cible est prédimensionnée à len (d), ce qui enregistre tous les redimensionnements qui se produisent dans une compréhension de liste. De plus, comme la liste des clés internes est dense, la copie des pointeurs est à peu près rapide comme copie de liste.
la source
OrderedDict
en liste à la fin. Si j'ai besoin de le convertir en liste, pour les petites entrées, l'approche de compréhension de liste est encore plus rapide jusqu'à 1,5 fois. Cela dit, cette solution est beaucoup plus propre.set()
aiderait des utilisateurs plus naïfs à développer des codes reproductibles.unique →
['1', '2', '3', '6', '4', '5']
la source
n^2
None
références dans le processus!)for
boucle à la placeNe pas donner un coup de pied à un cheval mort (cette question est très ancienne et a déjà beaucoup de bonnes réponses), mais voici une solution utilisant des pandas qui est assez rapide dans de nombreuses circonstances et qui est morte simple à utiliser.
la source
La liste n'a même pas besoin d'être triée , la condition suffisante est que des valeurs égales soient regroupées.
Edit: J'ai supposé que "préserver l'ordre" implique que la liste est réellement ordonnée. Si ce n'est pas le cas, la solution de MizardX est la bonne.
Modification de la communauté: c'est cependant la façon la plus élégante de "compresser les éléments consécutifs en double en un seul élément".
la source
Je pense que si vous voulez maintenir l'ordre,
vous pouvez essayer ceci:
OU de la même manière, vous pouvez le faire:
Vous pouvez également le faire:
Il peut également s'écrire comme ceci:
la source
Dans Python 3.7 et supérieur, les dictionnaires sont garantis pour se souvenir de leur ordre d'insertion de clé. La réponse à cette question résume la situation actuelle.
La
OrderedDict
solution devient ainsi obsolète et sans aucune déclaration d'importation, nous pouvons simplement émettre:la source
Pour une autre réponse très tardive à une autre très vieille question:
Les
itertools
recettes ont une fonction qui le fait, en utilisant laseen
technique définie, mais:key
fonction .seen.add
au lieu de la rechercher N fois. (f7
également cela, mais certaines versions ne le font pas.)ifilterfalse
, vous n'avez donc qu'à parcourir les éléments uniques en Python, au lieu de tous. (Vous parcourez toujours chacun d'eux à l'intérieurifilterfalse
, bien sûr, mais c'est en C, et beaucoup plus rapidement.)Est-ce réellement plus rapide que
f7
? Cela dépend de vos données, vous devrez donc les tester et voir. Si vous voulez une liste à la fin,f7
utilisez un listcomp, et il n'y a aucun moyen de le faire ici. (Vous pouvez directementappend
au lieu deyield
ing, ou vous pouvez alimenter le générateur dans lalist
fonction, mais ni l'un ni l'autre ne peut être aussi rapide que le LIST_APPEND dans un listcomp.) important comme ayant une fonction facilement compréhensible, réutilisable et déjà écrite qui ne nécessite pas de DSU lorsque vous souhaitez décorer.Comme pour toutes les recettes, il est également disponible en
more-iterools
.Si vous voulez juste le non-
key
cas, vous pouvez le simplifier comme:la source
more-itertools
c'est clairement la meilleure réponse. Unefrom more_itertools import unique_everseen
list(unique_everseen(items))
approche beaucoup plus rapide que la mienne et bien meilleure que la réponse acceptée, je pense que le téléchargement de la bibliothèque en vaut la peine. Je vais à la communauté wiki ma réponse et ajouter ceci.Juste pour ajouter un autre (très performant) la mise en œuvre de la fonctionnalité d' un tel d'un module externe 1 :
iteration_utilities.unique_everseen
:Timings
J'ai fait quelques timings (Python 3.6) et ceux-ci montrent que c'est plus rapide que toutes les autres alternatives que j'ai testées, y compris
OrderedDict.fromkeys
,f7
etmore_itertools.unique_everseen
:Et juste pour m'assurer que j'ai également fait un test avec plus de doublons juste pour vérifier si cela fait une différence:
Et un contenant une seule valeur:
Dans tous ces cas, la
iteration_utilities.unique_everseen
fonction est la plus rapide (sur mon ordinateur).Cette
iteration_utilities.unique_everseen
fonction peut également gérer des valeurs non partageables dans l'entrée (mais avec uneO(n*n)
performance au lieu de laO(n)
performance lorsque les valeurs sont hachables).1 Avertissement: je suis l'auteur de ce package.
la source
seen_add = seen.add
- est-ce nécessaire pour les benchmarks?dict.fromkeys()
méthode à votre tableau s'il vous plaît?ordereddict.fromkeys
?Pour les types non hachables (par exemple liste de listes), basé sur MizardX:
la source
Emprunter l'idée récursive utilisée pour définir la
nub
fonction de Haskell pour les listes, ce serait une approche récursive:par exemple:
Je l'ai essayé pour augmenter la taille des données et j'ai vu une complexité temporelle sub-linéaire (non définitive, mais suggère que cela devrait être bien pour des données normales).
Je pense également qu'il est intéressant que cela puisse être facilement généralisé à l'unicité par d'autres opérations. Comme ça:
Par exemple, vous pouvez passer une fonction qui utilise la notion d'arrondi au même entier comme s'il s'agissait "d'égalité" à des fins d'unicité, comme ceci:
alors unique (some_list, test_round) fournirait les éléments uniques de la liste où l'unicité ne signifiait plus l'égalité traditionnelle (ce qui est implicite en utilisant toute sorte d'approche basée sur un ensemble ou une clé dictée pour ce problème) mais plutôt destinée à prendre seul le premier élément qui arrondit à K pour chaque entier K possible que les éléments pourraient arrondir, par exemple:
la source
filter
bénéficiera à peine de l'appel précédent. Mais si le nombre d'éléments uniques est petit par rapport à la taille du tableau, cela devrait fonctionner assez bien.Variante 5 fois plus rapide mais plus sophistiquée
Explication:
la source
Vous pouvez référencer une compréhension de liste telle qu'elle est construite par le symbole '_ [1]'.
Par exemple, la fonction suivante unique-ifie une liste d'éléments sans changer leur ordre en référençant sa compréhension de liste.
Démo:
Production:
la source
La réponse de MizardX donne une bonne collection d'approches multiples.
Voici ce que j'ai trouvé en réfléchissant à haute voix:
la source
O(n)
opération et que vous l'exécutez sur chaque élément, la complexité résultante de votre solution seraitO(n^2)
. C'est tout simplement inacceptable pour un problème aussi banal.voici un moyen simple de le faire:
qui donne la sortie:
la source
Vous pourriez faire une sorte de hack de compréhension de liste laid.
la source
i,e in enumerate(l)
àl[i] for i in range(len(l))
.Approche relativement efficace avec
_sorted_
unnumpy
tableau:Les sorties:
la source
Expression de générateur qui utilise la recherche O (1) d'un ensemble pour déterminer s'il faut ou non inclure un élément dans la nouvelle liste.
la source
extend
avec une expression de générateur qui dépend de la chose étendue (donc +1), mais quiset(n)
est recalculée à chaque étape (qui est linéaire) et cela supplante l'approche globale du quadratique. En fait, c'est presque certainement pire que de simplement utiliserele in n
. Faire un ensemble pour un test d'appartenance unique ne vaut pas le coût de la création de l'ensemble. Pourtant - c'est une approche intéressante.Une solution récursive simple:
la source
Éliminer les valeurs en double dans une séquence, mais conserver l'ordre des éléments restants. Utilisation de la fonction génératrice à usage général.
la source
les utilisateurs de pandas devraient vérifier
pandas.unique
.La fonction renvoie un tableau NumPy. Si nécessaire, vous pouvez le convertir en liste avec la
tolist
méthode.la source
Si vous avez besoin d'une doublure, cela pourrait peut-être aider:
... devrait fonctionner mais corrigez-moi si je me trompe
la source
Si vous utilisez régulièrement
pandas
et que l'esthétique est préférée aux performances, envisagez la fonction intégréepandas.Series.drop_duplicates
:Horaire:
la source
cela préservera l'ordre et fonctionnera en temps O (n). en gros, l'idée est de créer un trou partout où un doublon est trouvé et de le couler vers le bas. utilise un pointeur de lecture et d'écriture. chaque fois qu'un doublon est trouvé, seul le pointeur de lecture avance et le pointeur d'écriture reste sur l'entrée en double pour l'écraser.
la source
Une solution sans utiliser de modules ou d'ensembles importés:
Donne la sortie:
la source
Une méthode sur place
Cette méthode est quadratique, car nous avons une recherche linéaire dans la liste pour chaque élément de la liste (à cela, nous devons ajouter le coût de réorganisation de la liste en raison de la
del
s).Cela dit, il est possible de fonctionner en place si nous partons de la fin de la liste et continuons vers l'origine en supprimant chaque terme qui est présent dans la sous-liste à sa gauche
Cette idée dans le code est tout simplement
Un test simple de l'implémentation
la source
l[:] = <one of the the faster methods>
si vous vouliez une opération sur place, non?a=[1]; b=a; a[:]=[2]
alors lab==[2]
valeur estTrue
et nous pouvons dire que nous le faisons sur place, néanmoins ce que vous proposez est d'utiliser un nouvel espace pour avoir une nouvelle liste, remplacer les anciennes données par les nouvelles et marquer la les anciennes données pour la collecte des ordures parce qu'elles ne sont plus référencées par quoi que ce soit, donc dire que cela fonctionne sur place est un peu étirer le concept par rapport à ce que j'ai montré qu'il est possible ... est-ce inefficace? oui, mais je l'ai dit à l'avance.L'approche de zmk utilise une compréhension de liste qui est très rapide, tout en gardant l'ordre naturellement. Pour appliquer aux chaînes sensibles à la casse, il peut être facilement modifié. Cela préserve également le boîtier d'origine.
Les fonctions étroitement associées sont:
la source
Compréhension d'une liste de lignes:
Ajoutez simplement une condition pour vérifier que la valeur ne se trouve pas sur une position précédente
la source