Sprawdzanie, czy wszystkie elementy na liście są unikalne


104

Jaki jest najlepszy sposób (najlepiej jak w konwencjonalny sposób) sprawdzenia, czy wszystkie elementy listy są unikalne?

Moje obecne podejście przy użyciu a Counterto:

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

Czy mogę zrobić lepiej?

Odpowiedzi:


164

Nie jest to najbardziej wydajne, ale proste i zwięzłe:

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

Prawdopodobnie nie zrobi to dużej różnicy w przypadku krótkich list.


To też robię. Prawdopodobnie nie jest skuteczny w przypadku dużych list.
tkerwin

Niekoniecznie spowoduje to wykonanie treści warunku, jeśli lista zawiera powtarzające się elementy (w przykładzie „# zrób coś”).
yan

2
W porządku, dobre rozwiązanie. Obsługuję zaledwie <500 elementów, więc to powinno zrobić to, co chcę.
user225312

4
Dla tych, którzy martwią się wydajnością z długimi listami, jest to skuteczne w przypadku długich list, które są faktycznie unikalne (gdzie wszystkie elementy wymagają sprawdzenia). Rozwiązania wczesnego wyjścia trwają dłużej (około 2x dłużej w moich testach) w przypadku faktycznie unikalnych list. Więc ... jeśli oczekujesz, że większość twoich list będzie unikalna, użyj tego prostego rozwiązania do sprawdzania długości zestawu. Jeśli oczekujesz, że większość twoich list NIE będzie unikatowa, użyj rozwiązania umożliwiającego wczesne wyjście. To, którego użyć, zależy od twojego przypadku użycia.
Russ

Ta odpowiedź jest miła. Jednak bądźmy tutaj ostrożni: len(x) > len(set(x))jest True, gdy elementy w xNIE są unikalne. Tytuł tego pytania brzmi dokładnie odwrotnie: „Sprawdzanie, czy wszystkie elementy na liście unikalne”
WhyWhat

96

Oto dwuliniowiec, który również wykona wczesne wyjście:

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

Jeśli elementy x nie są hashowane, będziesz musiał skorzystać z listy dla 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 czysto i nie przegląda całej listy, jeśli nie jest to potrzebne.
Kos

@ paul-mcguire: Czy chciałbyś licencjonować ten fragment kodu na licencji zgodnej z Apache 2.0 (np. Apache 2, 2/3-line BSD, MIT, X11, zlib). Chciałbym go użyć w projekcie Apache 2.0, którego używam, a ponieważ warunki licencyjne StackOverflow są fubar , proszę Cię jako oryginalnego autora.
Ryan Parman

Wydałem inny kod na licencji MIT, więc działa dla mnie dla tego fragmentu. Coś specjalnego, co muszę zrobić?
PaulMcG

21

Może to być rozwiązanie umożliwiające wczesne wyjście

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

Jednak w małych przypadkach lub jeśli wcześniejsze wyjście nie jest częstym przypadkiem, spodziewałbym się len(x) != len(set(x)), że będzie to najszybsza metoda.


Przyjąłem drugą odpowiedź, ponieważ nie szukałem specjalnie optymalizacji.
user225312

2
Możesz to skrócić, wstawiając następującą linię po s = set()...return not any(s.add(x) if x not in s else True for x in g)
Andrew Clark

Czy możesz wyjaśnić, dlaczego spodziewasz len(x) != len(set(x))się, że będziesz szybszy, jeśli wczesne wychodzenie nie jest powszechne? Czy obie operacje nie są O (len (x)) ? (gdzie xjest oryginalna lista)
Chris Redford

Och, widzę: twoja metoda nie jest O (len (x)), ponieważ sprawdzasz if x in swewnątrz pętli O (len (x)) .
Chris Redford

15

dla prędkości:

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

12

Co powiesz na dodanie wszystkich wpisów do zestawu i sprawdzenie jego długości?

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

1
Odpowiedział sekundę po yan, ouch. Krótkie i słodkie. Jakieś powody, by nie skorzystać z tego rozwiązania?
jasonleonhard

Nie wszystkie sekwencje (zwłaszcza generatory) obsługują len().
PaulMcG

9

Alternatywnie do pliku setmożesz użyć pliku dict.

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

9
Nie widzę żadnej korzyści z używania dyktowania nad zestawem. Wydaje się, że niepotrzebnie komplikuje sprawę.
metazoarous

3

Całkowicie inne podejście, używając sortowania i grupowania:

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

Wymaga sortowania, ale kończy na pierwszej powtórzonej wartości.


haszowanie jest szybsze niż sortowanie
IceArdor,

Przyszedłem tutaj, aby opublikować to samo rozwiązanie, używając groupbyi znalazłem tę odpowiedź. Uważam to za najbardziej eleganckie, ponieważ jest to pojedyncze wyrażenie i działa z wbudowanymi narzędziami bez konieczności stosowania dodatkowych zmiennych lub instrukcji pętli.
Lars Blumberg

1
Jeśli lista zawiera dowolne obiekty, których nie można sortować, możesz użyć id()funkcji do ich sortowania, ponieważ jest to warunek wstępny groupby()pracy:groupby(sorted(seq), key=id)
Lars Blumberg

3

Oto rekurencyjna wersja O (N 2 ) dla zabawy:

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

2

Oto rekurencyjna funkcja wczesnego wyjścia:

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)    

Jest dla mnie wystarczająco szybki bez używania dziwnych (wolnych) konwersji, mając jednocześnie podejście funkcjonalne.


1
H in Tprzeprowadza wyszukiwanie liniowe i T = L[1:]kopiuje pociętą część listy, więc będzie to znacznie wolniejsze niż inne rozwiązania, które były sugerowane na dużych listach. Myślę, że jest to O (N ^ 2), podczas gdy większość pozostałych to O (N) (zbiory) lub O (N log N) (rozwiązania oparte na sortowaniu).
Blckknght

1

Co powiesz na to

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

0

Możesz użyć składni Yana (len (x)> len (set (x))), ale zamiast set (x) zdefiniuj funkcję:

 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

i wykonaj len (x)> len (f5 (x)). To będzie szybkie i jednocześnie pozwoli zachować porządek.

Kod tam zaczerpnięto z: http://www.peterbe.com/plog/uniqifiers-benchmark


ta funkcja f5 będzie wolniejsza niż użycie zestawu, który jest lepiej zoptymalizowany pod kątem szybkości. Ten kod zaczyna się zepsuć, gdy lista staje się naprawdę duża z powodu kosztownej operacji „dołączania”. w przypadku dużych list, takich jak x = range(1000000) + range(1000000)uruchomienie set (x) jest szybsze niż f5 (x). Zamówienie nie jest wymagane w pytaniu, ale nawet posortowane uruchomienie (set (x)) jest nadal szybsze niż f5 (x)
OkezieE

0

Używając podobnego podejścia w ramce danych Pandas, aby sprawdzić, czy zawartość kolumny zawiera unikalne wartości:

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

Dla mnie jest to natychmiastowe dla zmiennej int w ramce dat zawierającej ponad milion wierszy.


0

wszystkie powyższe odpowiedzi są dobre, ale wolę używać all_uniqueprzykładu z 30 sekund Pythona

musisz użyć set()na podanej liście, aby usunąć duplikaty, porównać jego długość z długością listy.

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

zwraca, Truejeśli wszystkie wartości z płaskiej listy są unique, w Falseprzeciwnym razie

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

-3

Dla początkujących:

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

Podoba mi się ta odpowiedź, tylko dlatego, że dość dobrze pokazuje, jakiego kodu nie musisz pisać, używając zestawu. Nie nazwałbym tego „dla początkujących”, ponieważ uważam, że początkujący powinni nauczyć się tego robić we właściwy sposób od samego początku; ale spotkałem niedoświadczonych programistów, którzy byli przyzwyczajeni do pisania takiego kodu w innych językach.
cessor
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.