Suppression des doublons dans les listes


998

À peu près, j'ai besoin d'écrire un programme pour vérifier si une liste contient des doublons et s'il le fait, il les supprime et renvoie une nouvelle liste avec les éléments qui n'ont pas été dupliqués / supprimés. C'est ce que j'ai mais pour être honnête je ne sais pas quoi faire.

def remove_duplicates():
    t = ['a', 'b', 'c', 'd']
    t2 = ['a', 'c', 'd']
    for t in t2:
        t.append(t.remove())
    return t

22
Votre description indique que vous vérifiez "une liste" pour les doublons, mais votre code vérifie deux listes.
Brendan Long


* en utilisant set: list (set (ELEMENTS_LIST)) * en utilisant le dictionnaire: list (dict.fromkeys (ELEMENTS_LIST))
Shayan Amani

Réponses:


1643

L'approche courante pour obtenir une collection unique d'articles est d'utiliser a set. Les ensembles sont des collections non ordonnées d' objets distincts . Pour créer un ensemble à partir de n'importe quel itérable, vous pouvez simplement le passer à la set()fonction intégrée. Si vous avez besoin ultérieurement d'une vraie liste, vous pouvez également transmettre l'ensemble à la list()fonction.

L'exemple suivant doit couvrir tout ce que vous essayez de faire:

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> list(set(t))
[1, 2, 3, 5, 6, 7, 8]
>>> s = [1, 2, 3]
>>> list(set(t) - set(s))
[8, 5, 6, 7]

Comme vous pouvez le voir dans l'exemple de résultat, la commande d'origine n'est pas conservée . Comme mentionné ci-dessus, les ensembles eux-mêmes sont des collections non ordonnées, donc la commande est perdue. Lors de la reconversion d'un ensemble en liste, un ordre arbitraire est créé.

Maintenir l'ordre

Si l'ordre est important pour vous, vous devrez utiliser un mécanisme différent. Une solution très courante pour cela consiste à s'appuyer sur OrderedDictpour conserver l'ordre des clés lors de l'insertion:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

À partir de Python 3.7 , le dictionnaire intégré est également garanti de maintenir l'ordre d'insertion, vous pouvez donc également l'utiliser directement si vous êtes sur Python 3.7 ou version ultérieure (ou CPython 3.6):

>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Notez que cela peut entraîner une surcharge de création d'un dictionnaire, puis de création d'une liste à partir de celui-ci. Si vous n'avez pas réellement besoin de conserver l'ordre, il vaut souvent mieux utiliser un ensemble, surtout parce qu'il vous donne beaucoup plus d'opérations avec lesquelles travailler. Consultez cette question pour plus de détails et d'autres moyens de préserver l'ordre lors de la suppression des doublons.


Enfin, notez que les solutions setaussi bien que OrderedDict/ dictnécessitent que vos articles soient lavables . Cela signifie généralement qu'ils doivent être immuables. Si vous devez gérer des éléments qui ne sont pas hachables (par exemple, lister des objets), vous devrez utiliser une approche lente dans laquelle vous devrez essentiellement comparer chaque élément avec tous les autres éléments dans une boucle imbriquée.


4
Cela ne fonctionne pas pour les éléments de liste non partageables (par exemple une liste de listes)
KNejad

3
@KNejad C'est ce que dit le dernier paragraphe.
poke

Oh oups. Aurait dû lire le tout. Ce que j'ai fini par faire était d'utiliser des tuples au lieu de listes pour que cette approche puisse toujours fonctionner.
KNejad

ajoutez ceci à l'exemple, t = [3, 2, 1, 1, 2, 5, 6, 7, 8], montre clairement la différence!
sailfish009

"... frais généraux de création d'un dictionnaire d'abord ... Si vous n'avez pas vraiment besoin de conserver l'ordre, il vaut mieux utiliser un ensemble." - J'ai profilé cela parce que j'étais curieux de savoir si c'était vraiment vrai. Mes timings montrent qu'en effet l'ensemble est légèrement plus rapide: 1,12 µs par boucle (set) vs 1,53 µs par boucle (dict) sur 1M de boucles avec une différence de temps absolue d'environ 4s sur 1M d'itérations. Donc, si vous faites cela dans une boucle intérieure serrée, vous pouvez vous en soucier, sinon probablement pas.
millerdev

414

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:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

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:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

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:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

10
Je pense que c'est la seule façon de garder les articles en ordre.
Herberth Amaral


5
@MartijnPieters Correction: Je pense que c'est le seul moyen simple de garder les articles en ordre.
Herberth Amaral

12
Pour cela aussi, le contenu de la liste originale doit être lavable
Davide

Comme @Davide l'a mentionné, la liste d'origine doit être hachable. Cela signifie que cela ne fonctionne pas pour une liste de dictionnaires. TypeError: unhashable type: 'dictlist'
CraZ

187

C'est un vol simple: list(set(source_list))fera l'affaire.

A setest quelque chose qui ne peut pas avoir de doublons.

Mise à jour: une approche qui préserve l'ordre est de deux lignes:

from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()

Ici, nous utilisons le fait qui se OrderedDictsouvient de l'ordre d'insertion des clés et ne le change pas lorsqu'une valeur à une clé particulière est mise à jour. Nous insérons en Truetant que valeurs, mais nous pourrions insérer n'importe quoi, les valeurs ne sont tout simplement pas utilisées. ( setfonctionne un peu comme un dictavec des valeurs ignorées aussi.)


5
Cela ne fonctionne que si source_listest lavable.
Adrian Keister

@AdrianKeister: C'est vrai. Il existe des objets qui ont une sémantique d'égalité raisonnable mais qui ne sont pas hachables, par exemple des listes. OTOH si nous ne pouvons pas avoir un raccourci comme un hastable, nous nous retrouvons avec un algorithme quadratique de comparaison de chaque élément avec tous les éléments uniques actuellement connus. Cela peut être totalement OK pour les entrées courtes, en particulier avec beaucoup de doublons.
9000

Exactement. Je pense que votre réponse serait de meilleure qualité si vous preniez en compte ce cas d'utilisation très courant.
Adrian Keister

95
>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> s = []
>>> for i in t:
       if i not in s:
          s.append(i)
>>> s
[1, 2, 3, 5, 6, 7, 8]

33
Notez que cette méthode fonctionne en temps O (n ^ 2) et est donc très lente sur les grandes listes.
dotancohen

@Chris_Rands: Je ne suis pas sûr que cela frozensetfonctionne avec du contenu non hachable. J'obtiens toujours l'erreur non-hachable lors de l'utilisation frozenset.
Adrian Keister

85

Si vous ne vous souciez pas de la commande, faites simplement ceci:

def remove_duplicates(l):
    return list(set(l))

A setest garanti de ne pas avoir de doublons.


3
Ne fonctionne que s'il lest lavable.
Adrian Keister

41

Pour créer une nouvelle liste en conservant l'ordre des premiers éléments des doublons dans L

newlist=[ii for n,ii in enumerate(L) if ii not in L[:n]]

par exemple if L=[1, 2, 2, 3, 4, 2, 4, 3, 5]alorsnewlist sera[1,2,3,4,5]

Ceci vérifie que chaque nouvel élément n'est pas apparu précédemment dans la liste avant de l'ajouter. De plus, il n'a pas besoin d'importations.


3
Cela a une complexité temporelle de O (n ^ 2) . Les réponses avec setet OrderedDictpeuvent avoir une complexité de temps amorti inférieure.
blubberdiblub

J'ai utilisé dans mon code cette solution et j'ai très bien fonctionné, mais je pense que cela prend du temps
Gerasimos Ragavanis

@blubberdiblub pouvez-vous expliquer quel mécanisme plus efficace de code existe dans set et OrderedDict qui pourrait les réduire en temps? (hors frais généraux de chargement)
ilias iliadis

@iliasiliadis Les implémentations habituelles de set et dict utilisent des hachages ou (une forme d'arbre équilibré). Vous devez envisager de créer l' ensemble ou le dict et de le rechercher (plusieurs fois), mais leur complexité amortie est généralement toujours inférieure à O (n ^ 2) . «Amorti» en termes simples signifie en moyenne (ils peuvent avoir les pires cas avec une complexité plus élevée que le cas moyen). Cela n'est pertinent que lorsque vous avez un grand nombre d'articles.
blubberdiblub

25

Un collègue m'a envoyé la réponse acceptée dans le cadre de son code pour une révision du code aujourd'hui. Bien que j'admire certainement l'élégance de la réponse en question, je ne suis pas satisfait de la performance. J'ai essayé cette solution (j'utilise set pour réduire le temps de recherche)

def ordered_set(in_list):
    out_list = []
    added = set()
    for val in in_list:
        if not val in added:
            out_list.append(val)
            added.add(val)
    return out_list

Pour comparer l'efficacité, j'ai utilisé un échantillon aléatoire de 100 entiers - 62 étaient uniques

from random import randint
x = [randint(0,100) for _ in xrange(100)]

In [131]: len(set(x))
Out[131]: 62

Voici les résultats des mesures

In [129]: %timeit list(OrderedDict.fromkeys(x))
10000 loops, best of 3: 86.4 us per loop

In [130]: %timeit ordered_set(x)
100000 loops, best of 3: 15.1 us per loop

Eh bien, que se passe-t-il si l'ensemble est supprimé de la solution?

def ordered_set(inlist):
    out_list = []
    for val in inlist:
        if not val in out_list:
            out_list.append(val)
    return out_list

Le résultat n'est pas aussi mauvais qu'avec le OrderedDict , mais toujours plus de 3 fois la solution d'origine

In [136]: %timeit ordered_set(x)
10000 loops, best of 3: 52.6 us per loop

Agréable en utilisant la recherche rapide définie pour accélérer la comparaison en boucle. Si l'ordre n'a pas d'importance, la liste (set (x)) est encore 6x plus rapide que cela
Joop

@Joop, c'était ma première question pour mon collègue - l'ordre est important; sinon, cela aurait été un problème trivial
volcan

version optimisée de l'ensemble ordonné, pour toute personne intéressée def unique(iterable)::; seen = set(); seen_add = seen.add; return [item for item in iterable if not item in seen and not seen_add(item)]
DrD

25

Il existe également des solutions utilisant Pandas et Numpy. Ils renvoient tous les deux un tableau numpy, vous devez donc utiliser la fonction .tolist()si vous voulez une liste.

t=['a','a','b','b','b','c','c','c']
t2= ['c','c','b','b','b','a','a','a']

Solution Pandas

Utilisation de la fonction Pandas unique():

import pandas as pd
pd.unique(t).tolist()
>>>['a','b','c']
pd.unique(t2).tolist()
>>>['c','b','a']

Solution Numpy

Utilisation de la fonction numpy unique().

import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']

Notez que numpy.unique () trie également les valeurs . La liste t2est donc retournée triée. Si vous souhaitez que l'ordre soit préservé, utilisez comme dans cette réponse :

_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']

La solution n'est pas aussi élégante par rapport aux autres, cependant, par rapport à pandas.unique (), numpy.unique () vous permet également de vérifier si les tableaux imbriqués sont uniques le long d'un axe sélectionné.


Cela convertira la liste en tableau numpy qui est un gâchis et ne fonctionnera pas pour les chaînes.
user227666

1
@ user227666 merci pour votre avis, mais ce n'est pas vrai, cela fonctionne même avec une chaîne et vous pouvez ajouter .tolist si vous voulez obtenir une liste ...
GM

1
Je pense que c'est un peu comme essayer de tuer une abeille avec un marteau. Fonctionne, bien sûr! Mais, importer une bibliothèque à cette fin peut être un peu exagéré, non?
Debosmit Ray

@DebosmitRay, cela peut être utile si vous travaillez dans la science des données où vous travaillez généralement avec numpy et plusieurs fois vous devez travailler avec numpy array.
GM

la meilleure réponse en 2020 @ DebosmitRay j'espère que vous changez d'avis et utilisez numpy / pandas chaque fois que vous le pouvez
Egos

21

Une autre façon de faire:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]

1
Notez que dans les versions Python modernes (2.7+ je pense, mais je ne m'en souviens pas avec certitude), keys()retourne un objet de vue de dictionnaire, pas une liste.
Dustin Wyatt

16

Simple et facile:

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanlist = []
[cleanlist.append(x) for x in myList if x not in cleanlist]

Production:

>>> cleanlist 
[1, 2, 3, 5, 6, 7, 8]

5
complexité quadratique néanmoins - inest une opération O (n) et vous cleanlistaurez au plus des nnombres => pire des cas ~ O (n ^ 2)
jermenkoo

6
la compréhension des listes ne doit pas être utilisée pour les effets secondaires.
Jean-François Fabre

13

Dans cette réponse, il y aura deux sections: deux solutions uniques et un graphique de vitesse pour des solutions spécifiques.

Suppression des éléments en double

La plupart de ces réponses ne suppriment que les éléments en double qui sont lavables , mais cette question n'implique pas qu'il n'a pas seulement besoin d' éléments lavables , ce qui signifie que je proposerai des solutions qui ne nécessitent pas de nettoyage. articles .

collections.Counter est un outil puissant dans la bibliothèque standard qui pourrait être parfait pour cela. Il n'y a qu'une seule autre solution qui contient même Counter. Cependant, cette solution est également limitée à hashable clés .

Pour autoriser les clés non partageables dans Counter, j'ai créé une classe Container, qui essaiera d'obtenir la fonction de hachage par défaut de l'objet, mais si elle échoue, elle essaiera sa fonction d'identité. Il définit également un eq et une méthode de hachage . Cela devrait être suffisant pour autoriser les éléments non lavables dans notre solution. Les objets non lavables seront traités comme s'ils étaient lavables. Cependant, cette fonction de hachage utilise l'identité pour les objets non lavables, ce qui signifie que deux objets égaux qui sont tous les deux non lavables ne fonctionneront pas. Je vous suggère de remplacer cela et de le changer pour utiliser le hachage d'un type mutable équivalent (comme utiliser hash(tuple(my_list))ifmy_list est une liste).

J'ai également fait deux solutions. Une autre solution qui conserve l'ordre des articles, en utilisant une sous-classe à la fois OrderedDict et Counter qui est nommée 'OrderedCounter'. Maintenant, voici les fonctions:

from collections import OrderedDict, Counter

class Container:
    def __init__(self, obj):
        self.obj = obj
    def __eq__(self, obj):
        return self.obj == obj
    def __hash__(self):
        try:
            return hash(self.obj)
        except:
            return id(self.obj)

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

def remd(sequence):
    cnt = Counter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

def oremd(sequence):
    cnt = OrderedCounter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

remd est un tri non ordonné, oremd est un tri ordonné. Vous pouvez clairement dire lequel est le plus rapide, mais je l'expliquerai quand même. Le tri non ordonné est légèrement plus rapide. Il conserve moins de données, car il n'a pas besoin de commande.

Maintenant, je voulais aussi montrer les comparaisons de vitesse de chaque réponse. Donc, je vais le faire maintenant.

Quelle fonction est la plus rapide?

Pour supprimer les doublons, j'ai rassemblé 10 fonctions à partir de quelques réponses. J'ai calculé la vitesse de chaque fonction et l'ai mise dans un graphique en utilisant matplotlib.pyplot .

J'ai divisé cela en trois séries de graphiques. Un hachable est tout objet qui peut être haché, un non lavable est tout objet qui ne peut pas être haché. Une séquence ordonnée est une séquence qui préserve l'ordre, une séquence non ordonnée ne préserve pas l'ordre. Maintenant, voici quelques termes supplémentaires:

Unordered Hashable était pour toute méthode qui supprimait les doublons, qui ne devait pas nécessairement conserver la commande. Cela ne devait pas fonctionner pour les incontrôlables, mais cela pouvait.

Commandé Hashable était pour n'importe quelle méthode qui gardait l'ordre des articles dans la liste, mais cela ne devait pas fonctionner pour les éléments non modifiables, mais c'était possible.

Ordered Unhashable était une méthode qui maintenait l'ordre des éléments dans la liste et fonctionnait pour les éléments non partageables.

Sur l'axe des y est le nombre de secondes qu'il a fallu.

Sur l'axe des x se trouve le nombre auquel la fonction a été appliquée.

Nous avons généré des séquences de hashables non ordonnées et ordonnées hashables avec la compréhension suivante: [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

Pour les éléments non partagés commandés: [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

Notez qu'il y a une «étape» dans la plage, car sans elle, cela aurait pris 10 fois plus de temps. Aussi parce qu'à mon avis, je pensais que ça aurait pu paraître un peu plus facile à lire.

Notez également que les touches de la légende sont ce que j'ai essayé de deviner comme les parties les plus vitales de la fonction. Quant à quelle fonction fait le pire ou le meilleur? Le graphique parle de lui-même.

Avec cela réglé, voici les graphiques.

Hashables non ordonnés

entrez la description de l'image ici (Zoomé) entrez la description de l'image ici

Hashables commandés

entrez la description de l'image ici (Zoomé) entrez la description de l'image ici

Unhashables commandés

entrez la description de l'image ici (Zoomé) entrez la description de l'image ici


11

J'avais un dict dans ma liste, donc je ne pouvais pas utiliser l'approche ci-dessus. J'ai eu l'erreur:

TypeError: unhashable type:

Donc, si vous vous souciez de la commande et / ou que certains articles ne sont pas lavables . Ensuite, vous pourriez trouver cela utile:

def make_unique(original_list):
    unique_list = []
    [unique_list.append(obj) for obj in original_list if obj not in unique_list]
    return unique_list

Certains peuvent considérer que la compréhension de la liste avec un effet secondaire n'est pas une bonne solution. Voici une alternative:

def make_unique(original_list):
    unique_list = []
    map(lambda x: unique_list.append(x) if (x not in unique_list) else False, original_list)
    return unique_list

6
mapavec un effet secondaire est encore plus trompeur qu'un listcomp avec un effet secondaire. En outre, lambda x: unique_list.append(x)c'est juste un moyen plus maladroit et plus lent de passer unique_list.append.
abarnert

Un moyen très utile pour ajouter des éléments sur une seule ligne, merci!
ZLNK

2
@ZLNK s'il vous plaît, ne l'utilisez jamais. En plus d'être laid sur le plan conceptuel, il est également extrêmement inefficace, car vous créez en fait une liste potentiellement grande et la jetez juste pour effectuer une itération de base.
Eli Korvigo

10

Toutes les approches préservant l'ordre que j'ai vues jusqu'ici utilisent soit une comparaison naïve (avec O (n ^ 2) complexité temporelle au mieux) ou des combinaisons lourdes OrderedDicts/ set+ listqui sont limitées aux entrées lavables. Voici une solution O (nlogn) indépendante du hachage:

La mise à jour a ajouté l' keyargument, la documentation et la compatibilité Python 3.

# from functools import reduce <-- add this import on Python 3

def uniq(iterable, key=lambda x: x):
    """
    Remove duplicates from an iterable. Preserves order. 
    :type iterable: Iterable[Ord => A]
    :param iterable: an iterable of objects of any orderable type
    :type key: Callable[A] -> (Ord => B)
    :param key: optional argument; by default an item (A) is discarded 
    if another item (B), such that A == B, has already been encountered and taken. 
    If you provide a key, this condition changes to key(A) == key(B); the callable 
    must return orderable objects.
    """
    # Enumerate the list to restore order lately; reduce the sorted list; restore order
    def append_unique(acc, item):
        return acc if key(acc[-1][1]) == key(item[1]) else acc.append(item) or acc 
    srt_enum = sorted(enumerate(iterable), key=lambda item: key(item[1]))
    return [item[1] for item in sorted(reduce(append_unique, srt_enum, [srt_enum[0]]))] 

Pourtant, cette solution nécessite des éléments commandables. Je vais l'utiliser unifier ma liste de listes: c'est pénible de les lister tuple()et de les hacher. | | | | - D'une manière générale, le processus de hachage prend un temps proportionnel à la taille de l'ensemble des données, tandis que cette solution prend un temps O (nlog (n)), dépendant uniquement de la longueur de la liste.
loxaxs

Je pense que l'approche basée sur les ensembles est tout aussi bon marché (O (n log n)), ou moins cher, que le tri + la détection des uniques. (Cette approche se paralléliserait cependant beaucoup mieux.) Elle ne préserve pas non plus exactement l'ordre initial, mais elle donne un ordre prévisible.
9000

@ 9000 C'est vrai. Je n'ai jamais mentionné la complexité temporelle d'une approche basée sur une table de hachage, qui est évidemment O (n). Vous trouverez ici de nombreuses réponses intégrant des tables de hachage. Ils ne sont cependant pas universels, car ils nécessitent que les objets soient lavables. De plus, ils consomment beaucoup plus de mémoire.
Eli Korvigo

Prend du temps pour lire et comprendre cette réponse. Est-il utile d'énumérer lorsque vous n'utilisez pas les indices? Le reduce() travaille déjà sur une collection triée srt_enum, pourquoi avez-vous postulé à sortednouveau?
Brayoni

@Brayoni le premier tri est là pour regrouper des valeurs égales, le second tri est là pour restaurer l'ordre initial. L'énumération est nécessaire pour garder une trace de l'ordre relatif d'origine.
Eli Korvigo,

9

Si vous souhaitez conserver l'ordre et ne pas utiliser de modules externes, voici un moyen simple de le faire:

>>> t = [1, 9, 2, 3, 4, 5, 3, 6, 7, 5, 8, 9]
>>> list(dict.fromkeys(t))
[1, 9, 2, 3, 4, 5, 6, 7, 8]

Remarque: Cette méthode préserve l'ordre d'apparition, donc, comme indiqué ci-dessus, neuf viendront après un car c'était la première fois qu'elle apparaissait. Cependant, c'est le même résultat que vous obtiendriez en faisant

from collections import OrderedDict
ulist=list(OrderedDict.fromkeys(l))

mais il est beaucoup plus court et tourne plus vite.

Cela fonctionne car chaque fois que la fromkeysfonction essaie de créer une nouvelle clé, si la valeur existe déjà, elle la remplace simplement. Cependant, cela n'affectera pas du tout le dictionnaire, car fromkeyscrée un dictionnaire où toutes les clés ont la valeur None, donc il élimine efficacement tous les doublons de cette façon.


Essayez aussi ici
vineeshvs

8

Vous pouvez également faire ceci:

>>> t = [1, 2, 3, 3, 2, 4, 5, 6]
>>> s = [x for i, x in enumerate(t) if i == t.index(x)]
>>> s
[1, 2, 3, 4, 5, 6]

La raison pour laquelle cela fonctionne est que cette indexméthode ne renvoie que le premier index d'un élément. Les éléments en double ont des indices plus élevés. Reportez-vous ici :

list.index (x [, start [, end]])
Retourne un index de base zéro dans la liste du premier élément dont la valeur est x. Déclenche une ValueError s'il n'y a pas un tel élément.


C'est horriblement inefficace. list.indexest une opération en temps linéaire, ce qui rend votre solution quadratique.
Eli Korvigo

Tu as raison. Mais je pense aussi qu'il est assez évident que la solution est destinée à être un revêtement qui préserve l'ordre. Tout le reste est déjà là.
Atonal

7

Essayez d'utiliser des ensembles:

import sets
t = sets.Set(['a', 'b', 'c', 'd'])
t1 = sets.Set(['a', 'b', 'c'])

print t | t1
print t - t1

7

Réduisez la variante en conservant la commande:

Supposons que nous ayons une liste:

l = [5, 6, 6, 1, 1, 2, 2, 3, 4]

Réduire la variante (inefficace):

>>> reduce(lambda r, v: v in r and r or r + [v], l, [])
[5, 6, 1, 2, 3, 4]

5 fois plus rapide mais plus sophistiqué

>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]

Explication:

default = (list(), set())
# user list to keep order
# use set to make lookup faster

def reducer(result, item):
    if item not in result[1]:
        result[0].append(item)
        result[1].add(item)
    return result

reduce(reducer, l, default)[0]

7

La meilleure approche pour supprimer les doublons d'une liste est d'utiliser la fonction set () , disponible en python, convertissant à nouveau cet ensemble en liste

In [2]: some_list = ['a','a','v','v','v','c','c','d']
In [3]: list(set(some_list))
Out[3]: ['a', 'c', 'd', 'v']

@MeetZaveri heureux.!
Anurag Misra

L'instanciation de nouvelles listes et ensembles n'est pas gratuite. Que se passe-t-il si nous faisons cela plusieurs fois de suite rapidement (c'est-à-dire dans une boucle très serrée) et que les listes sont très petites?
Z4-tier

6

Vous pouvez utiliser la fonction suivante:

def rem_dupes(dup_list): 
    yooneeks = [] 
    for elem in dup_list: 
        if elem not in yooneeks: 
            yooneeks.append(elem) 
    return yooneeks

Exemple :

my_list = ['this','is','a','list','with','dupicates','in', 'the', 'list']

Usage:

rem_dupes(my_list)

['ceci', 'est', 'un', 'liste', 'avec', 'duplique', 'dans', 'le']


5

Il existe de nombreuses autres réponses suggérant différentes façons de le faire, mais ce sont toutes des opérations par lots, et certaines d'entre elles jettent la commande d'origine. Cela peut convenir selon ce dont vous avez besoin, mais si vous souhaitez parcourir les valeurs dans l'ordre de la première instance de chaque valeur et que vous souhaitez supprimer les doublons à la volée par rapport à tous à la fois, vous pouvez utiliser ce générateur:

def uniqify(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

Cela renvoie un générateur / itérateur, vous pouvez donc l'utiliser n'importe où que vous pouvez utiliser un itérateur.

for unique_item in uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]):
    print(unique_item, end=' ')

print()

Production:

1 2 3 4 5 6 7 8

Si vous en voulez un list, vous pouvez le faire:

unique_list = list(uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]))

print(unique_list)

Production:

[1, 2, 3, 4, 5, 6, 7, 8]

seen = set(iterable); for item in seen: yield itemest presque certainement plus rapide. (Je n'ai pas essayé ce cas spécifique, mais ce serait ma supposition.)
dylnmc

2
@dylnmc, c'est une opération par lots, et elle perd également la commande. Ma réponse était spécifiquement destinée à être à la volée et par ordre de première occurrence. :)
Cyphase

5

Sans utiliser l'ensemble

data=[1, 2, 3, 1, 2, 5, 6, 7, 8]
uni_data=[]
for dat in data:
    if dat not in uni_data:
        uni_data.append(dat)

print(uni_data) 

5

Vous pouvez utiliser setpour supprimer les doublons:

mylist = list(set(mylist))

Mais notez que les résultats ne seront pas ordonnés. Si c'est un problème:

mylist.sort()

1
Vous pouvez simplement faire: mylist = sorted (list (set (mylist)))
Erik Campobadal

5

Une meilleure approche pourrait être,

import pandas as pd

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanList = pd.Series(myList).drop_duplicates().tolist()
print(cleanList)

#> [1, 2, 3, 5, 6, 7, 8]

et l'ordre reste préservé.


Bien que cela puisse bien fonctionner, l'utilisation d'une bibliothèque lourde comme des pandas à cet effet semble être une exagération.
Glutexo

4

Celui-ci se soucie de la commande sans trop de tracas (OrderdDict & autres). Probablement pas le moyen le plus Pythonique, ni le plus court, mais fait l'affaire:

def remove_duplicates(list):
    ''' Removes duplicate items from a list '''
    singles_list = []
    for element in list:
        if element not in singles_list:
            singles_list.append(element)
    return singles_list

1. Vous ne devez jamais masquer les noms intégrés (au moins, aussi importants que list); 2. Votre méthode est extrêmement mauvaise: elle est quadratique en nombre d'éléments list.
Eli Korvigo

1. Exact, mais c'était un exemple; 2. C'est exact, et c'est exactement la raison pour laquelle je l'ai proposé. Toutes les solutions affichées ici ont des avantages et des inconvénients. Certains sacrifient la simplicité ou l'ordre, le mien sacrifie l'évolutivité.
cgf

c'est un algorithme "Shlemiel le peintre" ...
Z4-tier

4

le code ci-dessous est simple pour supprimer les doublons dans la liste

def remove_duplicates(x):
    a = []
    for i in x:
        if i not in a:
            a.append(i)
    return a

print remove_duplicates([1,2,2,3,3,4])

il renvoie [1,2,3,4]


2
Si vous ne vous souciez pas de la commande, cela prend beaucoup plus de temps. list(set(..))(plus d'un million de passes) battra cette solution d'environ 10 secondes entières - alors que cette approche prend environ 12 secondes, list(set(..))ne prend qu'environ 2 secondes!
dylnmc

@dylnmc c'est aussi un doublon d'une réponse
Eli Korvigo

4

Voici la solution pythonique la plus rapide comparée à d'autres répertoriées dans les réponses.

L'utilisation des détails d'implémentation de l'évaluation des courts-circuits permet d'utiliser la compréhension de liste, ce qui est assez rapide. visited.add(item)renvoie toujours Nonecomme résultat, qui est évalué comme False, donc le côté droit deor serait toujours le résultat d'une telle expression.

Faites le temps vous-même

def deduplicate(sequence):
    visited = set()
    adder = visited.add  # get rid of qualification overhead
    out = [adder(item) or item for item in sequence if item not in visited]
    return out

4

Utilisation de l' ensemble :

a = [0,1,2,3,4,3,3,4]
a = list(set(a))
print a

En utilisant unique :

import numpy as np
a = [0,1,2,3,4,3,3,4]
a = np.unique(a).tolist()
print a

4

Malheureusement. La plupart des réponses ici ne préservent pas l'ordre ou sont trop longues. Voici une réponse simple et préservant l'ordre.

s = [1,2,3,4,5,2,5,6,7,1,3,9,3,5]
x=[]

[x.append(i) for i in s if i not in x]
print(x)

Cela vous donnera x avec les doublons supprimés mais préservant l'ordre.


3

Manière très simple en Python 3:

>>> n = [1, 2, 3, 4, 1, 1]
>>> n
[1, 2, 3, 4, 1, 1]
>>> m = sorted(list(set(n)))
>>> m
[1, 2, 3, 4]

2
sorted(list(...))est redondant ( sortedconvertit déjà implicitement son argument en un nouveau list, le trie, puis renvoie le nouveau list, donc utiliser les deux signifie créer un temporaire inutile list). Utilisez uniquement listsi le résultat n'a pas besoin d'être trié, utilisez uniquement sortedsi le résultat doit être trié.
ShadowRanger

3

La magie de Python Type intégré

En python, il est très facile de traiter les cas compliqués comme celui-ci et uniquement par le type intégré de python.

Laissez-moi vous montrer comment faire!

Méthode 1: Cas général

La façon ( code 1 ligne ) de supprimer l'élément dupliqué dans la liste et de conserver l'ordre de tri

line = [1, 2, 3, 1, 2, 5, 6, 7, 8]
new_line = sorted(set(line), key=line.index) # remove duplicated element
print(new_line)

Vous obtiendrez le résultat

[1, 2, 3, 5, 6, 7, 8]

Méthode 2: cas spécial

TypeError: unhashable type: 'list'

Le cas particulier pour traiter les données non partageables ( codes à 3 lignes )

line=[['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']]

tuple_line = [tuple(pt) for pt in line] # convert list of list into list of tuple
tuple_new_line = sorted(set(tuple_line),key=tuple_line.index) # remove duplicated element
new_line = [list(t) for t in tuple_new_line] # convert list of tuple into list of list

print (new_line)

Vous obtiendrez le résultat:

[
  ['16.4966155686595', '-27.59776154691', '52.3786295521147'], 
  ['17.6508629295574', '-27.143305738671', '47.534955022564'], 
  ['18.8051102904552', '-26.688849930432', '42.6912804930134'], 
  ['19.5504702331098', '-26.205884452727', '37.7709192714727'], 
  ['20.2929416861422', '-25.722717575124', '32.8500163147157']
]

Parce que le tuple est lavable et vous pouvez facilement convertir des données entre la liste et le tuple

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.