Verificar se todos os elementos de uma lista são únicos


104

Qual é a melhor maneira (melhor como da maneira convencional) de verificar se todos os elementos de uma lista são únicos?

Minha abordagem atual usando um 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 fazer melhor?

Respostas:


164

Não é o mais eficiente, mas direto e conciso:

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

Provavelmente não fará muita diferença para listas curtas.


Isso é o que eu também faço. Provavelmente não é eficiente para listas grandes.
tkerwin

Não necessariamente, isso executará o corpo da condicional se a lista tiver elementos repetidos (o "#fazer algo" no exemplo).
yan,

2
Muito justo, boa solução. Estou lidando com menos de 500 elementos, então isso deve fazer o que eu quero.
user225312

4
Para aqueles preocupados com a eficiência com listas longas, isso é eficiente para listas longas que são realmente únicas (onde todos os elementos precisam ser verificados). As soluções de saída antecipada demoram mais (cerca de 2x mais tempo em meus testes) para listas realmente exclusivas. Então ... se você espera que a maioria de suas listas seja única, use esta solução simples de verificação de comprimento de conjunto. Se você espera que a maioria de suas listas NÃO seja exclusiva, use uma solução de saída antecipada. Qual usar depende do seu caso de uso.
Russ,

Essa resposta é boa. No entanto, vamos ter cuidado aqui: len(x) > len(set(x))é True quando os elementos em xNÃO são exclusivos. O título desta pergunta pergunta exatamente o oposto: "Verificando se todos os elementos em uma lista são únicos"
WhyWhat

96

Aqui está uma linha dupla que também fará a saída antecipada:

>>> 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 os elementos de x não tiverem hash, você terá que recorrer ao uso de uma 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

5
+1 limpa e não itera em toda a lista se não for necessário.
Kos

@ paul-mcguire: Você gostaria de licenciar este trecho de código sob uma licença compatível com Apache 2.0 (por exemplo, Apache 2, BSD de 2/3 linhas, MIT, X11, zlib). Gostaria de usá-lo em um projeto Apache 2.0 que estou usando e, como os termos de licença do StackOverflow são fubar , estou perguntando a você como autor original.
Ryan Parman

Eu coloquei outro código usando a licença MIT, então isso funciona para mim para este trecho. Preciso fazer algo especial?
PaulMcG

21

Uma solução de saída antecipada pode ser

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

no entanto, para casos pequenos ou se a saída antecipada não for o caso comum, eu esperaria len(x) != len(set(x))ser o método mais rápido.


Aceitei a outra resposta porque não estava particularmente procurando por otimização.
user225312

2
Você pode encurtar isso colocando a seguinte linha após s = set()...return not any(s.add(x) if x not in s else True for x in g)
Andrew Clark

Você poderia explicar por que esperaria len(x) != len(set(x))ser mais rápido do que isso se a saída antecipada não é comum? Ambas as operações não são O (len (x)) ? (onde xestá a lista original)
Chris Redford

Ah, entendo: seu método não é O (len (x)) porque você verifica if x in sdentro do O (len (x)) o loop.
Chris Redford

15

para velocidade:

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

12

Que tal adicionar todas as entradas a um conjunto e verificar seu comprimento?

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

1
Respondeu um segundo depois de yan, ai. Curto e grosso. Alguma razão para não usar esta solução?
jasonleonhard

Nem todas as sequências (especialmente geradores) suportam len().
PaulMcG

9

Alternativa a a set, você pode usar a dict.

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

9
Não vejo nenhuma vantagem em usar um dicionário sobre um conjunto. Parece complicar as coisas desnecessariamente.
metasoarous de

3

Outra abordagem totalmente, usando classificado e agrupado:

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

Ele exige uma classificação, mas sai no primeiro valor repetido.


hashing é mais rápido do que classificação
IceArdor

Vim aqui postar a mesma solução usando groupbye achei essa resposta. Acho isso muito elegante, pois é uma expressão única e funciona com as ferramentas integradas sem exigir qualquer variável extra ou instrução de loop.
Lars Blumberg

1
Se a sua lista contém objetos arbitrários que não são classificáveis, você pode usar a id()função para classificá-los, pois este é um pré-requisito para groupby()funcionar:groupby(sorted(seq), key=id)
Lars Blumberg

3

Aqui está uma versão recursiva O (N 2 ) para diversão:

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

2

Aqui está uma função de saída precoce 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)    

É rápido o suficiente para mim, sem usar conversões estranhas (lentas) e, ao mesmo tempo, ter uma abordagem de estilo funcional.


1
H in Tfaz uma pesquisa linear e T = L[1:]copia a parte fatiada da lista, então isso será muito mais lento do que as outras soluções que foram sugeridas em listas grandes. É O (N ^ 2), eu acho, enquanto a maioria dos outros são O (N) (conjuntos) ou O (N log N) (soluções baseadas em classificação).
Blckknght

1

Que tal agora

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

0

Você pode usar a sintaxe de Yan (len (x)> len (set (x))), mas em vez de set (x), defina uma função:

 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 faça len (x)> len (f5 (x)). Isso será rápido e também preservará a ordem.

O código foi retirado de: http://www.peterbe.com/plog/uniqifiers-benchmark


esta função f5 será mais lenta do que usar set, que é melhor otimizado para velocidade. Este código começa a quebrar quando a lista fica muito grande devido à cara operação "anexar". com listas grandes como x = range(1000000) + range(1000000), executar set (x) é mais rápido do que f5 (x). A ordem não é um requisito na questão, mas mesmo executando ordenado (conjunto (x)) ainda é mais rápido do que f5 (x)
OkezieE

0

Usando uma abordagem semelhante em um dataframe Pandas para testar se o conteúdo de uma coluna contém valores únicos:

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

Para mim, isso é instantâneo em uma variável int em um quadro de datas contendo mais de um milhão de linhas.


0

todas as respostas acima são boas, mas eu prefiro usar o all_uniqueexemplo de 30 segundos de python

você precisa usar set()na lista fornecida para remover duplicatas, compare seu comprimento com o comprimento da lista.

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

ele retorna Truese todos os valores em uma lista plana são unique, Falsecaso contrário

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

-3

Para iniciantes:

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

Gosto dessa resposta, porque mostra muito bem qual código você não precisa escrever ao usar um conjunto. Eu não o chamaria de "para iniciantes", pois acredito que os iniciantes devem aprender a fazer da maneira certa desde o início; mas conheci alguns desenvolvedores inexperientes que estavam acostumados a escrever esse código em outras linguagens.
cessor
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.