Come posso trovare i duplicati in un elenco e creare un altro elenco con loro?


437

Come posso trovare i duplicati in un elenco Python e creare un altro elenco dei duplicati? L'elenco contiene solo numeri interi.



1
vuoi i duplicati una volta o ogni volta che viene visto di nuovo?
Moooeeeep

Penso che qui sia stata data una risposta con molta più efficienza. stackoverflow.com/a/642919/1748045 intersezione è un metodo di impostazione incorporato e dovrebbe fare esattamente ciò che è richiesto
Tom Smith

Risposte:


545

Per rimuovere i duplicati utilizzare set(a). Per stampare duplicati, qualcosa del tipo:

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]

Si noti che Counternon è particolarmente efficiente ( tempistiche ) e probabilmente eccessivo qui. setsi esibirà meglio. Questo codice calcola un elenco di elementi univoci nell'ordine di origine:

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

o, più concisamente:

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

Non consiglio quest'ultimo stile, perché non è ovvio cosa not seen.add(x)stia facendo (il add()metodo set ritorna sempre None, quindi la necessità di not).

Per calcolare l'elenco di elementi duplicati senza librerie:

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

Se gli elementi dell'elenco non sono cancellabili, non è possibile utilizzare set / dicts e ricorrere a una soluzione temporale quadratica (confrontarli con ciascuno). Per esempio:

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

2
@eric: Immagino che sia O(n), perché scorre una volta sola la lista e lo sono le ricerche impostate O(1).
Georg

3
@Hugo, per vedere l'elenco dei duplicati, dobbiamo solo creare un nuovo elenco chiamato dup e aggiungere un'istruzione else. Ad esempio:dup = [] else: dup.append(x)
Chris Nielsen,

4
@oxeimon: probabilmente hai capito, ma la stampa viene chiamata tra parentesi in pitone 3print()
Moberg

4
convertire la risposta per set () per ottenere solo duplicati. seen = set()quindidupe = set(x for x in a if x in seen or seen.add(x))
Ta946

2
Per Python 3.x: stampa ([oggetto per oggetto, conta nelle raccolte. Contatore (a) .items () se conta> 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])

2
C'è qualche motivo per usare una comprensione dell'elenco anziché una comprensione del generatore?

64
In effetti, una soluzione semplice, ma la complessità è quadrata perché ogni count () analizza di nuovo l'elenco, quindi non utilizzare per elenchi di grandi dimensioni.
Danuker,

4
@JohnJ, anche l'ordinamento delle bolle è semplice e funziona. Ciò non significa che dovremmo usarlo!
John La Rooy,

@JohnLaRooy In realtà significa che non dovremmo usarlo, perché c'è quasi sempre un modo più efficiente (e più semplice) per fare l'ordinamento.
lostsoul29,

1
@watsonic: il tuo "semplice interruttore" non riesce a ridurre la complessità temporale da quadratica a quadrata nel caso generale. La sostituzione lcon set(l)riduce solo la complessità del tempo peggiore e quindi non fa nulla per affrontare i problemi di efficienza su larga scala con questa risposta. Probabilmente non era poi così semplice. In breve, non farlo.
Cecil Curry,

82

Non è necessario il conteggio, solo se l'oggetto è stato visto o meno prima. Adattato quella risposta a questo 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]

Nel caso in cui la velocità sia importante, ecco alcuni tempi:

# 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

Ecco i risultati: (ben fatto @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

È interessante notare che, oltre ai tempi stessi, anche la classifica cambia leggermente quando viene usato pypy. Cosa più interessante, l'approccio Counter-based beneficia enormemente delle ottimizzazioni di pypy, mentre l'approccio di caching del metodo che ho suggerito sembra non avere quasi alcun effetto.

$ 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

Apparentemente questo effetto è legato alla "duplicazione" dei dati di input. Ho impostato l = [random.randrange(1000000) for i in xrange(10000)]e ottenuto questi risultati:

$ 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

6
Solo curioso: qual è lo scopo di seen_add = seen.add qui?
Rob,

3
@Rob In questo modo basta chiamare la funzione che hai cercato una volta. Altrimenti dovresti cercare (una query del dizionario) la funzione membro addogni volta che sarebbe necessario un inserimento.
Moooeeeep,

verificato con i miei dati e il tempo% di Ipython il tuo metodo sembra più veloce sul test MA: "La corsa più lenta ha richiesto 4,34 volte più lunga della più veloce. Ciò potrebbe significare che un risultato intermedio viene memorizzato nella cache"
Joop

1
@moooeeeep, ho aggiunto un'altra versione al tuo script per farti provare :) Prova anche pypyse lo hai a portata di mano e stai andando veloce.
John La Rooy,

@JohnLaRooy Bel miglioramento delle prestazioni! È interessante notare che quando ho usato pypy per valutare i risultati, l'approccio Counter-based migliora significativamente.
Moooeeeep,

42

Puoi usare iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

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

o se vuoi solo uno di ogni duplicato, questo può essere combinato 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]

Può anche gestire elementi non lavabili (comunque a costo di prestazione):

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

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

Questo è qualcosa che solo alcuni degli altri approcci qui possono gestire.

Punti di riferimenti

Ho fatto un rapido benchmark contenente la maggior parte (ma non tutti) degli approcci citati qui.

Il primo benchmark includeva solo una piccola gamma di lunghezze di elenco perché alcuni approcci hanno un O(n**2)comportamento.

Nei grafici l'asse y rappresenta il tempo, quindi un valore più basso significa migliore. È anche tracciato log-log in modo da poter visualizzare meglio l'ampia gamma di valori:

inserisci qui la descrizione dell'immagine

Rimuovendo gli O(n**2)approcci ho fatto un altro benchmark fino a mezzo milione di elementi in un elenco:

inserisci qui la descrizione dell'immagine

Come puoi vedere, l' iteration_utilities.duplicatesapproccio è più veloce di qualsiasi altro approccio e persino il concatenamento è unique_everseen(duplicates(...))stato più veloce o altrettanto veloce degli altri approcci.

Un'altra cosa interessante da notare qui è che gli approcci dei panda sono molto lenti per elenchi di piccole dimensioni ma possono facilmente competere per elenchi più lunghi.

Tuttavia, dal momento che questi benchmark mostrano che la maggior parte degli approcci si comporta approssimativamente allo stesso modo, quindi non importa molto quale viene utilizzato (tranne per i 3 che hanno O(n**2)runtime).

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

Indice di riferimento 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()

Indice di riferimento 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()

disconoscimento

1 Questo è da una libreria di terze parti che ho scritto: iteration_utilities.


1
Ho intenzione di tirarmi fuori il collo qui e suggerire di scrivere una biblioteca su misura per fare il lavoro in C piuttosto che in Python non è probabilmente lo spirito della risposta che si cercava - ma questo è un approccio legittimo! Mi piace l'ampiezza della risposta e la visualizzazione grafica dei risultati - molto bello vedere che stanno convergendo, mi chiedo se mai si incrociano mentre gli input aumentano ulteriormente! Domanda: qual è il risultato con liste per lo più ordinate rispetto a liste completamente casuali?
F1Rumors il

30

Mi sono imbattuto in questa domanda mentre cercavo qualcosa di simile - e mi chiedo perché nessuno offrisse una soluzione basata su un generatore? Risolvere questo problema sarebbe:

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

Mi occupavo della scalabilità, quindi ho testato diversi approcci, inclusi elementi ingenui che funzionano bene su elenchi di piccole dimensioni, ma scalano orribilmente man mano che gli elenchi diventano più grandi (nota: sarebbe stato meglio usare timeit, ma questo è illustrativo).

Ho incluso @moooeeeep per il confronto (è incredibilmente veloce: più veloce se l'elenco di input è completamente casuale) e un approccio itertools che è ancora più veloce per gli elenchi per lo più ordinati ... Ora include l'approccio panda da @firelynx - lento, ma non orribilmente così e semplice. Nota: l'approccio sort / tee / zip è costantemente più veloce sulla mia macchina per grandi elenchi per lo più ordinati, moooeeeep è più veloce per gli elenchi mescolati, ma il tuo chilometraggio può variare.

vantaggi

  • molto veloce semplice da testare per 'qualsiasi' duplicati usando lo stesso codice

ipotesi

  • I duplicati devono essere segnalati una sola volta
  • Non è necessario conservare l'ordine duplicato
  • Il duplicato potrebbe trovarsi ovunque nell'elenco

Soluzione più veloce, voci 1m:

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

Approcci testati

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

I risultati del test "tutti i duplicati" sono stati coerenti, trovando i duplicati "prima" e poi "tutti" in questo array:

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  

Quando le liste vengono mescolate per prime, il prezzo dell'ordinamento diventa evidente: l'efficienza diminuisce notevolmente e l'approccio @moooeeeep domina, con gli approcci di set & dict che sono simili ma i performer minori:

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  

@moooeeeep - sii interessato a vedere le tue opinioni sull'approccio ifilter / izip / tee.
F1Rumors

1
questa risposta è incredibilmente buona. Non capisco che non avesse più punti per le spiegazioni e i test che sono molto utili per coloro che ne avrebbero bisogno.
Dlewin il

1
l'ordinamento di python è O (n) quando un solo oggetto è fuori servizio. Dovresti renderlo random.shuffle(c)conto. Inoltre, non riesco a replicare i tuoi risultati nemmeno quando eseguo lo script inalterato (ordinamento totalmente diverso), quindi forse dipende anche dalla CPU.
John La Rooy,

Grazie @ John-La-Rooy, l'osservazione astuta sulla CPU / macchina locale ha un impatto - quindi dovrei modificare l'articolo YYMV . L'uso dell'ordinamento O (n) è stato deliberato: l'elemento duplicato viene inserito in posizioni diverse in modo specifico per vedere l'impatto dell'approccio se esiste un solo duplicato in una posizione buona (inizio dell'elenco) o cattiva (fine dell'elenco) con questi approcci. Ho preso in considerazione un elenco casuale - ad esempio random.shuffle - ma ho deciso che sarebbe stato sensato solo se avessi fatto molte più corse! Dovrò restituire e confrontare un equivalente corsa multipla / shuffle e vedere qual è l'impatto.
F1Rumors il

Modificato per includere l'approccio dei panda @firelynx e per essere eseguito su un elenco completamente mescolato e su un elenco ordinato. Questo perché il timsort nativo usato da Python è malvagio nei dati per lo più ordinati (caso migliore) e gli elenchi mescolati sono il suo scenario peggiore - che scuote i risultati.
F1Rumors il

13

Usando i panda:

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

10

collezioni.Counter è nuovo in 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'
>>> 

In una versione precedente puoi invece usare un dict convenzionale:

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]

9

Ecco una soluzione chiara e concisa:

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

li = list(set(li))

L'elenco originale è perso, però. Questo può essere risolto copiando il contenuto in un altro elenco - temp = li [:]
Nikhil Prabhu

3
È un esercizio piuttosto sgradevole su elenchi di grandi dimensioni: rimuovere elementi dagli elenchi è piuttosto costoso!
F1Rumors

7

Senza la conversione in elenco e probabilmente il modo più semplice sarebbe qualcosa di simile di seguito. Questo può essere utile durante un'intervista quando chiedono di non usare i set

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

======= altro per ottenere 2 elenchi separati di valori univoci e valori duplicati

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)

1
Ciò non comporta un elenco di duplicati di un (o dell'elenco originale), ma comporta un elenco di tutti gli elementi univoci di un (o dell'elenco originale). Cosa farebbe qualcuno dopo aver finito di formare la lista "dup"?
gameCoder95

6

Lo farei con i panda, perché uso molto i panda

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

[3,6]

Probabilmente non è molto efficiente, ma è sicuramente meno codice di molte altre risposte, quindi ho pensato di contribuire


3
Si noti inoltre che Panda contiene una funzione di duplicati incorporata pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken

6

il terzo esempio della risposta accettata fornisce una risposta errata e non tenta di fornire duplicati. Ecco la versione corretta:

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

6

Che ne dite semplicemente di scorrere ogni elemento nell'elenco controllando il numero di occorrenze, quindi aggiungendole a un set che stamperà i duplicati. Spero che questo aiuti qualcuno là fuori.

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]

5

Possiamo usare itertools.groupbyper trovare tutti gli oggetti che hanno duplicati:

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  

L'output sarà:

4
6

1
O più concisamente:dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
dal

5

Immagino che il modo più efficace per trovare duplicati in un elenco sia:

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

Usa Countertutti gli elementi e tutti gli elementi unici. Sottraendo il primo con il secondo si tralasciano solo i duplicati.


4

Un po 'in ritardo, ma forse utile per alcuni. Per un elenco di grandi dimensioni, ho trovato che ha funzionato per me.

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]

Mostra solo e tutti i duplicati e conserva l'ordine.


3

Il modo molto semplice e rapido per trovare duplicati con una iterazione in Python è:

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

testListDict = {}

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

print testListDict

L'output sarà il seguente:

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

Questo e altro nel mio blog http://www.howtoprogramwithpython.com


3

Sto entrando molto tardi in questa discussione. Anche se, vorrei affrontare questo problema con una fodera. Perché questo è il fascino di Python. se vogliamo solo inserire i duplicati in un elenco separato (o in qualsiasi raccolta), suggerirei di fare come di seguito. Diciamo che abbiamo un elenco duplicato che possiamo chiamare come "target"

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

Ora, se vogliamo ottenere i duplicati, possiamo usare un solo liner come di seguito:

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

Questo codice inserirà i record duplicati come chiave e conterà come valore nel dizionario "duplicati". Il dizionario "duplicato" sarà simile al seguente:

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

Se vuoi solo tutti i record con i duplicati da soli in un elenco, è di nuovo un codice molto più breve:

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

L'output sarà:

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

Funziona perfettamente nelle versioni python 2.7.x +


3

Python 3.8 one-liner se non ti interessa scrivere il tuo algoritmo o utilizzare le librerie:

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)

Stampa l'elemento e conta:

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

groupbyaccetta una funzione di raggruppamento in modo da poter definire i raggruppamenti in diversi modi e restituire Tuplecampi aggiuntivi , se necessario.

groupby è pigro, quindi non dovrebbe essere troppo lento.


2

Alcuni altri test. Certo da fare ...

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

... è troppo costoso. È circa 500 volte più veloce (l'array più lungo offre risultati migliori) per utilizzare il prossimo metodo finale:

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 anelli, nessuna l.count()operazione molto costosa .

Ecco un codice per confrontare i metodi, ad esempio. Il codice è sotto, ecco l'output:

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

Il codice di 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()

2

Metodo 1:

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

Spiegazione: [val per idx, val in enumerate (input_list) se val in input_list [idx + 1:]] è una comprensione dell'elenco, che restituisce un elemento, se lo stesso elemento è presente dalla sua posizione corrente, nell'elenco, l'indice .

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

a partire dal primo elemento nell'elenco, 42, con l'indice 0, controlla se l'elemento 42 è presente in input_list [1:] (ovvero dall'indice 1 fino alla fine dell'elenco) Perché 42 è presente in input_list [1:] , restituirà 42.

Quindi passa all'elemento 31 successivo, con l'indice 1, e controlla se l'elemento 31 è presente in input_list [2:] (cioè dall'indice 2 fino alla fine dell'elenco), poiché 31 è presente in input_list [2:], restituirà 31.

allo stesso modo passa attraverso tutti gli elementi dell'elenco e restituirà solo gli elementi ripetuti / duplicati in un elenco.

Quindi, poiché abbiamo duplicati, in un elenco, dobbiamo scegliere uno di ciascun duplicato, ovvero rimuovere duplicati tra duplicati e, per fare ciò, chiamiamo un set di pitone incorporato chiamato (), e rimuove i duplicati,

Quindi ci rimane un set, ma non un elenco, e quindi per convertire da un set a list, usiamo, typecasting, list () e che converte il set di elementi in un elenco.

Metodo 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

Spiegazione: Qui creiamo due elenchi vuoti, per cominciare. Quindi continua a spostarti tra tutti gli elementi dell'elenco, per vedere se esiste in temp_list (inizialmente vuoto). Se non è presente nell'elenco temp_, quindi lo aggiungiamo all'elenco temp_ usando il metodo append .

Se esiste già in temp_list, significa che l'elemento corrente dell'elenco è un duplicato e quindi dobbiamo aggiungerlo a dupe_list usando il metodo append .


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]

Fondamentalmente si rimuovono i duplicati convertendoli in set ( clean_list), quindi si esegue l'iterazione di raw_list, rimuovendo ciascuno itemnell'elenco pulito per occorrenza in raw_list. Se itemnon viene trovato, l' ValueErroreccezione sollevata viene rilevata e itemaggiunta aduplicated_items all'elenco.

Se è necessario l'indice degli elementi duplicati, basta enumeratel'elenco e giocare con l'indice. ( for index, item in enumerate(raw_list):) che è più veloce e ottimizzato per elenchi di grandi dimensioni (come migliaia + di elementi)


2

uso del list.count()metodo nell'elenco per scoprire gli elementi duplicati di un determinato elenco

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)

modo semplice per trovare gli elementi duplicati nell'elenco usando la funzione di conteggio
Ravikiran D

2

one-liner, per divertimento, e dove è richiesta una singola dichiarazione.

(lambda iterable: reduce(lambda (uniq, dup), item: (uniq, dup | {item}) if item in uniq else (uniq | {item}, dup), iterable, (set(), set())))(some_iterable)

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)

1

Una soluzione di linea:

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

1

Ci sono molte risposte qui, ma penso che questo sia un approccio relativamente molto leggibile e di facile comprensione:

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)

Appunti:

  • Se desideri conservare il conteggio delle duplicazioni, elimina il cast da "impostare" in fondo per ottenere l'elenco completo
  • Se si preferisce utilizzare i generatori, sostituire duplicates.append (x) con yield x e l'istruzione return in fondo (è possibile eseguire il cast per impostare in seguito)

1

Ecco un generatore veloce che utilizza un dict per memorizzare ogni elemento come chiave con un valore booleano per verificare se l'articolo duplicato è già stato prodotto.

Per gli elenchi con tutti gli elementi che sono tipi hash:

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]

Per elenchi che potrebbero contenere elenchi:

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]

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

Si restituisce un set e non un elenco come richiesto. Un set contiene solo elementi unici, quindi l'istruzione if non è realmente necessaria. Dovresti anche spiegare, qual è il vantaggio della tua soluzione rispetto all'altro.
clemens,

1

Quando si utilizza 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] 

0

questo è il modo in cui ho dovuto farlo perché mi sono sfidato a non usare altri metodi:

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

quindi il tuo campione funziona come:

>>>a = [1,2,3,3,3,4,5,6,6,7]
>>>dupList(a)
[1, 2, 3, 4, 5, 6, 7]

3
Non è quello che voleva l'OP. Voleva un elenco dei duplicati, non un elenco con i duplicati rimossi. Per fare un elenco con i duplicati rimossi, suggerirei duplist = list(set(a)).
zondo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.