Verifica se tutti gli elementi in un elenco sono unici


104

Qual è il modo migliore (migliore come nel modo convenzionale) per verificare se tutti gli elementi di un elenco sono unici?

Il mio attuale approccio che utilizza a Counterè:

>>> x = [1, 1, 1, 2, 3, 4, 5, 6, 2]
>>> counter = Counter(x)
>>> for values in counter.itervalues():
        if values > 1: 
            # do something

Posso fare di meglio?

Risposte:


164

Non il più efficiente, ma semplice e conciso:

if len(x) > len(set(x)):
   pass # do something

Probabilmente non farà molta differenza per gli elenchi brevi.


Questo è quello che faccio anch'io. Probabilmente non è efficiente per elenchi di grandi dimensioni.
tkerwin

Non necessariamente, questo eseguirà il corpo del condizionale se l'elenco contiene elementi ripetuti (il "#do qualcosa" nell'esempio).
yan

2
Abbastanza giusto, buona soluzione. Sto gestendo a malapena <500 elementi, quindi questo dovrebbe fare quello che voglio.
user225312

4
Per coloro che sono preoccupati dell'efficienza con elenchi lunghi, questo è efficiente per elenchi lunghi che sono effettivamente unici (dove tutti gli elementi devono essere controllati). Le prime soluzioni di uscita richiedono più tempo (circa 2 volte di più nei miei test) per elenchi effettivamente unici. Quindi ... se ti aspetti che la maggior parte delle tue liste sia unica, usa questa semplice soluzione per il controllo della lunghezza impostata. Se ti aspetti che la maggior parte dei tuoi elenchi NON sia univoca, utilizza una soluzione di uscita anticipata. Quale usare dipende dal tuo caso d'uso.
Russ

Questa risposta è carina. Tuttavia, stiamo attenti qui: len(x) > len(set(x))è vero quando gli elementi in xNON sono unici. Il titolo di questa domanda chiede esattamente l'opposto: "Verifica se tutti gli elementi di un elenco sono unici"
WhyWhat

96

Ecco una doppia linea che farà anche un'uscita anticipata:

>>> def allUnique(x):
...     seen = set()
...     return not any(i in seen or seen.add(i) for i in x)
...
>>> allUnique("ABCDEF")
True
>>> allUnique("ABACDEF")
False

Se gli elementi di x non sono modificabili, dovrai ricorrere all'uso di un elenco per seen:

>>> def allUnique(x):
...     seen = list()
...     return not any(i in seen or seen.append(i) for i in x)
...
>>> allUnique([list("ABC"), list("DEF")])
True
>>> allUnique([list("ABC"), list("DEF"), list("ABC")])
False

5
+1 pulito e non scorre l'intero elenco se non necessario.
Kos

@ paul-mcguire: Saresti disposto a concedere in licenza questo frammento di codice con una licenza compatibile con Apache 2.0 (ad esempio, Apache 2, BSD a 2/3 righe, MIT, X11, zlib). Mi piacerebbe usarlo in un progetto Apache 2.0 che sto utilizzando e poiché i termini di licenza di StackOverflow sono fubar , ti chiedo come autore originale.
Ryan Parman,

Ho pubblicato altro codice usando la licenza MIT, quindi per me funziona per questo frammento. Qualcosa di speciale che devo fare?
PaulMcG,

21

Potrebbe essere una soluzione per l'uscita anticipata

def unique_values(g):
    s = set()
    for x in g:
        if x in s: return False
        s.add(x)
    return True

tuttavia per piccoli casi o se l'uscita anticipata non è il caso comune, mi aspetto che len(x) != len(set(x))sia il metodo più veloce.


Ho accettato l'altra risposta perché non ero particolarmente alla ricerca di ottimizzazione.
user225312

2
Puoi accorciarlo inserendo la seguente riga dopo s = set()...return not any(s.add(x) if x not in s else True for x in g)
Andrew Clark

Potresti spiegare perché ti aspetteresti len(x) != len(set(x))di essere più veloce di così se l'uscita anticipata non è comune? Entrambe le operazioni non sono O (len (x)) ? (dov'è xl'elenco originale)
Chris Redford

Oh, capisco: il tuo metodo non è O (len (x)) perché controlli if x in sall'interno del ciclo O (len (x)) .
Chris Redford

15

per velocità:

import numpy as np
x = [1, 1, 1, 2, 3, 4, 5, 6, 2]
np.unique(x).size == len(x)

12

Che ne dici di aggiungere tutte le voci a un set e controllarne la lunghezza?

len(set(x)) == len(x)

1
Ha risposto un secondo dopo yan, ahi. Breve e dolce. Qualche motivo per non utilizzare questa soluzione?
jasonleonhard

Non tutte le sequenze (in particolare i generatori) supportano len().
PaulMcG

9

In alternativa a a set, puoi utilizzare a dict.

len({}.fromkeys(x)) == len(x)

9
Non vedo assolutamente alcun vantaggio nell'usare un dict su un set. Sembra complicare inutilmente le cose.
metasoarous

3

Un altro approccio completamente, utilizzando ordinato e groupby:

from itertools import groupby
is_unique = lambda seq: all(sum(1 for _ in x[1])==1 for x in groupby(sorted(seq)))

Richiede un ordinamento, ma esce sul primo valore ripetuto.


l'hashing è più veloce
dell'ordinamento

Sono venuto qui per pubblicare la stessa soluzione usando groupbye ho trovato questa risposta. Lo trovo molto elegante, poiché è una singola espressione e funziona con gli strumenti incorporati senza richiedere alcuna variabile aggiuntiva o istruzione di ciclo.
Lars Blumberg

1
Se la tua lista contiene oggetti arbitrari che non sono ordinabili, puoi usare la id()funzione per ordinarli in quanto questo è un prerequisito per groupby()funzionare:groupby(sorted(seq), key=id)
Lars Blumberg

3

Ecco una versione ricorsiva O (N 2 ) per divertimento:

def is_unique(lst):
    if len(lst) > 1:
        return is_unique(s[1:]) and (s[0] not in s[1:])
    return True

2

Ecco una funzione di uscita anticipata ricorsiva:

def distinct(L):
    if len(L) == 2:
        return L[0] != L[1]
    H = L[0]
    T = L[1:]
    if (H in T):
            return False
    else:
            return distinct(T)    

È abbastanza veloce per me senza utilizzare conversioni strane (lente) pur avendo un approccio in stile funzionale.


1
H in Tesegue una ricerca lineare e T = L[1:]copia la parte tagliata dell'elenco, quindi sarà molto più lenta rispetto alle altre soluzioni suggerite su elenchi grandi. Penso sia O (N ^ 2), mentre la maggior parte degli altri sono O (N) (insiemi) o O (N log N) (soluzioni basate sull'ordinamento).
Blckknght

1

Cosa ne pensi di questo

def is_unique(lst):
    if not lst:
        return True
    else:
        return Counter(lst).most_common(1)[0][1]==1

0

Puoi usare la sintassi di Yan (len (x)> len (set (x))), ma invece di set (x), definisci una funzione:

 def f5(seq, idfun=None): 
    # order preserving
    if idfun is None:
        def idfun(x): return x
    seen = {}
    result = []
    for item in seq:
        marker = idfun(item)
        # in old Python versions:
        # if seen.has_key(marker)
        # but in new ones:
        if marker in seen: continue
        seen[marker] = 1
        result.append(item)
    return result

e do len (x)> len (f5 (x)). Questo sarà veloce e preserva anche l'ordine.

Il codice è preso da: http://www.peterbe.com/plog/uniqifiers-benchmark


questa funzione f5 sarà più lenta rispetto all'uso di set che è meglio ottimizzato per la velocità. Questo codice inizia a interrompersi quando l'elenco diventa molto grande a causa della costosa operazione di "aggiunta". con elenchi di grandi dimensioni come x = range(1000000) + range(1000000), l'esecuzione di set (x) è più veloce di f5 (x). L'ordine non è un requisito nella domanda, ma anche l'esecuzione di Sort (set (x)) è ancora più veloce di f5 (x)
OkezieE

0

Utilizzando un approccio simile in un dataframe Pandas per verificare se il contenuto di una colonna contiene valori univoci:

if tempDF['var1'].size == tempDF['var1'].unique().size:
    print("Unique")
else:
    print("Not unique")

Per me, questo è istantaneo su una variabile int in un dataframe contenente oltre un milione di righe.


0

tutte le risposte sopra sono buone ma preferisco usare l' all_uniqueesempio di 30 secondi di Python

è necessario utilizzare set()nell'elenco fornito per rimuovere i duplicati, confrontare la sua lunghezza con la lunghezza dell'elenco.

def all_unique(lst):
  return len(lst) == len(set(lst))

restituisce Truese tutti i valori in un elenco semplice lo sono unique, Falsealtrimenti

x = [1,2,3,4,5,6]
y = [1,2,2,3,4,5]
all_unique(x) # True
all_unique(y) # False

-3

Per i principianti:

def AllDifferent(s):
    for i in range(len(s)):
        for i2 in range(len(s)):
            if i != i2:
                if s[i] == s[i2]:
                    return False
    return True

Mi piace questa risposta, solo perché mostra abbastanza bene quale codice non devi scrivere quando usi un set. Non lo etichetterei "per principianti", poiché credo che i principianti dovrebbero imparare a farlo nel modo giusto in anticipo; ma ho incontrato alcuni sviluppatori inesperti che erano abituati a scrivere tale codice in altri linguaggi.
cessatore
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.