¿Cómo encuentro los duplicados en una lista y creo otra lista con ellos?

437

¿Cómo puedo encontrar los duplicados en una lista de Python y crear otra lista de los duplicados? La lista solo contiene enteros.

MFB
fuente
1
¿Desea los duplicados una vez, o cada vez que se ve de nuevo?
moooeeeep
Creo que esto ha sido respondido con mucha más eficiencia aquí. stackoverflow.com/a/642919/1748045 intersección es un método integrado de conjunto y debe hacer exactamente lo que se requiere
Tom Smith

Respuestas:

545

Para eliminar duplicados use set(a). Para imprimir duplicados, algo como:

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]

Tenga en cuenta que Counterno es particularmente eficiente ( tiempos ) y probablemente exagere aquí. setfuncionará mejor. Este código calcula una lista de elementos únicos en el orden de origen:

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

o, más concisamente:

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

No recomiendo el último estilo, porque no es obvio lo que not seen.add(x)está haciendo (el add()método set siempre regresa None, de ahí la necesidad denot ).

Para calcular la lista de elementos duplicados sin bibliotecas:

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 los elementos de la lista no son modificables, no puede usar conjuntos / dictos y debe recurrir a una solución de tiempo cuadrático (compare cada uno con cada uno). Por ejemplo:

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
fuente
2
@eric: Supongo que sí O(n), porque solo itera la lista una vez y establece las búsquedas O(1).
georg
3
@Hugo, para ver la lista de duplicados, solo necesitamos crear una nueva lista llamada dup y agregar una instrucción else. Por ejemplo:dup = [] else: dup.append(x)
Chris Nielsen
44
@oxeimon: probablemente tienes esto, pero imprimes se llama entre paréntesis en python 3print()
Moberg
44
convirtiendo su respuesta para set () para obtener solo duplicados. seen = set()entoncesdupe = set(x for x in a if x in seen or seen.add(x))
Ta946
2
Para Python 3.x: print ([elemento por elemento, contar en colecciones. Contador (a) .items () si cuenta> 1])
kibitzforu
327
>>> 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
fuente
2
¿Hay alguna razón por la que use una comprensión de lista en lugar de una comprensión de generador?
64
De hecho, es una solución simple, pero la complejidad es cuadrada porque cada cuenta () analiza la lista de nuevo, así que no la use para listas grandes.
danuker
44
@JohnJ, el ordenamiento de burbujas también es simple y funciona. ¡Eso no significa que debamos usarlo!
John La Rooy
@JohnLaRooy En realidad significa que no debemos usarlo, porque casi siempre hay una forma más eficiente (y más simple) de ordenar.
lostsoul29
1
@watsonic: Su "cambio simple" no puede reducir la complejidad del tiempo de cuadrático a cuadrado en el caso general. Sustitución lcon set(l)sólo reduce el tiempo en el peor caso y por lo tanto no nada para abordar las cuestiones de eficiencia de mayor escala con esta respuesta. Probablemente no fue tan simple después de todo. En resumen, no hagas esto.
Cecil Curry
82

No necesita el recuento, solo si el artículo se vio o no antes. Adaptado esa respuesta a este problema:

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]

En caso de que la velocidad sea importante, aquí hay algunos tiempos:

# 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

Aquí están los resultados: (¡bien hecho @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

Curiosamente, además de los tiempos en sí, también la clasificación cambia ligeramente cuando se usa pypy. Lo más interesante es que el enfoque basado en el contador se beneficia enormemente de las optimizaciones de pypy, mientras que el método de almacenamiento en caché del método que he sugerido parece no tener casi ningún efecto.

$ 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

Aparentemente, este efecto está relacionado con la "duplicación" de los datos de entrada. He configurado l = [random.randrange(1000000) for i in xrange(10000)]y obtuve estos resultados:

$ 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
fuente
66
Simplemente curioso: ¿cuál es el propósito de seen_add = seen.add aquí?
Rob
3
@Rob De esta manera, simplemente llama a la función que buscaste una vez. De lo contrario, necesitaría buscar (una consulta de diccionario) la función miembro addcada vez que fuera necesaria una inserción.
moooeeeep
comprobado con mis propios datos y el% de tiempo de Ipython, su método se ve más rápido en la prueba PERO: "La ejecución más lenta tomó 4.34 veces más que la más rápida. Esto podría significar que se está almacenando en caché un resultado intermedio"
Joop
1
@moooeeeep, agregué otra versión a tu script para que lo pruebes :) También prueba pypysi lo tienes a mano y buscas velocidad.
John La Rooy
@JohnLaRooy ¡Buena mejora en el rendimiento! Curiosamente, cuando usé pypy para evaluar los resultados, el enfoque basado en Counter mejora significativamente.
moooeeeep
42

Puedes usar iteration_utilities.duplicates :

>>> from iteration_utilities import duplicates

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

o si solo quieres uno de cada duplicado, esto se puede combinar con iteration_utilities.unique_everseen :

>>> from iteration_utilities import unique_everseen

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

También puede manejar elementos no compartibles (sin embargo, a costa del rendimiento):

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

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

Eso es algo que solo algunos de los otros enfoques aquí pueden manejar.

Puntos de referencia

Hice un punto de referencia rápido que contiene la mayoría (pero no todos) de los enfoques mencionados aquí.

El primer punto de referencia incluyó solo un pequeño rango de longitudes de lista porque algunos enfoques tienen O(n**2) comportamiento.

En los gráficos, el eje y representa el tiempo, por lo que un valor más bajo significa mejor. También se traza log-log para que se pueda visualizar mejor la amplia gama de valores:

ingrese la descripción de la imagen aquí

Eliminando los O(n**2)enfoques, hice otro punto de referencia de hasta medio millón de elementos en una lista:

ingrese la descripción de la imagen aquí

Como puede ver, el iteration_utilities.duplicatesenfoque es más rápido que cualquiera de los otros enfoques e incluso encadenamientounique_everseen(duplicates(...)) fue más rápido o igual de rápido que los otros enfoques.

Una cosa interesante adicional a tener en cuenta aquí es que los enfoques de los pandas son muy lentos para listas pequeñas, pero pueden competir fácilmente por listas más largas.

Sin embargo, como muestran estos puntos de referencia, la mayoría de los enfoques tienen un rendimiento aproximadamente igual, por lo que no importa mucho cuál se use (excepto los 3 que tuvieron O(n**2)tiempo de ejecución).

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()

Benchmark 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()

Descargo de responsabilidad

1 Se trata de una biblioteca de terceros que he escrito: iteration_utilities.

MSeifert
fuente
1
Voy a meter la cabeza aquí y sugerir que escribir una biblioteca a medida para hacer el trabajo en C en lugar de Python probablemente no sea el espíritu de la respuesta que se estaba buscando, ¡pero ese es un enfoque legítimo! Me gusta el ancho de la respuesta y la visualización gráfica de los resultados. Muy agradable ver que convergen, me hace preguntarme si alguna vez se cruzarán a medida que las entradas aumenten aún más. Pregunta: ¿cuál es el resultado con listas mayormente ordenadas en lugar de listas completamente aleatorias?
F1Rumors
30

Me encontré con esta pregunta mientras buscaba algo relacionado, y me pregunto por qué nadie ofreció una solución basada en un generador. Resolver este problema sería:

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

Estaba preocupado por la escalabilidad, por lo que probé varios enfoques, incluidos elementos ingenuos que funcionan bien en listas pequeñas, pero escalan horriblemente a medida que las listas se hacen más grandes (nota: habría sido mejor usar timeit, pero esto es ilustrativo).

Incluí @moooeeeep para la comparación (es impresionantemente rápido: más rápido si la lista de entrada es completamente aleatoria) y un enfoque de itertools que es aún más rápido nuevamente para listas en su mayoría ordenadas ... Ahora incluye el enfoque de pandas de @firelynx - lento, pero no horriblemente, y simple. Nota: el enfoque de clasificación / tee / zip es consistentemente más rápido en mi máquina para listas grandes en su mayoría ordenadas, moooeeeep es más rápido para listas barajadas, pero su millaje puede variar.

Ventajas

  • muy rápido, simple para probar 'cualquier' duplicado usando el mismo código

Supuestos

  • Los duplicados deben reportarse solo una vez
  • No es necesario preservar el pedido duplicado
  • Duplicado puede estar en cualquier lugar de la lista

La solución más rápida, 1 millón de entradas:

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

Enfoques probados

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

Los resultados para la prueba de 'todos los duplicados' fueron consistentes, encontrando "primero" duplicado y luego "todos" duplicados en esta matriz:

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  

Cuando las listas se barajan primero, el precio del tipo se hace evidente: la eficiencia cae notablemente y el enfoque @moooeeeep domina, con enfoques set & dict que son similares pero de menor rendimiento:

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  
F1Rumours
fuente
@moooeeeep: esté interesado en ver sus puntos de vista sobre el enfoque ifilter / izip / tee.
F1Rumors
1
esta respuesta es increíblemente buena. No entiendo que no haya tenido más puntos para las explicaciones y pruebas que son muy útiles para aquellos que lo necesiten.
dlewin
1
La clasificación de Python es O (n) cuando solo un elemento está fuera de servicio. Deberías random.shuffle(c)dar cuenta de eso. Además, tampoco puedo replicar sus resultados cuando ejecuto el script inalterado (un orden totalmente diferente), por lo que tal vez también dependa de la CPU.
John La Rooy
Gracias @ John-La-Rooy, la observación astuta de la CPU / máquina local es impactante, así que debería enmendar el ítem YYMV . El uso de la clasificación O (n) fue deliberado: el elemento duplicado se inserta en diferentes ubicaciones específicamente para ver el impacto del enfoque si hay un duplicado único en una ubicación buena (inicio de la lista) o mala (final de la lista) con estos enfoques. Consideré una lista aleatoria, por ejemplo, random.shuffle, ¡pero decidí que solo sería sensato si hacía muchas más carreras! Tendré que regresar y comparar un equivalente de carrera múltiple / barajar y ver cuál es el impacto.
F1Rumors
Se modificó para incluir el enfoque de pandas @firelynx y para ejecutarse en una lista completamente aleatoria, así como en una lista ordenada. Esto se debe a la timsort nativo utilizado por Python es malvado rápido en su mayoría datos ordenados (mejor de los casos) y en las listas barajadas son su peor de los casos - que sacude a los resultados.
F1Rumors
13

Usando 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])
Herrero de palabras
fuente
10

collections.Counter es nuevo 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'
>>> 

En una versión anterior, puede usar un dict convencional en su lugar:

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]
Eduardo
fuente
9

Aquí hay una solución ordenada y concisa:

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

li = list(set(li))
Nikhil Prabhu
fuente
Sin embargo, la lista original se pierde. Esto se puede solucionar copiando el contenido a otra lista - temp = li [:]
Nikhil Prabhu
3
Es un ejercicio bastante desagradable en listas grandes: ¡eliminar elementos de las listas es bastante costoso!
F1Rumors
7

Sin convertir a la lista y probablemente la forma más simple sería algo como a continuación. Esto puede ser útil durante una entrevista cuando piden no usar sets

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

======= más para obtener 2 listas separadas de valores únicos y valores duplicados

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
fuente
1
Sin embargo, esto no da como resultado una lista de duplicados de a (o la lista original), esto da como resultado una lista de todos los elementos únicos de a (o la lista original). ¿Qué haría alguien después de terminar de formar la lista "dup"?
gameCoder95
6

Haría esto con los pandas, porque los uso mucho

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()

Da

[3,6]

Probablemente no sea muy eficiente, pero seguro es menos código que muchas de las otras respuestas, así que pensé que contribuiría

firelynx
fuente
3
Tenga en cuenta también que los pandas contienen una función de duplicados incorporada pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken
6

El tercer ejemplo de la respuesta aceptada da una respuesta errónea y no intenta dar duplicados. Aquí está la versión correcta:

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
fuente
6

¿Qué tal si simplemente recorre cada elemento de la lista verificando el número de ocurrencias y luego agregándolos a un conjunto que luego imprimirá los duplicados? Espero que esto ayude a alguien por ahí.

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
fuente
5

Podemos usar itertools.groupbypara encontrar todos los artículos que tienen dups:

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  

El resultado será:

4
6
alfasin
fuente
1
O de manera más concisa:dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
viernes
5

Creo que la forma más efectiva de encontrar duplicados en una lista es:

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]))

Utiliza Countertodos los elementos y todos los elementos únicos. Restar el primero con el segundo dejará fuera solo los duplicados.

Anand Chitipothu
fuente
4

Un poco tarde, pero tal vez útil para algunos. Para una lista grande, encontré que esto funcionó para mí.

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]

Muestra solo y todos los duplicados y conserva el orden.

usuario3109122
fuente
3

La forma muy simple y rápida de encontrar personas engañadas con una iteración en Python es:

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

testListDict = {}

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

print testListDict

La salida será la siguiente:

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

Esto y más en mi blog http://www.howtoprogramwithpython.com

Igor Vishnevskiy
fuente
3

Estoy entrando mucho más tarde en esta discusión. Sin embargo, me gustaría tratar este problema con un revestimiento. Porque ese es el encanto de Python. si solo queremos obtener los duplicados en una lista separada (o cualquier colección), sugeriría hacer lo siguiente: digamos que tenemos una lista duplicada que podemos llamar como 'objetivo'

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

Ahora, si queremos obtener los duplicados, podemos usar el único revestimiento de la siguiente manera:

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

Este código colocará los registros duplicados como clave y contará como valor en el diccionario 'duplicados'. El diccionario 'duplicado' tendrá el siguiente aspecto:

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

Si solo desea todos los registros con duplicados solo en una lista, nuevamente es un código mucho más corto:

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

La salida será:

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

Esto funciona perfectamente en las versiones de python 2.7.x +

akhil pathirippilly
fuente
3

Python 3.8 one-liner si no le importa escribir su propio algoritmo o usar bibliotecas:

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 el artículo y cuenta:

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

groupbytoma una función de agrupación para que pueda definir sus agrupaciones de diferentes maneras y devolver Tuplecampos adicionales según sea necesario.

groupby es vago, por lo que no debería ser demasiado lento.

yǝsʞǝla
fuente
2

Algunas otras pruebas. Por supuesto que hacer ...

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

... es muy costoso. Es aproximadamente 500 veces más rápido (la matriz más larga da mejores resultados) para usar el siguiente método final:

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()

Solo 2 bucles, sin l.count()operaciones muy costosas .

Aquí hay un código para comparar los métodos, por ejemplo. El código está abajo, aquí está la salida:

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

El código de prueba:

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
fuente
2

Método 1:

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

Explicación: [val para idx, val en enumerate (input_list) si val en input_list [idx + 1:]] es una comprensión de la lista, que devuelve un elemento, si el mismo elemento está presente desde su posición actual, en la lista, el índice .

Ejemplo: input_list = [42,31,42,31,3,31,31,5,6,6,6,6,6,7,42]

comenzando con el primer elemento en la lista, 42, con el índice 0, verifica si el elemento 42 está presente en input_list [1:] (es decir, desde el índice 1 hasta el final de la lista) Porque 42 está presente en input_list [1:] , devolverá 42.

Luego va al siguiente elemento 31, con el índice 1, y verifica si el elemento 31 está presente en input_list [2:] (es decir, desde el índice 2 hasta el final de la lista), porque 31 está presente en input_list [2:], devolverá 31.

de manera similar, revisa todos los elementos de la lista y solo devolverá los elementos repetidos / duplicados en una lista.

Luego, debido a que tenemos duplicados, en una lista, necesitamos elegir uno de cada duplicado, es decir, eliminar duplicados entre duplicados, y para hacerlo, llamamos a un conjunto de nombres incorporado de Python (), y elimina los duplicados,

Luego nos queda un conjunto, pero no una lista, y por lo tanto, para convertir de un conjunto a una lista, usamos typecasting, list (), y eso convierte el conjunto de elementos en una lista.

Método 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

Explicación: Aquí creamos dos listas vacías, para empezar. Luego, siga recorriendo todos los elementos de la lista para ver si existe en temp_list (inicialmente vacío). Si no está allí en temp_list, entonces lo agregamos a temp_list, usando el método append .

Si ya existe en temp_list, significa que el elemento actual de la lista es un duplicado y, por lo tanto, debemos agregarlo a dupe_list usando el método append .

S471
fuente
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]

Básicamente, elimina los duplicados convirtiéndolos en set ( clean_list), luego itera raw_list, mientras elimina cada uno itemen la lista limpia para que ocurra raw_list. Si itemno se encuentra, ValueErrorse captura la excepción generada y itemse agrega a la duplicated_itemslista.

Si se necesita el índice de elementos duplicados, solo enumeratela lista y juegue con el índice. ( for index, item in enumerate(raw_list):) que es más rápido y optimizado para listas grandes (como miles + de elementos)

Todo es variedad
fuente
2

uso del list.count()método en la lista para descubrir los elementos duplicados de una lista dada

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
fuente
manera simple de encontrar los elementos duplicados en la lista usando la función de conteo
Ravikiran D
2

one-liner, por diversión, y donde se requiere una sola declaración.

(lambda iterable: reduce(lambda (uniq, dup), item: (uniq, dup | {item}) if item in uniq else (uniq | {item}, dup), iterable, (set(), set())))(some_iterable)
Wizr
fuente
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
fuente
1

Solución de una línea:

set([i for i in list if sum([1 for a in list if a == i]) > 1])
Ytpillai
fuente
1

Aquí hay muchas respuestas, pero creo que este es un enfoque relativamente fácil de leer y de entender:

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)

Notas:

  • Si desea conservar el recuento de duplicaciones, elimine el elenco para 'establecer' en la parte inferior para obtener la lista completa
  • Si prefiere usar generadores, reemplace duplicates.append (x) con rendimiento x y la declaración de retorno en la parte inferior (puede emitir para configurar más adelante)
tvt173
fuente
1

Aquí hay un generador rápido que usa un dict para almacenar cada elemento como una clave con un valor booleano para verificar si el elemento duplicado ya ha sido entregado.

Para listas con todos los elementos que son tipos hashable:

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]

Para listas que pueden contener listas:

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
fuente
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]))
RANJAN CENIZO
fuente
Devuelve un conjunto y no una lista según lo solicitado. Un conjunto contiene solo elementos únicos, por lo tanto, la instrucción if no es realmente necesaria. También debe explicar cuál es la ventaja de su solución en comparación con la otra.
clemens
1

Al usar 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
fuente
0

esta es la forma en que tuve que hacerlo porque me desafié a mí mismo a no usar otros métodos:

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

entonces su muestra funciona como:

>>>a = [1,2,3,3,3,4,5,6,6,7]
>>>dupList(a)
[1, 2, 3, 4, 5, 6, 7]
Matt S
fuente
3
Esto no es lo que quería el OP. Quería una lista de los duplicados, no una lista con los duplicados eliminados. Para hacer una lista con los duplicados eliminados, sugeriría duplist = list(set(a)).
zondo