Comment trouver les doublons dans une liste et créer une autre liste avec eux?

439

Comment puis-je trouver les doublons dans une liste Python et créer une autre liste des doublons? La liste ne contient que des entiers.

MFB
la source
1
voulez-vous les doublons une fois, ou chaque fois qu'il est revu?
moooeeeep
Je pense que cela a été répondu avec beaucoup plus d'efficacité ici. stackoverflow.com/a/642919/1748045 intersection est une méthode intégrée de définition et devrait faire exactement ce qui est nécessaire
Tom Smith

Réponses:

546

Pour supprimer les doublons, utilisez set(a). Pour imprimer des doublons, quelque chose comme:

a = [1,2,3,2,1,5,6,5,5,5]

import collections
print([item for item, count in collections.Counter(a).items() if count > 1])

## [1, 2, 5]

Notez que ce Countern'est pas particulièrement efficace ( timings ) et probablement exagéré ici. setfonctionnera mieux. Ce code calcule une liste d'éléments uniques dans l'ordre source:

seen = set()
uniq = []
for x in a:
    if x not in seen:
        uniq.append(x)
        seen.add(x)

ou, plus concis:

seen = set()
uniq = [x for x in a if x not in seen and not seen.add(x)]    

Je ne recommande pas ce dernier style, car ce n'est pas évident not seen.add(x)(la add()méthode set revient toujours None, d'où la nécessité not).

Pour calculer la liste des éléments dupliqués sans bibliothèques:

seen = {}
dupes = []

for x in a:
    if x not in seen:
        seen[x] = 1
    else:
        if seen[x] == 1:
            dupes.append(x)
        seen[x] += 1

Si les éléments de la liste ne sont pas hachables, vous ne pouvez pas utiliser d'ensembles / dict et devez recourir à une solution temporelle quadratique (comparer chacun avec chacun). Par exemple:

a = [[1], [2], [3], [1], [5], [3]]

no_dupes = [x for n, x in enumerate(a) if x not in a[:n]]
print no_dupes # [[1], [2], [3], [5]]

dupes = [x for n, x in enumerate(a) if x in a[:n]]
print dupes # [[1], [3]]
georg
la source
2
@eric: Je suppose que oui O(n), car il n'itère la liste qu'une seule fois et les recherches définies le sont O(1).
georg
3
@Hugo, pour voir la liste des doublons, il suffit de créer une nouvelle liste appelée dup et d'ajouter une instruction else. Par exemple:dup = [] else: dup.append(x)
Chris Nielsen
4
@oxeimon: vous avez probablement obtenu ceci, mais votre impression est appelée avec des parenthèses en python 3print()
Moberg
4
convertir votre réponse pour set () pour obtenir des doublons uniquement. seen = set()puisdupe = set(x for x in a if x in seen or seen.add(x))
Ta946
2
Pour Python 3.x: print ([article pour article, compte dans les collections.Counter (a) .items () si count> 1])
kibitzforu
328
>>> l = [1,2,3,4,4,5,5,6,1]
>>> set([x for x in l if l.count(x) > 1])
set([1, 4, 5])
Ritesh Kumar
la source
2
Y a-t-il une raison pour laquelle vous utilisez une compréhension de liste au lieu d'une compréhension de générateur?
64
En effet, une solution simple, mais la complexité est au carré car chaque count () analyse la liste à nouveau, donc ne l'utilisez pas pour les grandes listes.
danuker
4
@JohnJ, le tri à bulles est également simple et fonctionne. Cela ne signifie pas que nous devrions l'utiliser!
John La Rooy
@JohnLaRooy Cela signifie en fait que nous ne devrions pas l'utiliser, car il existe presque toujours un moyen plus efficace (et plus simple) de trier.
lostsoul29
1
@watsonic: Votre "simple interrupteur" ne parvient pas à réduire la complexité temporelle du quadratique au carré dans le cas général. Le remplacement lpar set(l)ne réduit que la complexité temporelle la plus défavorable et ne fait donc rien pour résoudre les problèmes d'efficacité à plus grande échelle avec cette réponse. Ce n'était probablement pas si simple après tout. Bref, ne faites pas ça.
Cecil Curry
82

Vous n'avez pas besoin du nombre, juste si l'article a été vu auparavant ou non. Adapté cette réponse à ce problème:

def list_duplicates(seq):
  seen = set()
  seen_add = seen.add
  # adds all elements it doesn't know yet to seen and all other to seen_twice
  seen_twice = set( x for x in seq if x in seen or seen_add(x) )
  # turn the set into a list (as requested)
  return list( seen_twice )

a = [1,2,3,2,1,5,6,5,5,5]
list_duplicates(a) # yields [1, 2, 5]

Juste au cas où la vitesse compte, voici quelques timings:

# file: test.py
import collections

def thg435(l):
    return [x for x, y in collections.Counter(l).items() if y > 1]

def moooeeeep(l):
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    seen_twice = set( x for x in l if x in seen or seen_add(x) )
    # turn the set into a list (as requested)
    return list( seen_twice )

def RiteshKumar(l):
    return list(set([x for x in l if l.count(x) > 1]))

def JohnLaRooy(L):
    seen = set()
    seen2 = set()
    seen_add = seen.add
    seen2_add = seen2.add
    for item in L:
        if item in seen:
            seen2_add(item)
        else:
            seen_add(item)
    return list(seen2)

l = [1,2,3,2,1,5,6,5,5,5]*100

Voici les résultats: (bravo @JohnLaRooy!)

$ python -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
10000 loops, best of 3: 74.6 usec per loop
$ python -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
10000 loops, best of 3: 91.3 usec per loop
$ python -mtimeit -s 'import test' 'test.thg435(test.l)'
1000 loops, best of 3: 266 usec per loop
$ python -mtimeit -s 'import test' 'test.RiteshKumar(test.l)'
100 loops, best of 3: 8.35 msec per loop

Fait intéressant, outre le timing lui-même, le classement change également légèrement lorsque Pypy est utilisé. Plus intéressant encore, l'approche Counter-based bénéficie énormément des optimisations de Pypy, tandis que l'approche de mise en cache de méthode que j'ai suggérée semble n'avoir presque aucun effet.

$ pypy -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
100000 loops, best of 3: 17.8 usec per loop
$ pypy -mtimeit -s 'import test' 'test.thg435(test.l)'
10000 loops, best of 3: 23 usec per loop
$ pypy -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
10000 loops, best of 3: 39.3 usec per loop

Apparemment, cet effet est lié à la "duplication" des données d'entrée. J'ai défini l = [random.randrange(1000000) for i in xrange(10000)]et obtenu ces résultats:

$ pypy -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
1000 loops, best of 3: 495 usec per loop
$ pypy -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
1000 loops, best of 3: 499 usec per loop
$ pypy -mtimeit -s 'import test' 'test.thg435(test.l)'
1000 loops, best of 3: 1.68 msec per loop
moooeeeep
la source
6
Juste curieux - quel est le but de seen_add = seen.add ici?
Rob
3
@Rob De cette façon, vous appelez simplement la fonction que vous avez recherchée auparavant. Sinon, vous devrez rechercher (une requête de dictionnaire) la fonction membre addchaque fois qu'une insertion sera nécessaire.
moooeeeep
vérifié avec mes propres données et% timeit d'Ipython votre méthode semble plus rapide sur le test MAIS: "L'exécution la plus lente a pris 4,34 fois plus longtemps que la plus rapide. Cela pourrait signifier qu'un résultat intermédiaire est mis en cache"
Joop
1
@moooeeeep, j'ai ajouté une autre version à votre script pour que vous puissiez l'essayer :) Essayez également pypysi vous l'avez à portée de main et que vous allez pour la vitesse.
John La Rooy du
@JohnLaRooy Belle amélioration des performances! Fait intéressant, lorsque j'ai utilisé Pypy pour évaluer les résultats, l'approche basée sur les compteurs s'améliore considérablement.
moooeeeep
42

Vous pouvez utiliser iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

>>> list(duplicates([1,1,2,1,2,3,4,2]))
[1, 1, 2, 2]

ou si vous ne voulez qu'un seul de chaque doublon, cela peut être combiné avec iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen

>>> list(unique_everseen(duplicates([1,1,2,1,2,3,4,2])))
[1, 2]

Il peut également gérer des éléments non lavables (mais au détriment des performances):

>>> list(duplicates([[1], [2], [1], [3], [1]]))
[[1], [1]]

>>> list(unique_everseen(duplicates([[1], [2], [1], [3], [1]])))
[[1]]

C'est quelque chose que seules quelques-unes des autres approches ici peuvent gérer.

Repères

J'ai fait un benchmark rapide contenant la plupart (mais pas toutes) des approches mentionnées ici.

Le premier repère ne comprenait qu'une petite gamme de longueurs de liste car certaines approches O(n**2) comportement.

Dans les graphiques, l'axe des y représente le temps, donc une valeur inférieure signifie mieux. Il est également tracé le journal de bord afin que la large gamme de valeurs puisse être mieux visualisée:

entrez la description de l'image ici

En supprimant les O(n**2)approches, j'ai fait un autre benchmark jusqu'à un demi-million d'éléments dans une liste:

entrez la description de l'image ici

Comme vous pouvez le voir, l' iteration_utilities.duplicatesapproche est plus rapide que toutes les autres approches et même le chaînage unique_everseen(duplicates(...))était plus rapide ou aussi rapide que les autres approches.

Une autre chose intéressante à noter ici est que les approches des pandas sont très lentes pour les petites listes mais peuvent facilement rivaliser pour les listes plus longues.

Cependant, comme ces tests de référence montrent que la plupart des approches fonctionnent à peu près de la même manière, peu importe laquelle est utilisée (à l'exception des 3 qui avaient un O(n**2)temps d'exécution).

from iteration_utilities import duplicates, unique_everseen
from collections import Counter
import pandas as pd
import itertools

def georg_counter(it):
    return [item for item, count in Counter(it).items() if count > 1]

def georg_set(it):
    seen = set()
    uniq = []
    for x in it:
        if x not in seen:
            uniq.append(x)
            seen.add(x)

def georg_set2(it):
    seen = set()
    return [x for x in it if x not in seen and not seen.add(x)]   

def georg_set3(it):
    seen = {}
    dupes = []

    for x in it:
        if x not in seen:
            seen[x] = 1
        else:
            if seen[x] == 1:
                dupes.append(x)
            seen[x] += 1

def RiteshKumar_count(l):
    return set([x for x in l if l.count(x) > 1])

def moooeeeep(seq):
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    seen_twice = set( x for x in seq if x in seen or seen_add(x) )
    # turn the set into a list (as requested)
    return list( seen_twice )

def F1Rumors_implementation(c):
    a, b = itertools.tee(sorted(c))
    next(b, None)
    r = None
    for k, g in zip(a, b):
        if k != g: continue
        if k != r:
            yield k
            r = k

def F1Rumors(c):
    return list(F1Rumors_implementation(c))

def Edward(a):
    d = {}
    for elem in a:
        if elem in d:
            d[elem] += 1
        else:
            d[elem] = 1
    return [x for x, y in d.items() if y > 1]

def wordsmith(a):
    return pd.Series(a)[pd.Series(a).duplicated()].values

def NikhilPrabhu(li):
    li = li.copy()
    for x in set(li):
        li.remove(x)

    return list(set(li))

def firelynx(a):
    vc = pd.Series(a).value_counts()
    return vc[vc > 1].index.tolist()

def HenryDev(myList):
    newList = set()

    for i in myList:
        if myList.count(i) >= 2:
            newList.add(i)

    return list(newList)

def yota(number_lst):
    seen_set = set()
    duplicate_set = set(x for x in number_lst if x in seen_set or seen_set.add(x))
    return seen_set - duplicate_set

def IgorVishnevskiy(l):
    s=set(l)
    d=[]
    for x in l:
        if x in s:
            s.remove(x)
        else:
            d.append(x)
    return d

def it_duplicates(l):
    return list(duplicates(l))

def it_unique_duplicates(l):
    return list(unique_everseen(duplicates(l)))

Benchmark 1

from simple_benchmark import benchmark
import random

funcs = [
    georg_counter, georg_set, georg_set2, georg_set3, RiteshKumar_count, moooeeeep, 
    F1Rumors, Edward, wordsmith, NikhilPrabhu, firelynx,
    HenryDev, yota, IgorVishnevskiy, it_duplicates, it_unique_duplicates
]

args = {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(2, 12)}

b = benchmark(funcs, args, 'list size')

b.plot()

Repère 2

funcs = [
    georg_counter, georg_set, georg_set2, georg_set3, moooeeeep, 
    F1Rumors, Edward, wordsmith, firelynx,
    yota, IgorVishnevskiy, it_duplicates, it_unique_duplicates
]

args = {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(2, 20)}

b = benchmark(funcs, args, 'list size')
b.plot()

Avertissement

1 Ceci est d'une bibliothèque tierce , je l' ai écrit: iteration_utilities.

MSeifert
la source
1
Je vais coller mon cou ici et suggérer d'écrire une bibliothèque sur mesure pour faire le travail en C plutôt qu'en Python n'est probablement pas l'esprit de la réponse qui était recherchée - mais c'est une approche légitime! J'aime la largeur de la réponse et l'affichage graphique des résultats - très agréable de voir qu'ils convergent, je me demande s'ils se croisent à mesure que les entrées augmentent encore! Question: quel est le résultat avec des listes principalement triées par opposition à des listes complètement aléatoires?
F1Rumors
30

Je suis tombé sur cette question en regardant quelque chose de connexe - et je me demande pourquoi personne n'a offert une solution basée sur un générateur? Résoudre ce problème serait:

>>> print list(getDupes_9([1,2,3,2,1,5,6,5,5,5]))
[1, 2, 5]

J'étais préoccupé par l'évolutivité, j'ai donc testé plusieurs approches, y compris des éléments naïfs qui fonctionnent bien sur de petites listes, mais évoluent horriblement à mesure que les listes s'agrandissent (note - il aurait été préférable d'utiliser timeit, mais cela est illustratif).

J'ai inclus @moooeeeep pour comparaison (c'est incroyablement rapide: plus rapide si la liste d'entrée est complètement aléatoire) et une approche itertools qui est encore plus rapide pour les listes principalement triées ... Inclut maintenant l'approche pandas de @firelynx - lente, mais pas horriblement et simple. Remarque - l'approche tri / tee / zip est toujours la plus rapide sur ma machine pour les grandes listes principalement commandées, moooeeeep est la plus rapide pour les listes mélangées, mais votre kilométrage peut varier.

Les avantages

  • très rapide simple pour tester les doublons en utilisant le même code

Hypothèses

  • Les doublons doivent être déclarés une seule fois
  • Il n'est pas nécessaire de conserver l'ordre en double
  • Le doublon peut se trouver n'importe où dans la liste

Solution la plus rapide, 1m d'entrées:

def getDupes(c):
        '''sort/tee/izip'''
        a, b = itertools.tee(sorted(c))
        next(b, None)
        r = None
        for k, g in itertools.izip(a, b):
            if k != g: continue
            if k != r:
                yield k
                r = k

Approches testées

import itertools
import time
import random

def getDupes_1(c):
    '''naive'''
    for i in xrange(0, len(c)):
        if c[i] in c[:i]:
            yield c[i]

def getDupes_2(c):
    '''set len change'''
    s = set()
    for i in c:
        l = len(s)
        s.add(i)
        if len(s) == l:
            yield i

def getDupes_3(c):
    '''in dict'''
    d = {}
    for i in c:
        if i in d:
            if d[i]:
                yield i
                d[i] = False
        else:
            d[i] = True

def getDupes_4(c):
    '''in set'''
    s,r = set(),set()
    for i in c:
        if i not in s:
            s.add(i)
        elif i not in r:
            r.add(i)
            yield i

def getDupes_5(c):
    '''sort/adjacent'''
    c = sorted(c)
    r = None
    for i in xrange(1, len(c)):
        if c[i] == c[i - 1]:
            if c[i] != r:
                yield c[i]
                r = c[i]

def getDupes_6(c):
    '''sort/groupby'''
    def multiple(x):
        try:
            x.next()
            x.next()
            return True
        except:
            return False
    for k, g in itertools.ifilter(lambda x: multiple(x[1]), itertools.groupby(sorted(c))):
        yield k

def getDupes_7(c):
    '''sort/zip'''
    c = sorted(c)
    r = None
    for k, g in zip(c[:-1],c[1:]):
        if k == g:
            if k != r:
                yield k
                r = k

def getDupes_8(c):
    '''sort/izip'''
    c = sorted(c)
    r = None
    for k, g in itertools.izip(c[:-1],c[1:]):
        if k == g:
            if k != r:
                yield k
                r = k

def getDupes_9(c):
    '''sort/tee/izip'''
    a, b = itertools.tee(sorted(c))
    next(b, None)
    r = None
    for k, g in itertools.izip(a, b):
        if k != g: continue
        if k != r:
            yield k
            r = k

def getDupes_a(l):
    '''moooeeeep'''
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    for x in l:
        if x in seen or seen_add(x):
            yield x

def getDupes_b(x):
    '''iter*/sorted'''
    x = sorted(x)
    def _matches():
        for k,g in itertools.izip(x[:-1],x[1:]):
            if k == g:
                yield k
    for k, n in itertools.groupby(_matches()):
        yield k

def getDupes_c(a):
    '''pandas'''
    import pandas as pd
    vc = pd.Series(a).value_counts()
    i = vc[vc > 1].index
    for _ in i:
        yield _

def hasDupes(fn,c):
    try:
        if fn(c).next(): return True    # Found a dupe
    except StopIteration:
        pass
    return False

def getDupes(fn,c):
    return list(fn(c))

STABLE = True
if STABLE:
    print 'Finding FIRST then ALL duplicates, single dupe of "nth" placed element in 1m element array'
else:
    print 'Finding FIRST then ALL duplicates, single dupe of "n" included in randomised 1m element array'
for location in (50,250000,500000,750000,999999):
    for test in (getDupes_2, getDupes_3, getDupes_4, getDupes_5, getDupes_6,
                 getDupes_8, getDupes_9, getDupes_a, getDupes_b, getDupes_c):
        print 'Test %-15s:%10d - '%(test.__doc__ or test.__name__,location),
        deltas = []
        for FIRST in (True,False):
            for i in xrange(0, 5):
                c = range(0,1000000)
                if STABLE:
                    c[0] = location
                else:
                    c.append(location)
                    random.shuffle(c)
                start = time.time()
                if FIRST:
                    print '.' if location == test(c).next() else '!',
                else:
                    print '.' if [location] == list(test(c)) else '!',
                deltas.append(time.time()-start)
            print ' -- %0.3f  '%(sum(deltas)/len(deltas)),
        print
    print

Les résultats pour le test «toutes les dupes» étaient cohérents, trouvant «premier» doublon puis «tous» les doublons dans ce tableau:

Finding FIRST then ALL duplicates, single dupe of "nth" placed element in 1m element array
Test set len change :    500000 -  . . . . .  -- 0.264   . . . . .  -- 0.402  
Test in dict        :    500000 -  . . . . .  -- 0.163   . . . . .  -- 0.250  
Test in set         :    500000 -  . . . . .  -- 0.163   . . . . .  -- 0.249  
Test sort/adjacent  :    500000 -  . . . . .  -- 0.159   . . . . .  -- 0.229  
Test sort/groupby   :    500000 -  . . . . .  -- 0.860   . . . . .  -- 1.286  
Test sort/izip      :    500000 -  . . . . .  -- 0.165   . . . . .  -- 0.229  
Test sort/tee/izip  :    500000 -  . . . . .  -- 0.145   . . . . .  -- 0.206  *
Test moooeeeep      :    500000 -  . . . . .  -- 0.149   . . . . .  -- 0.232  
Test iter*/sorted   :    500000 -  . . . . .  -- 0.160   . . . . .  -- 0.221  
Test pandas         :    500000 -  . . . . .  -- 0.493   . . . . .  -- 0.499  

Lorsque les listes sont mélangées en premier, le prix du tri devient apparent - l'efficacité chute sensiblement et l'approche @moooeeeep domine, les approches set & dict étant similaires mais moins performantes:

Finding FIRST then ALL duplicates, single dupe of "n" included in randomised 1m element array
Test set len change :    500000 -  . . . . .  -- 0.321   . . . . .  -- 0.473  
Test in dict        :    500000 -  . . . . .  -- 0.285   . . . . .  -- 0.360  
Test in set         :    500000 -  . . . . .  -- 0.309   . . . . .  -- 0.365  
Test sort/adjacent  :    500000 -  . . . . .  -- 0.756   . . . . .  -- 0.823  
Test sort/groupby   :    500000 -  . . . . .  -- 1.459   . . . . .  -- 1.896  
Test sort/izip      :    500000 -  . . . . .  -- 0.786   . . . . .  -- 0.845  
Test sort/tee/izip  :    500000 -  . . . . .  -- 0.743   . . . . .  -- 0.804  
Test moooeeeep      :    500000 -  . . . . .  -- 0.234   . . . . .  -- 0.311  *
Test iter*/sorted   :    500000 -  . . . . .  -- 0.776   . . . . .  -- 0.840  
Test pandas         :    500000 -  . . . . .  -- 0.539   . . . . .  -- 0.540  
F1Rumeurs
la source
@moooeeeep - soyez intéressé de voir votre point de vue sur l'approche ifilter / izip / tee.
F1Rumors
1
cette réponse est incroyablement bonne. Je ne comprends pas qu'elle n'ait pas eu plus de points pour les explications et les tests qui sont très utiles pour ceux qui en auraient besoin.
dlewin
1
le tri de python est O (n) lorsqu'un seul élément est en panne. Vous devez random.shuffle(c)en tenir compte. De plus, je ne peux pas non plus répliquer vos résultats lors de l'exécution du script non modifié (ordre totalement différent), donc cela dépend peut-être aussi du CPU.
John La Rooy
Merci @ John-La-Rooy, une observation astucieuse sur le CPU / la machine locale ayant un impact - je devrais donc modifier l'article YYMV . L'utilisation du tri O (n) était délibérée: l'élément duplicatif est inséré à différents endroits spécifiquement pour voir l'impact de l'approche s'il y a un seul doublon dans un bon (début de liste) ou mauvais (fin de liste) emplacement avec ceux-ci approches. J'ai considéré une liste aléatoire - par exemple random.shuffle - mais j'ai décidé que ce ne serait raisonnable que si je faisais beaucoup plus de runs! Je devrai revenir et comparer un équivalent à plusieurs exécutions / shuffles et voir quel est l'impact.
F1Rumors
Modifié pour inclure l'approche des pandas @firelynx et pour fonctionner sur une liste entièrement mélangée ainsi que sur une liste triée. En effet, le timsort natif utilisé par Python est méchant rapidement sur la plupart des données triées (dans le meilleur des cas) et les listes mélangées sont son pire scénario - cela secoue les résultats.
F1Rumors
13

Utilisation de pandas:

>>> import pandas as pd
>>> a = [1, 2, 1, 3, 3, 3, 0]
>>> pd.Series(a)[pd.Series(a).duplicated()].values
array([1, 3, 3])
forgeron
la source
10

collections.Counter est nouveau en python 2.7:


Python 2.5.4 (r254:67916, May 31 2010, 15:03:39) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
a = [1,2,3,2,1,5,6,5,5,5]
import collections
print [x for x, y in collections.Counter(a).items() if y > 1]
Type "help", "copyright", "credits" or "license" for more information.
  File "", line 1, in 
AttributeError: 'module' object has no attribute 'Counter'
>>> 

Dans une version antérieure, vous pouvez utiliser un dict classique à la place:

a = [1,2,3,2,1,5,6,5,5,5]
d = {}
for elem in a:
    if elem in d:
        d[elem] += 1
    else:
        d[elem] = 1

print [x for x, y in d.items() if y > 1]
Edward
la source
9

Voici une solution nette et concise -

for x in set(li):
    li.remove(x)

li = list(set(li))
Nikhil Prabhu
la source
La liste d'origine est cependant perdue. Cela peut être résolu en copiant le contenu dans une autre liste - temp = li [:]
Nikhil Prabhu
3
C'est un exercice assez désagréable sur de grandes listes - supprimer des éléments des listes coûte assez cher!
F1Rumors
7

Je le ferais avec des pandas, car j'utilise beaucoup de pandas

import pandas as pd
a = [1,2,3,3,3,4,5,6,6,7]
vc = pd.Series(a).value_counts()
vc[vc > 1].index.tolist()

Donne

[3,6]

Ce n'est probablement pas très efficace, mais c'est certainement moins de code que la plupart des autres réponses, alors j'ai pensé contribuer

firelynx
la source
4
Notez également que pandas contient une fonction de doublons intégrée pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken
7

Sans convertir en liste et probablement le moyen le plus simple serait quelque chose comme ci-dessous. Cela peut être utile lors d'un entretien quand ils demandent de ne pas utiliser d'ensembles

a=[1,2,3,3,3]
dup=[]
for each in a:
  if each not in dup:
    dup.append(each)
print(dup)

======= else pour obtenir 2 listes distinctes de valeurs uniques et de valeurs en double

a=[1,2,3,3,3]
uniques=[]
dups=[]

for each in a:
  if each not in uniques:
    uniques.append(each)
  else:
    dups.append(each)
print("Unique values are below:")
print(uniques)
print("Duplicate values are below:")
print(dups)
Chetan_Vasudevan
la source
1
Cela ne se traduit pas par une liste de doublons d'un (ou la liste d'origine) cependant, cela se traduit par une liste de tous les éléments uniques d'un (ou la liste d'origine). Que ferait quelqu'un après avoir fini de former la liste "dup"?
gameCoder95
6

le troisième exemple de la réponse acceptée donne une réponse erronée et n'essaie pas de donner des doublons. Voici la bonne version:

number_lst = [1, 1, 2, 3, 5, ...]

seen_set = set()
duplicate_set = set(x for x in number_lst if x in seen_set or seen_set.add(x))
unique_set = seen_set - duplicate_set
yota
la source
6

Que diriez-vous simplement de parcourir chaque élément de la liste en vérifiant le nombre d'occurrences, puis en les ajoutant à un ensemble qui imprimera ensuite les doublons. J'espère que cela aide quelqu'un là-bas.

myList  = [2 ,4 , 6, 8, 4, 6, 12];
newList = set()

for i in myList:
    if myList.count(i) >= 2:
        newList.add(i)

print(list(newList))
## [4 , 6]
HenryDev
la source
5

Nous pouvons utiliser itertools.groupbyafin de trouver tous les articles qui ont des doublons:

from itertools import groupby

myList  = [2, 4, 6, 8, 4, 6, 12]
# when the list is sorted, groupby groups by consecutive elements which are similar
for x, y in groupby(sorted(myList)):
    #  list(y) returns all the occurences of item x
    if len(list(y)) > 1:
        print x  

La sortie sera:

4
6
alfasin
la source
1
Ou plus concis:dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
frnhr
5

Je suppose que le moyen le plus efficace de trouver des doublons dans une liste est:

from collections import Counter

def duplicates(values):
    dups = Counter(values) - Counter(set(values))
    return list(dups.keys())

print(duplicates([1,2,3,6,5,2]))

Il utilise Countertous les éléments et tous les éléments uniques. Soustraire le premier au second ne laissera de côté que les doublons.

Anand Chitipothu
la source
4

Un peu tard, mais peut-être utile pour certains. Pour une liste assez longue, j'ai trouvé que cela fonctionnait pour moi.

l=[1,2,3,5,4,1,3,1]
s=set(l)
d=[]
for x in l:
    if x in s:
        s.remove(x)
    else:
        d.append(x)
d
[1,3,1]

Affiche juste et tous les doublons et préserve l'ordre.

user3109122
la source
3

Un moyen très simple et rapide de trouver des dupes avec une seule itération en Python est:

testList = ['red', 'blue', 'red', 'green', 'blue', 'blue']

testListDict = {}

for item in testList:
  try:
    testListDict[item] += 1
  except:
    testListDict[item] = 1

print testListDict

La sortie sera la suivante:

>>> print testListDict
{'blue': 3, 'green': 1, 'red': 2}

Ceci et plus dans mon blog http://www.howtoprogramwithpython.com

Igor Vishnevskiy
la source
3

J'entre beaucoup plus tard dans cette discussion. Même si, je voudrais traiter ce problème avec un liners. Parce que c'est le charme de Python. si nous voulons simplement obtenir les doublons dans une liste séparée (ou n'importe quelle collection), je suggère de faire comme ci-dessous. Dites que nous avons une liste dupliquée que nous pouvons appeler comme `` cible ''

    target=[1,2,3,4,4,4,3,5,6,8,4,3]

Maintenant, si nous voulons obtenir les doublons, nous pouvons utiliser la doublure comme ci-dessous:

    duplicates=dict(set((x,target.count(x)) for x in filter(lambda rec : target.count(rec)>1,target)))

Ce code mettra les enregistrements dupliqués comme clé et comptera comme valeur dans le dictionnaire 'duplicates'. Le dictionnaire 'duplicate' ressemblera à ce qui suit:

    {3: 3, 4: 4} #it saying 3 is repeated 3 times and 4 is 4 times

Si vous voulez juste tous les enregistrements avec des doublons seuls dans une liste, son code est encore beaucoup plus court:

    duplicates=filter(lambda rec : target.count(rec)>1,target)

La sortie sera:

    [3, 4, 4, 4, 3, 4, 3]

Cela fonctionne parfaitement dans les versions python 2.7.x +

akhil pathirippilly
la source
3

Python 3.8 one-liner si vous ne vous souciez pas d'écrire votre propre algorithme ou d'utiliser des bibliothèques:

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

res = [(x, count) for x, g in groupby(sorted(l)) if (count := len(list(g))) > 1]

print(res)

Imprime l'élément et compte:

[(1, 2), (2, 2), (5, 4)]

groupby prend une fonction de regroupement afin que vous puissiez définir vos regroupements de différentes manières et renvoyer des informations supplémentaires Tuple champs si nécessaire.

groupby est paresseux donc ça ne devrait pas être trop lent.

yǝsʞǝla
la source
2

Quelques autres tests. Bien sûr à faire ...

set([x for x in l if l.count(x) > 1])

... est trop coûteux. C'est environ 500 fois plus rapide (le tableau le plus long donne de meilleurs résultats) pour utiliser la méthode finale suivante:

def dups_count_dict(l):
    d = {}

    for item in l:
        if item not in d:
            d[item] = 0

        d[item] += 1

    result_d = {key: val for key, val in d.iteritems() if val > 1}

    return result_d.keys()

Seulement 2 boucles, pas très coûteuses l.count() opérations .

Voici un code pour comparer les méthodes par exemple. Le code est ci-dessous, voici la sortie:

dups_count: 13.368s # this is a function which uses l.count()
dups_count_dict: 0.014s # this is a final best function (of the 3 functions)
dups_count_counter: 0.024s # collections.Counter

Le code de test:

import numpy as np
from time import time
from collections import Counter

class TimerCounter(object):
    def __init__(self):
        self._time_sum = 0

    def start(self):
        self.time = time()

    def stop(self):
        self._time_sum += time() - self.time

    def get_time_sum(self):
        return self._time_sum


def dups_count(l):
    return set([x for x in l if l.count(x) > 1])


def dups_count_dict(l):
    d = {}

    for item in l:
        if item not in d:
            d[item] = 0

        d[item] += 1

    result_d = {key: val for key, val in d.iteritems() if val > 1}

    return result_d.keys()


def dups_counter(l):
    counter = Counter(l)    

    result_d = {key: val for key, val in counter.iteritems() if val > 1}

    return result_d.keys()



def gen_array():
    np.random.seed(17)
    return list(np.random.randint(0, 5000, 10000))


def assert_equal_results(*results):
    primary_result = results[0]
    other_results = results[1:]

    for other_result in other_results:
        assert set(primary_result) == set(other_result) and len(primary_result) == len(other_result)


if __name__ == '__main__':
    dups_count_time = TimerCounter()
    dups_count_dict_time = TimerCounter()
    dups_count_counter = TimerCounter()

    l = gen_array()

    for i in range(3):
        dups_count_time.start()
        result1 = dups_count(l)
        dups_count_time.stop()

        dups_count_dict_time.start()
        result2 = dups_count_dict(l)
        dups_count_dict_time.stop()

        dups_count_counter.start()
        result3 = dups_counter(l)
        dups_count_counter.stop()

        assert_equal_results(result1, result2, result3)

    print 'dups_count: %.3f' % dups_count_time.get_time_sum()
    print 'dups_count_dict: %.3f' % dups_count_dict_time.get_time_sum()
    print 'dups_count_counter: %.3f' % dups_count_counter.get_time_sum()
sergzach
la source
2

Méthode 1:

list(set([val for idx, val in enumerate(input_list) if val in input_list[idx+1:]]))

Explication: [val pour idx, val en énumération (liste_entrée) si val dans liste_entrée [idx + 1:]] est une compréhension de liste, qui renvoie un élément, si le même élément est présent à partir de sa position actuelle, dans la liste, l'index .

Exemple: liste_entrées = [42,31,42,31,3,31,31,5,6,6,6,6,6,7,42]

en commençant par le premier élément de la liste, 42, avec l'index 0, il vérifie si l'élément 42 est présent dans input_list [1:] (c'est-à-dire de l'index 1 jusqu'à la fin de la liste) Parce que 42 est présent dans input_list [1:] , il retournera 42.

Ensuite, il passe à l'élément suivant 31, avec l'index 1, et vérifie si l'élément 31 est présent dans la liste_entrée [2:] (c'est-à-dire de l'index 2 jusqu'à la fin de la liste), Parce que 31 est présent dans la liste_entrée [2:], il reviendra 31.

de même, il parcourt tous les éléments de la liste et ne renvoie que les éléments répétés / en double dans une liste.

Ensuite, parce que nous avons des doublons, dans une liste, nous devons choisir un de chaque doublon, c'est-à-dire supprimer le doublon parmi les doublons, et pour ce faire, nous appelons un ensemble nommé python nommé set (), et il supprime les doublons,

Ensuite, nous nous retrouvons avec un ensemble, mais pas une liste, et donc pour convertir d'un ensemble en liste, nous utilisons, le transtypage, list (), et qui convertit l'ensemble des éléments en une liste.

Méthode 2:

def dupes(ilist):
    temp_list = [] # initially, empty temporary list
    dupe_list = [] # initially, empty duplicate list
    for each in ilist:
        if each in temp_list: # Found a Duplicate element
            if not each in dupe_list: # Avoid duplicate elements in dupe_list
                dupe_list.append(each) # Add duplicate element to dupe_list
        else: 
            temp_list.append(each) # Add a new (non-duplicate) to temp_list

    return dupe_list

Explication: Ici, nous créons deux listes vides, pour commencer. Continuez ensuite à parcourir tous les éléments de la liste, pour voir si elle existe dans temp_list (initialement vide). S'il n'est pas là dans la temp_list, alors nous l'ajoutons à la temp_list, en utilisant la méthode append .

S'il existe déjà dans temp_list, cela signifie que l'élément courant de la liste est un doublon, et donc nous devons l'ajouter à dupe_list en utilisant la méthode append .

S471
la source
2
raw_list = [1,2,3,3,4,5,6,6,7,2,3,4,2,3,4,1,3,4,]

clean_list = list(set(raw_list))
duplicated_items = []

for item in raw_list:
    try:
        clean_list.remove(item)
    except ValueError:
        duplicated_items.append(item)


print(duplicated_items)
# [3, 6, 2, 3, 4, 2, 3, 4, 1, 3, 4]

Vous supprimez essentiellement les doublons en les convertissant en set ( clean_list), puis en itérant le raw_list, tout en supprimant chacun itemdans la liste claire pour l'occurrence dans raw_list. S'il itemn'est pas trouvé, l' ValueErrorexception déclenchée est interceptée et itemajoutée à la duplicated_itemsliste.

Si l'index des éléments dupliqués est nécessaire, juste enumeratela liste et jouer avec l'index. ( for index, item in enumerate(raw_list):) qui est plus rapide et optimisé pour les grandes listes (comme des milliers + d'éléments)

Tout est possible
la source
2

utilisation d'une list.count()méthode dans la liste pour trouver les éléments en double d'une liste donnée

arr=[]
dup =[]
for i in range(int(input("Enter range of list: "))):
    arr.append(int(input("Enter Element in a list: ")))
for i in arr:
    if arr.count(i)>1 and i not in dup:
        dup.append(i)
print(dup)
Ravikiran D
la source
moyen simple de trouver les éléments en double dans la liste à l'aide de la fonction de comptage
Ravikiran D
2

une ligne, pour le plaisir, et où une seule déclaration est requise.

(lambda iterable: reduce(lambda (uniq, dup), item: (uniq, dup | {item}) if item in uniq else (uniq | {item}, dup), iterable, (set(), set())))(some_iterable)
Wizr
la source
1
list2 = [1, 2, 3, 4, 1, 2, 3]
lset = set()
[(lset.add(item), list2.append(item))
 for item in list2 if item not in lset]
print list(lset)
Haresh Shyara
la source
1

Solution en une ligne:

set([i for i in list if sum([1 for a in list if a == i]) > 1])
ytpillai
la source
1

Il y a beaucoup de réponses ici, mais je pense que c'est relativement une approche très lisible et facile à comprendre:

def get_duplicates(sorted_list):
    duplicates = []
    last = sorted_list[0]
    for x in sorted_list[1:]:
        if x == last:
            duplicates.append(x)
        last = x
    return set(duplicates)

Remarques:

  • Si vous souhaitez conserver le nombre de doublons, supprimez la distribution pour 'définir' en bas pour obtenir la liste complète
  • Si vous préférez utiliser des générateurs, remplacez duplicates.append (x) par yield x et la déclaration de retour en bas (vous pouvez convertir pour définir plus tard)
tvt173
la source
1

Voici un générateur rapide qui utilise un dict pour stocker chaque élément sous forme de clé avec une valeur booléenne pour vérifier si l'élément en double a déjà été généré.

Pour les listes avec tous les éléments qui sont des types hachables:

def gen_dupes(array):
    unique = {}
    for value in array:
        if value in unique and unique[value]:
            unique[value] = False
            yield value
        else:
            unique[value] = True

array = [1, 2, 2, 3, 4, 1, 5, 2, 6, 6]
print(list(gen_dupes(array)))
# => [2, 1, 6]

Pour les listes pouvant contenir des listes:

def gen_dupes(array):
    unique = {}
    for value in array:
        is_list = False
        if type(value) is list:
            value = tuple(value)
            is_list = True

        if value in unique and unique[value]:
            unique[value] = False
            if is_list:
                value = list(value)

            yield value
        else:
            unique[value] = True

array = [1, 2, 2, [1, 2], 3, 4, [1, 2], 5, 2, 6, 6]
print(list(gen_dupes(array)))
# => [2, [1, 2], 6]
John B
la source
1
def removeduplicates(a):
  seen = set()

  for i in a:
    if i not in seen:
      seen.add(i)
  return seen 

print(removeduplicates([1,1,2,2]))
ASHISH RANJAN
la source
Vous retournez un ensemble et non une liste comme demandé. Un ensemble ne contient que des éléments uniques, donc l'instruction if n'est pas vraiment nécessaire. Vous devez également expliquer quel est l'avantage de votre solution par rapport à l'autre.
clemens
1

Lors de l'utilisation de toolz :

from toolz import frequencies, valfilter

a = [1,2,2,3,4,5,4]
>>> list(valfilter(lambda count: count > 1, frequencies(a)).keys())
[2,4] 
Andreas Profous
la source
0

c'est ainsi que je devais le faire car je me suis mis au défi de ne pas utiliser d'autres méthodes:

def dupList(oldlist):
    if type(oldlist)==type((2,2)):
        oldlist=[x for x in oldlist]
    newList=[]
    newList=newList+oldlist
    oldlist=oldlist
    forbidden=[]
    checkPoint=0
    for i in range(len(oldlist)):
        #print 'start i', i
        if i in forbidden:
            continue
        else:
            for j in range(len(oldlist)):
                #print 'start j', j
                if j in forbidden:
                    continue
                else:
                    #print 'after Else'
                    if i!=j: 
                        #print 'i,j', i,j
                        #print oldlist
                        #print newList
                        if oldlist[j]==oldlist[i]:
                            #print 'oldlist[i],oldlist[j]', oldlist[i],oldlist[j]
                            forbidden.append(j)
                            #print 'forbidden', forbidden
                            del newList[j-checkPoint]
                            #print newList
                            checkPoint=checkPoint+1
    return newList

pour que votre échantillon fonctionne comme:

>>>a = [1,2,3,3,3,4,5,6,6,7]
>>>dupList(a)
[1, 2, 3, 4, 5, 6, 7]
Matt S
la source
3
Ce n'est pas ce que le PO voulait. Il voulait une liste des doublons, pas une liste avec les doublons supprimés. Pour faire une liste avec les doublons supprimés, je suggère duplist = list(set(a)).
zondo