Comprobando si todos los elementos de una lista son únicos

104

¿Cuál es la mejor forma (mejor que en la forma convencional) de comprobar si todos los elementos de una lista son únicos?

Mi enfoque actual usando a Counteres:

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

¿Puedo hacerlo mejor?

user225312
fuente

Respuestas:

164

No es el más eficiente, pero sencillo y conciso:

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

Probablemente no haga mucha diferencia para las listas cortas.

yan
fuente
Esto es lo que yo también hago. Aunque probablemente no sea eficiente para listas grandes.
tkerwin
No necesariamente, eso ejecutará el cuerpo del condicional si la lista tiene elementos repetidos (el "#hacer algo" en el ejemplo).
yan
2
Bastante buena solución. Estoy manejando apenas <500 elementos, así que esto debería hacer lo que quiero.
user225312
4
Para aquellos preocupados por la eficiencia con listas largas, esto es eficiente para listas largas que son realmente únicas (donde todos los elementos necesitan verificación). Las soluciones de salida anticipada toman más tiempo (aproximadamente 2 veces más en mis pruebas) para listas realmente únicas. Entonces ... si espera que la mayoría de sus listas sean únicas, use esta sencilla solución de verificación de longitud de conjuntos. Si espera que la mayoría de sus listas NO sean únicas, utilice una solución de salida anticipada. Cuál usar depende de su caso de uso.
Russ
Esta respuesta es buena. Sin embargo, tengamos cuidado aquí: len(x) > len(set(x))es Verdadero cuando los elementos xNO son únicos. El título de esta pregunta pregunta exactamente lo contrario: "Comprobar si todos los elementos de una lista son únicos"
qué Qué
96

Aquí hay una línea de dos líneas que también hará una salida anticipada:

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

Si los elementos de x no se pueden cifrar, tendrá que recurrir al uso de una lista para 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
PaulMcG
fuente
5
+1 limpia y no recorre toda la lista si no es necesario.
Kos
@ paul-mcguire: ¿Estaría dispuesto a licenciar este fragmento de código bajo una licencia compatible con Apache 2.0 (por ejemplo, Apache 2, BSD de 2/3 líneas, MIT, X11, zlib). Me gustaría usarlo en un proyecto de Apache 2.0 que estoy usando, y como los términos de licencia de StackOverflow son fubar , le pregunto como autor original.
Ryan Parman
He publicado otro código usando la licencia MIT, así que me funciona para este fragmento. ¿Algo especial que deba hacer?
PaulMcG
21

Una solución de salida temprana podría ser

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

sin embargo, para casos pequeños o si la salida anticipada no es el caso común, esperaría que len(x) != len(set(x))sea ​​el método más rápido.

6502
fuente
Acepté la otra respuesta porque no buscaba una optimización en particular.
user225312
2
Puede acortarlo poniendo la siguiente línea después de s = set()...return not any(s.add(x) if x not in s else True for x in g)
Andrew Clark
¿Podría explicar por qué esperaría len(x) != len(set(x))ser más rápido que esto si la salida anticipada no es común? ¿No son ambas operaciones O (len (x)) ? (donde xestá la lista original)
Chris Redford
Oh, ya veo: tu método no es O (len (x)) porque verificas if x in sdentro del ciclo O (len (x)) .
Chris Redford
15

por velocidad:

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

¿Qué tal agregar todas las entradas a un conjunto y verificar su longitud?

len(set(x)) == len(x)
Grzegorz Oledzki
fuente
1
Respondió un segundo después de yan, ay. Corto y dulce. ¿Alguna razón por la que no utilizar esta solución?
Jasonleonhard
No todas las secuencias (especialmente los generadores) son compatibles len().
PaulMcG
9

Alternativa a a set, puede utilizar a dict.

len({}.fromkeys(x)) == len(x)
Tugrul Ates
fuente
9
No veo absolutamente ninguna ventaja en usar un dictado sobre un conjunto. Parece complicar innecesariamente las cosas.
metasoarous
3

Otro enfoque completamente, usando sorted y groupby:

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

Requiere una ordenación, pero sale en el primer valor repetido.

PaulMcG
fuente
hash es más rápido que clasificar
IceArdor
Vine aquí para publicar la misma solución usando groupbyy encontré esta respuesta. Encuentro esto de lo más elegante, ya que es una expresión única y funciona con las herramientas integradas sin requerir ninguna variable adicional o declaración de bucle.
Lars Blumberg
1
Si su lista contiene objetos arbitrarios que no se pueden id()ordenar , puede usar la función para ordenarlos, ya que este es un requisito previo para groupby()que funcione:groupby(sorted(seq), key=id)
Lars Blumberg
3

Aquí hay una versión recursiva O (N 2 ) por diversión:

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

Aquí hay una función de salida temprana recursiva:

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)    

Es lo suficientemente rápido para mí sin usar conversiones extrañas (lentas) mientras tengo un enfoque de estilo funcional.

mhourdakis
fuente
1
H in Trealiza una búsqueda lineal y T = L[1:]copia la parte cortada de la lista, por lo que esto será mucho más lento que las otras soluciones que se han sugerido en listas grandes. Creo que es O (N ^ 2), mientras que la mayoría de los demás son O (N) (conjuntos) u O (N log N) (soluciones basadas en clasificación).
Blckknght
1

Qué tal esto

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

Puede usar la sintaxis de Yan (len (x)> len (set (x))), pero en lugar de set (x), defina una función:

 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

y haz len (x)> len (f5 (x)). Esto será rápido y también preservará el orden.

El código se toma de: http://www.peterbe.com/plog/uniqifiers-benchmark

canisrufus
fuente
esta función f5 será más lenta que usar un conjunto que está mejor optimizado para la velocidad. Este código comienza a romperse cuando la lista se vuelve muy grande debido a la costosa operación de "agregar". con listas grandes como x = range(1000000) + range(1000000), ejecutar set (x) es más rápido que f5 (x). El orden no es un requisito en la pregunta, pero incluso ejecutar ordenado (conjunto (x)) es aún más rápido que f5 (x)
OkezieE
0

Usando un enfoque similar en un marco de datos de Pandas para probar si el contenido de una columna contiene valores únicos:

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

Para mí, esto es instantáneo en una variable int en un marco de fecha que contiene más de un millón de filas.

usuario1718097
fuente
0

todas las respuestas anteriores son buenas pero prefiero usar all_unique ejemplo de 30 segundos de Python

necesitas usar set() en la lista dada para eliminar duplicados, compare su longitud con la longitud de la lista.

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

devuelve Truesi todos los valores en una lista plana son unique, de lo Falsecontrario

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

Para principiantes:

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
DonChriss
fuente
Me gusta esta respuesta, solo porque muestra bastante bien qué código no tiene que escribir cuando usa un conjunto. No lo etiquetaría como "para principiantes", ya que creo que los principiantes deberían aprender a hacerlo de la manera correcta desde el principio; pero conocí a algunos desarrolladores sin experiencia que estaban acostumbrados a escribir ese código en otros lenguajes.
cesor