Rimozione dei duplicati negli elenchi


998

Praticamente ho bisogno di scrivere un programma per verificare se un elenco ha dei duplicati e se lo fa li rimuove e restituisce un nuovo elenco con gli elementi che non sono stati duplicati / rimossi. Questo è quello che ho ma, ad essere sincero, non so cosa fare.

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

22
La tua descrizione dice che controlli "un elenco" per i duplicati, ma il tuo codice controlla due elenchi.
Brendan Long,


* using set: list (set (ELEMENTS_LIST)) * using dizionari: list (dict.fromkeys (ELEMENTS_LIST))
Shayan Amani,

Risposte:


1643

L'approccio comune per ottenere una raccolta univoca di elementi è utilizzare a set. Gli insiemi sono raccolte non ordinate di oggetti distinti . Per creare un set da qualsiasi iterabile, puoi semplicemente passarlo alla set()funzione integrata. Se in seguito hai bisogno di un nuovo elenco reale, puoi allo stesso modo passare il set alla list()funzione.

L'esempio seguente dovrebbe coprire tutto ciò che stai cercando di fare:

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

Come puoi vedere dal risultato dell'esempio, l'ordine originale non viene mantenuto . Come accennato in precedenza, i set stessi sono raccolte non ordinate, quindi l'ordine viene perso. Quando si converte un set in un elenco, viene creato un ordine arbitrario.

Mantenere l'ordine

Se l'ordine è importante per te, dovrai utilizzare un meccanismo diverso. Una soluzione molto comune per questo è fare affidamento OrderedDictper mantenere l'ordine delle chiavi durante l'inserimento:

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

A partire da Python 3.7 , il dizionario integrato garantisce anche l'ordine di inserimento, quindi puoi usarlo direttamente anche se sei su Python 3.7 o versioni successive (o CPython 3.6):

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

Si noti che ciò può comportare prima un sovraccarico nella creazione di un dizionario e quindi nella creazione di un elenco da esso. Se in realtà non hai bisogno di preservare l'ordine, spesso stai meglio usando un set, soprattutto perché ti dà molte più operazioni con cui lavorare. Dai un'occhiata a questa domanda per maggiori dettagli e modi alternativi per preservare l'ordine durante la rimozione dei duplicati.


Infine, nota che sia la soluzione setche la OrderedDict/ dictrichiedono che i tuoi articoli siano hash . Questo di solito significa che devono essere immutabili. Se hai a che fare con oggetti non cancellabili (ad es. Oggetti elenco), dovrai utilizzare un approccio lento in cui dovrai sostanzialmente confrontare ogni oggetto con ogni altro oggetto in un ciclo nidificato.


4
Questo non funziona con elementi di elenco non lavabili (ad esempio un elenco di elenchi)
KNejad,

3
@KNejad Ecco cosa afferma l'ultimo paragrafo.
colpì il

Oh oops. Avrei dovuto leggere tutto. Quello che ho finito per fare è stato usare le tuple invece degli elenchi in modo che questo approccio potesse ancora funzionare.
KNejad,

aggiungi questo ad esempio, t = [3, 2, 1, 1, 2, 5, 6, 7, 8], mostra chiaramente la differenza!
sailfish009,

"... sovraccarico di creare prima un dizionario ... Se in realtà non è necessario preservare l'ordine, è meglio usare un set." - Ho profilato questo perché ero curioso se fosse effettivamente vero. I miei tempi mostrano che in effetti il ​​set è leggermente più veloce: 1,12 µs per loop (set) contro 1,53 µs per loop (dict) su loop da 1 milione con una differenza di tempo assoluta di circa 4 secondi su iterazioni da 1 milione. Quindi, se lo stai facendo in un circuito interno stretto, ti potrebbe interessare, altrimenti probabilmente no.
Millerdev,

414

In Python 2.7 , il nuovo modo di rimuovere i duplicati da un iterabile mantenendolo nell'ordine originale è:

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

In Python 3.5 , OrderedDict ha un'implementazione C. I miei tempi mostrano che questo è ora il più veloce e il più breve dei vari approcci per Python 3.5.

In Python 3.6 , il dict regolare divenne sia ordinato che compatto. (Questa funzione è valida per CPython e PyPy ma potrebbe non essere presente in altre implementazioni). Questo ci dà un nuovo modo più veloce di dedurre mantenendo l'ordine:

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

In Python 3.7 , il dict regolare è garantito per entrambi ordinato in tutte le implementazioni. Quindi, la soluzione più breve e veloce è:

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

10
Penso che questo sia l'unico modo per mantenere gli articoli in ordine.
Herberth Amaral,

19
@HerberthAmaral: è molto lontano dall'essere vero, vedi Come si rimuovono i duplicati da un elenco in Python mantenendo l'ordine?
Martijn Pieters

5
@MartijnPieters Correzione: penso che questo sia l'unico modo semplice per mantenere gli articoli in ordine.
Herberth Amaral,

12
Anche per questo, il contenuto dell'elenco originale deve essere hash
Davide,

Come accennato da @Davide, l'elenco originale deve essere hash. Ciò significa che non funziona per un elenco di dizionari. TypeError: unhashable type: 'dictlist'
CraZ,

187

È un one-liner: list(set(source_list))farà il trucco.

A setè qualcosa che non può avere duplicati.

Aggiornamento: un approccio che preserva l'ordine è di due righe:

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

Qui usiamo il fatto che OrderedDictricorda l'ordine di inserimento delle chiavi e non lo cambia quando viene aggiornato un valore in una determinata chiave. Inseriamo Truecome valori, ma potremmo inserire qualsiasi cosa, i valori non vengono semplicemente utilizzati. ( setfunziona in modo molto simile a un dictcon valori ignorati).


5
Funziona solo se source_listè lavabile.
Adrian Keister,

@AdrianKeister: questo è vero. Esistono oggetti che hanno una semantica di uguaglianza ragionevole ma non sono hash, ad esempio elenchi. OTOH se non possiamo avere una scorciatoia come un hastable, finiamo con un algoritmo quadratico che confronta semplicemente ogni elemento con tutti gli elementi unici attualmente conosciuti. Questo può essere totalmente OK per input brevi, specialmente con molti duplicati.
9000,

Giusto, esattamente. Penso che la tua risposta sarebbe di qualità superiore se prendessi in considerazione questo caso d'uso molto comune.
Adrian Keister,

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

33
Si noti che questo metodo funziona in tempo O (n ^ 2) ed è quindi molto lento su grandi elenchi.
dotancohen,

@Chris_Rands: non è sicuro che frozensetfunzioni con contenuti non cancellabili. Ricevo ancora l'errore non cancellabile durante l'utilizzo frozenset.
Adrian Keister,

85

Se non ti interessa l'ordine, fai questo:

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

A setè garantito per non avere duplicati.


3
Non funziona se non lè lavabile.
Adrian Keister,

41

Per creare un nuovo elenco mantenendo l'ordine dei primi elementi dei duplicati in L

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

per esempio if L=[1, 2, 2, 3, 4, 2, 4, 3, 5]allora newlistsarà[1,2,3,4,5]

Questo controlla che ogni nuovo elemento non sia apparso in precedenza nell'elenco prima di aggiungerlo. Inoltre non ha bisogno di importazioni.


3
Ciò ha una complessità temporale di O (n ^ 2) . Le risposte con sete OrderedDictpossono avere una complessità temporale inferiore ammortizzata.
blubberdiblub,

Ho usato nel mio codice questa soluzione e ha funzionato benissimo ma penso che
richieda

@blubberdiblub puoi spiegare quale meccanismo di codice più efficiente esiste in set e OrderedDict che potrebbe farli perdere meno tempo? (escluso il sovraccarico di caricarli)
ilias iliadis

@iliasiliadis Le solite implementazioni di set e dict usano hash o (qualche forma di bilanciato) alberi. Devi considerare di costruire il set o il dict e di cercarlo (più volte), ma la loro complessità ammortizzata di solito è ancora inferiore a O (n ^ 2) . "Ammortizzati" in termini semplici significa in media (possono avere casi peggiori con una complessità maggiore rispetto al caso medio). Questo è rilevante solo quando hai un gran numero di articoli.
Blubberdiblub,

25

Un collega mi ha inviato la risposta accettata come parte del suo codice per una visione di codice oggi. Mentre ammiro certamente l'eleganza della risposta in questione, non sono contento della performance. Ho provato questa soluzione (uso set per ridurre i tempi di ricerca)

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

Per confrontare l'efficienza, ho usato un campione casuale di 100 numeri interi - 62 erano unici

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

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

Ecco i risultati delle misurazioni

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

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

Bene, cosa succede se set viene rimosso dalla soluzione?

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

Il risultato non è così negativo come con OrderedDict , ma ancora più di 3 volte della soluzione originale

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

Bello usare la ricerca rapida impostata per accelerare il confronto in loop. Se l'ordine non ha importanza, l'elenco (set (x)) è ancora 6 volte più veloce di questo
Joop,

@Joop, questa è stata la mia prima domanda per il mio collega - l'ordine è importante; altrimenti, sarebbe stato un problema banale
vulcano, il

versione ottimizzata del set ordinato, per chiunque sia interessato def unique(iterable)::; seen = set(); seen_add = seen.add; return [item for item in iterable if not item in seen and not seen_add(item)]
DrD

25

Ci sono anche soluzioni che usano Panda e Numpy. Entrambi restituiscono array numpy, quindi è necessario utilizzare la funzione .tolist()se si desidera un elenco.

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

Soluzione di Panda

Utilizzando la funzione Panda unique():

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

Soluzione numpy

Usando la funzione numpy unique().

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

Si noti che numpy.unique () ordina anche i valori . Quindi l'elenco t2viene restituito ordinato. Se si desidera conservare l'ordine, utilizzare come in questa risposta :

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

La soluzione non è così elegante rispetto alle altre, tuttavia, rispetto a pandas.unique (), numpy.unique () consente anche di verificare se le matrici nidificate sono uniche lungo un asse selezionato.


Questo convertirà l'elenco in array intorpidito che è un disastro e non funzionerà per le stringhe.
user227666,

1
@utente227666 grazie per la tua recensione ma non è vero che funziona anche con la stringa e puoi aggiungere .tolist se vuoi ottenere un elenco ...
GM

1
Penso che sia un po 'come cercare di uccidere un'ape con una mazza. Funziona, certo! Ma importare una libreria solo per questo scopo potrebbe essere un po 'eccessivo, no?
Debosmit Ray

@DebosmitRay potrebbe essere utile se lavori in Data Science dove di solito lavori con numpy e molte volte devi lavorare con l'array numpy.
GM

la migliore risposta nel 2020 @DebosmitRay spero che cambi idea e usi numpy / panda ogni volta che puoi
Egos,

21

Un altro modo di fare:

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

1
Si noti che nelle moderne versioni di Python (credo 2.7+, ma non ricordo per certo), keys()restituisce un oggetto vista dizionario, non un elenco.
Dustin Wyatt,

16

Semplice e facile:

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

Produzione:

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

5
complessità quadratica comunque - inè l'operazione O (n) e il tuo cleanlistavrà al massimo nnumeri => peggiore dei casi ~ O (n ^ 2)
jermenkoo

6
la comprensione dell'elenco non deve essere utilizzata per gli effetti collaterali.
Jean-François Fabre

13

In questa risposta, ci saranno due sezioni: due soluzioni uniche e un grafico della velocità per soluzioni specifiche.

Rimozione di elementi duplicati

La maggior parte di queste risposte rimuove solo gli oggetti duplicati che sono lavabili , ma questa domanda non implica che non abbia solo bisogno di oggetti lavabili , il che significa che offrirò alcune soluzioni che non richiedono oggetti lavabili .

collezioni.Counter è un potente strumento nella libreria standard che potrebbe essere perfetto per questo. C'è solo un'altra soluzione che ha persino Counter. Tuttavia, tale soluzione si limita anche alle chiavi cancellabili .

Per consentire le chiavi non cancellabili in Counter, ho creato una classe Container, che tenterà di ottenere la funzione hash predefinita dell'oggetto, ma se fallisce, proverà la sua funzione di identità. Definisce anche un metodo eq e un metodo hash . Questo dovrebbe essere sufficiente per consentire articoli non lavabili nella nostra soluzione. Gli oggetti non lavabili verranno trattati come se fossero cancellabili. Tuttavia, questa funzione hash utilizza l'identità per oggetti non lavabili, il che significa che due oggetti uguali che sono entrambi non lavabili non funzioneranno. Ti suggerisco di ignorare questo e di cambiarlo per usare l'hash di un tipo mutabile equivalente (come usare hash(tuple(my_list))se my_listè un elenco).

Ho anche fatto due soluzioni. Un'altra soluzione che mantiene l'ordine degli articoli, usando una sottoclasse di OrderedDict e Counter che si chiama 'OrderedCounter'. Ora, ecco le funzioni:

from collections import OrderedDict, Counter

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

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

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

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

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

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

remd è l'ordinamento non ordinato, oremd è l'ordinamento ordinato. Puoi dire chiaramente quale è più veloce, ma lo spiegherò comunque. L'ordinamento non ordinato è leggermente più veloce. Mantiene meno dati, poiché non ha bisogno di ordine.

Ora, volevo anche mostrare i confronti di velocità di ciascuna risposta. Quindi, lo farò ora.

Quale funzione è la più veloce?

Per rimuovere i duplicati, ho raccolto 10 funzioni da alcune risposte. Ho calcolato la velocità di ciascuna funzione e l'ho inserita in un grafico usando matplotlib.pyplot .

L'ho diviso in tre turni di rappresentazione grafica. Un hashable è qualsiasi oggetto che può essere sottoposto a hash, un non lavabile è qualsiasi oggetto che non può essere hash. Una sequenza ordinata è una sequenza che conserva l'ordine, una sequenza non ordinata non conserva l'ordine. Ora, ecco alcuni altri termini:

Hashable non ordinato era per qualsiasi metodo che rimuoveva i duplicati, che non doveva necessariamente mantenere l'ordine. Non ha dovuto funzionare per inhahable, ma potrebbe.

Hashable ordinato è stato per qualsiasi metodo che ha mantenuto l'ordine degli elementi nell'elenco, ma non ha dovuto funzionare per gli inhahable, ma poteva.

Ordered Unhashable è stato qualsiasi metodo che ha mantenuto l'ordine degli articoli nell'elenco e ha funzionato in modo insostituibile.

Sull'asse y è la quantità di secondi impiegata.

Sull'asse x è presente il numero a cui è stata applicata la funzione.

Abbiamo generato sequenze di hashble non ordinati e ordinato hashable con la seguente comprensione: [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

Per gli insostituibili ordinati: [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

Nota che c'è un "passo" nell'intervallo perché senza di esso, questo avrebbe richiesto 10 volte più tempo. Anche perché secondo la mia opinione personale, ho pensato che avrebbe potuto sembrare un po 'più facile da leggere.

Nota anche che i tasti della legenda sono quelli che ho cercato di indovinare come le parti più vitali della funzione. Per quanto riguarda quale funzione fa il peggio o il meglio? Il grafico parla da solo.

Con quello risolto, ecco i grafici.

Hashables non ordinati

inserisci qui la descrizione dell'immagine (Ingrandito) inserisci qui la descrizione dell'immagine

Hashables ordinati

inserisci qui la descrizione dell'immagine (Ingrandito) inserisci qui la descrizione dell'immagine

Non ordinabili ordinati

inserisci qui la descrizione dell'immagine (Ingrandito) inserisci qui la descrizione dell'immagine


11

Avevo un dict nella mia lista, quindi non potevo usare l'approccio sopra. Ho ricevuto l'errore:

TypeError: unhashable type:

Quindi, se ti interessa l' ordine e / o alcuni articoli non sono lavabili . Quindi potresti trovare utile questo:

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

Alcuni potrebbero considerare la comprensione dell'elenco con un effetto collaterale non essere una buona soluzione. Ecco un'alternativa:

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

6
mapcon un effetto collaterale è ancora più fuorviante di un elenco composto da un effetto collaterale. Inoltre, lambda x: unique_list.append(x)è solo un modo più clunkier e più lento per passare unique_list.append.
Abarnert,

Modo molto utile per aggiungere elementi in una sola riga, grazie!
ZLNK,

2
@ZLNK per favore, non usarlo mai. Oltre ad essere concettualmente brutto, è anche estremamente inefficiente, perché in realtà crei un elenco potenzialmente grande e lo butti via solo per eseguire l'iterazione di base.
Eli Korvigo,

10

Tutti gli approcci per la conservazione degli ordini che ho visto qui finora utilizzano un confronto ingenuo (con O (n ^ 2) complessità temporale nel migliore dei casi) o combinazioni pesanti OrderedDicts/ set+ listche sono limitate a input hash. Ecco una soluzione O (nlogn) indipendente dall'hash:

L'aggiornamento ha aggiunto l' keyargomento, la documentazione e la compatibilità con Python 3.

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

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

Tuttavia, questa soluzione richiede elementi ordinabili. Lo userò per unificare il mio elenco di elenchi: è una seccatura elencarli e cancellarli tuple(). | | | | - In generale, il processo di hash richiede un tempo proporzionale alla dimensione di tutti i dati, mentre questa soluzione richiede un tempo O (nlog (n)), a seconda della lunghezza dell'elenco.
Loxaxs,

Penso che l'approccio basato su set sia ugualmente economico (O (n log n)), o più economico, dell'ordinamento + rilevamento di elementi unici. (Questo approccio parallelizzerebbe molto meglio, però.) Inoltre non preserva esattamente l'ordine iniziale, ma dà un ordine prevedibile.
9000,

@ 9000 Questo è vero. Non ho mai menzionato la complessità temporale di un approccio basato su hash table, che è ovviamente O (n). Qui puoi trovare molte risposte che incorporano le tabelle hash. Non sono universali, tuttavia, perché richiedono che gli oggetti siano hashing. Inoltre, richiedono molta più memoria.
Eli Korvigo,

Richiede tempo per leggere e comprendere questa risposta. C'è un punto nell'enumerare quando non si usano gli indici? The reduce() sta già lavorando su una raccolta differenziata srt_enum, perché hai fatto sortednuovamente domanda ?
Brayoni,

@Brayoni il primo ordinamento è lì per raggruppare valori uguali, il secondo ordinamento è lì per ripristinare l'ordine iniziale. L'enumerazione è necessaria per tenere traccia dell'ordine relativo originale.
Eli Korvigo,

9

Se vuoi preservare l'ordine e non utilizzare moduli esterni qui è un modo semplice per farlo:

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

Nota: questo metodo preserva l'ordine di apparizione, quindi, come visto sopra, nove ne seguiranno uno perché era la prima volta che appariva. Questo, tuttavia, è lo stesso risultato che otterresti facendo

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

ma è molto più corto e corre più veloce.

Questo funziona perché ogni volta che la fromkeysfunzione tenta di creare una nuova chiave, se il valore esiste già, lo sovrascriverà semplicemente. Ciò tuttavia non influirà affatto sul dizionario, in quanto fromkeyscrea un dizionario in cui tutte le chiavi hanno il valore None, così efficacemente elimina tutti i duplicati in questo modo.


Prova anche qui
Vineeshvs

8

Puoi anche fare questo:

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

La ragione per cui sopra funziona è che il indexmetodo restituisce solo il primo indice di un elemento. Gli elementi duplicati hanno indici più alti. Fare riferimento qui :

list.index (x [, start [, end]])
Restituisce indice in base zero nell'elenco del primo elemento il cui valore è x. Genera un ValueError se non esiste tale elemento.


Questo è orribilmente inefficiente. list.indexè un'operazione a tempo lineare che rende quadratica la soluzione.
Eli Korvigo,

Hai ragione. Ma credo anche che sia abbastanza ovvio che la soluzione è intesa per essere una fodera che preserva l'ordine. Tutto il resto è già qui.
Atonal,

7

Prova a usare i set:

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

print t | t1
print t - t1

7

Ridurre la variante con riserva di ordinazione:

Supponiamo di avere un elenco:

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

Ridurre la variante (inefficiente):

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

5 volte più veloce ma più sofisticato

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

Spiegazione:

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

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

reduce(reducer, l, default)[0]

7

Il miglior approccio per rimuovere i duplicati da un elenco è usare la funzione set () , disponibile in Python, convertendo di nuovo quel set in elenco

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

@MeetZaveri felice.!
Anurag Misra,

L'istanza di nuovi elenchi e set non è gratuita. Cosa succede se lo facciamo molte volte in rapida successione (cioè in un ciclo molto stretto) e le liste sono molto piccole?
Z4 livello

6

È possibile utilizzare la seguente funzione:

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

Esempio :

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

Uso:

rem_dupes(my_list)

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


5

Esistono molte altre risposte che suggeriscono diversi modi per farlo, ma sono tutte operazioni batch e alcune di esse eliminano l'ordine originale. Potrebbe andare bene a seconda di ciò di cui hai bisogno, ma se vuoi iterare i valori nell'ordine della prima istanza di ogni valore e vuoi rimuovere i duplicati al volo contro tutti in una volta, puoi usare questo generatore:

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

Questo restituisce un generatore / iteratore, quindi puoi usarlo ovunque tu possa usare un iteratore.

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

print()

Produzione:

1 2 3 4 5 6 7 8

Se vuoi un list, puoi farlo:

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

print(unique_list)

Produzione:

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

seen = set(iterable); for item in seen: yield itemè quasi certamente più veloce. (Non ho provato questo caso specifico, ma sarebbe la mia ipotesi.)
dylnmc

2
@dylnmc, è un'operazione batch e perde anche l'ordinamento. La mia risposta era specificamente pensata per essere al volo e in ordine di prima occorrenza. :)
Cyphase,

5

Senza usare set

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

print(uni_data) 

5

Puoi usare setper rimuovere i duplicati:

mylist = list(set(mylist))

Ma nota che i risultati non saranno ordinati. Se questo è un problema:

mylist.sort()

1
Puoi semplicemente fare: mylist = ordinato (list (set (mylist)))
Erik Campobadal

5

Un altro approccio migliore potrebbe essere,

import pandas as pd

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

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

e l'ordine rimane preservato.


Anche se questo potrebbe funzionare bene, l'uso di una libreria pesante come i panda a questo scopo sembra eccessivo.
Glutexo,

4

Questo si preoccupa dell'ordine senza troppe seccature (OrderdDict e altri). Probabilmente non è il modo più Pythonic, né il modo più breve, ma fa il trucco:

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

1. Non dovresti mai ombreggiare i nomi predefiniti (almeno importanti quanto list); 2. Il tuo metodo si ridimensiona in modo estremamente negativo: è quadratico nel numero di elementi in list.
Eli Korvigo,

1. Corretto, ma questo era un esempio; 2. Corretto, e questo è esattamente il motivo per cui l'ho offerto. Tutte le soluzioni pubblicate qui hanno pro e contro. Qualche sacrificio di semplicità o ordine, il mio sacrifica la scalabilità.
CG

questo è un algoritmo "Shlemiel il pittore" ...
Z4

4

sotto il codice è semplice per rimuovere i duplicati nell'elenco

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

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

restituisce [1,2,3,4]


2
Se non ti interessa l'ordine, questo richiede molto più tempo. list(set(..))(oltre 1 milione di passaggi) supererà questa soluzione di circa 10 secondi interi - mentre questo approccio richiede circa 12 secondi, list(set(..))richiede solo circa 2 secondi!
dylnmc,

@dylnmc questo è anche un duplicato di una risposta
Eli Korvigo

4

Ecco la soluzione pitonica più veloce rispetto agli altri elencati nelle risposte.

L'uso dei dettagli di implementazione della valutazione del corto circuito consente di utilizzare la comprensione dell'elenco, che è abbastanza veloce. visited.add(item)restituisce sempre Nonecome risultato, che viene valutato come False, quindi il lato destro dior sarebbe sempre il risultato di tale espressione.

Cronometra tu stesso

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

4

Utilizzando set :

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

Utilizzando unico :

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

4

Purtroppo. La maggior parte delle risposte qui non preservano l'ordine o sono troppo lunghe. Ecco una semplice risposta per preservare l'ordine.

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

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

Questo ti darà x con i duplicati rimossi ma preservando l'ordine.


3

Modo molto semplice in Python 3:

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

2
sorted(list(...))è ridondante ( sortedconverte già implicitamente il suo argomento in un nuovo list, lo ordina, quindi restituisce il nuovo list, quindi utilizzare entrambi significa rendere un temporaneo non necessario list). Utilizzare solo listse il risultato non deve essere ordinato, utilizzare solo sortedse il risultato deve essere ordinato.
ShadowRanger,

3

The Magic of Python Tipo incorporato

In Python, è molto semplice elaborare i casi complicati come questo e solo dal tipo incorporato di Python.

Lascia che ti mostri come fare!

Metodo 1: caso generale

Il modo ( codice di 1 riga ) per rimuovere l'elemento duplicato nell'elenco e mantenere comunque l'ordinamento

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

Otterrai il risultato

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

Metodo 2: Caso speciale

TypeError: unhashable type: 'list'

Il caso speciale da elaborare non lavabile ( codici a 3 righe )

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

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

print (new_line)

Otterrai il risultato:

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

Perché la tupla è hash e puoi convertire facilmente i dati tra elenco e tupla

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.