Jak znaleźć duplikaty na liście i utworzyć z nimi kolejną listę?

437

Jak znaleźć duplikaty na liście Python i utworzyć kolejną listę duplikatów? Lista zawiera tylko liczby całkowite.

MFB
źródło
1
czy chcesz duplikaty raz, czy za każdym razem, gdy zostanie to ponownie wyświetlone?
moooeeeep
Myślę, że odpowiedź na to pytanie jest znacznie bardziej efektywna. stackoverflow.com/a/642919/1748045 skrzyżowanie jest wbudowaną metodą zestawu i powinno robić dokładnie to, co jest wymagane
Tom Smith

Odpowiedzi:

545

Aby usunąć duplikaty, użyj set(a). Aby wydrukować duplikaty, coś takiego:

a = [1,2,3,2,1,5,6,5,5,5]

import collections
print([item for item, count in collections.Counter(a).items() if count > 1])

## [1, 2, 5]

Pamiętaj, że Counternie jest to szczególnie wydajne ( czasy ) i prawdopodobnie przesada tutaj. setbędzie działać lepiej. Ten kod oblicza listę unikalnych elementów w kolejności źródłowej:

seen = set()
uniq = []
for x in a:
    if x not in seen:
        uniq.append(x)
        seen.add(x)

lub bardziej zwięźle:

seen = set()
uniq = [x for x in a if x not in seen and not seen.add(x)]    

Nie polecam tego drugiego stylu, ponieważ nie jest oczywiste, co not seen.add(x)się dzieje ( add()metoda set zawsze powraca None, stąd potrzeba not).

Aby obliczyć listę zduplikowanych elementów bez bibliotek:

seen = {}
dupes = []

for x in a:
    if x not in seen:
        seen[x] = 1
    else:
        if seen[x] == 1:
            dupes.append(x)
        seen[x] += 1

Jeśli elementy listy nie są haszowalne, nie możesz używać zestawów / dykt i musisz uciekać się do kwadratowego rozwiązania czasowego (porównaj każdy z nich). Na przykład:

a = [[1], [2], [3], [1], [5], [3]]

no_dupes = [x for n, x in enumerate(a) if x not in a[:n]]
print no_dupes # [[1], [2], [3], [5]]

dupes = [x for n, x in enumerate(a) if x in a[:n]]
print dupes # [[1], [3]]
Georg
źródło
2
@eric: Myślę, że tak O(n), ponieważ iteruje listę tylko raz i ustawia wyszukiwania O(1).
georg
3
@ Hugo, aby zobaczyć listę duplikatów, wystarczy utworzyć nową listę o nazwie dup i dodać instrukcję else. Na przykład:dup = [] else: dup.append(x)
Chris Nielsen
4
@oxeimon: prawdopodobnie masz to, ale drukujesz w nawiasie python 3 w nawiasach drukowanychprint()
Moberg
4
konwertując odpowiedź dla set (), aby uzyskać tylko duplikaty. seen = set()następniedupe = set(x for x in a if x in seen or seen.add(x))
Ta946,
2
W przypadku Pythona 3.x: print ([pozycja dla elementu, policz w kolekcjach. Licznik (a) .items () jeśli liczba> 1])
kibitzforu
327
>>> l = [1,2,3,4,4,5,5,6,1]
>>> set([x for x in l if l.count(x) > 1])
set([1, 4, 5])
Ritesh Kumar
źródło
2
Czy jest jakiś powód, dla którego używasz rozumienia listy zamiast generatora?
64
Rzeczywiście, proste rozwiązanie, ale złożoność jest podniesiona do kwadratu, ponieważ każda funkcja count () ponownie analizuje listę, więc nie używaj jej w przypadku dużych list.
danuker,
4
@JohnJ, sortowanie bąbelkowe jest również proste i działa. To nie znaczy, że powinniśmy go użyć!
John La Rooy,
@JohnLaRooy W rzeczywistości oznacza to, że nie powinniśmy go używać, ponieważ prawie zawsze istnieje bardziej wydajny (i prostszy) sposób sortowania.
lostsoul29
1
@watsonic: Twój „prosty przełącznik” nie zmniejsza złożoności czasowej z kwadratowej do kwadratowej w ogólnym przypadku. Zastąpienie lgo set(l)tylko zmniejsza złożoność czasu najgorszego przypadku, a zatem nie robi nic, aby rozwiązać problemy związane z wydajnością na większą skalę dzięki tej odpowiedzi. To chyba nie było takie proste. Krótko mówiąc, nie rób tego.
Cecil Curry,
82

Nie potrzebujesz liczenia, tylko czy przedmiot był wcześniej widziany. Dostosowałem tę odpowiedź do tego problemu:

def list_duplicates(seq):
  seen = set()
  seen_add = seen.add
  # adds all elements it doesn't know yet to seen and all other to seen_twice
  seen_twice = set( x for x in seq if x in seen or seen_add(x) )
  # turn the set into a list (as requested)
  return list( seen_twice )

a = [1,2,3,2,1,5,6,5,5,5]
list_duplicates(a) # yields [1, 2, 5]

Na wszelki wypadek liczy się prędkość:

# file: test.py
import collections

def thg435(l):
    return [x for x, y in collections.Counter(l).items() if y > 1]

def moooeeeep(l):
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    seen_twice = set( x for x in l if x in seen or seen_add(x) )
    # turn the set into a list (as requested)
    return list( seen_twice )

def RiteshKumar(l):
    return list(set([x for x in l if l.count(x) > 1]))

def JohnLaRooy(L):
    seen = set()
    seen2 = set()
    seen_add = seen.add
    seen2_add = seen2.add
    for item in L:
        if item in seen:
            seen2_add(item)
        else:
            seen_add(item)
    return list(seen2)

l = [1,2,3,2,1,5,6,5,5,5]*100

Oto wyniki: (dobra robota @JohnLaRooy!)

$ python -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
10000 loops, best of 3: 74.6 usec per loop
$ python -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
10000 loops, best of 3: 91.3 usec per loop
$ python -mtimeit -s 'import test' 'test.thg435(test.l)'
1000 loops, best of 3: 266 usec per loop
$ python -mtimeit -s 'import test' 'test.RiteshKumar(test.l)'
100 loops, best of 3: 8.35 msec per loop

Co ciekawe, oprócz samego taktowania, również ranking zmienia się nieznacznie, gdy używany jest pypy. Co najciekawsze, podejście oparte na licznikach czerpie ogromne korzyści z optymalizacji pypy, podczas gdy podejście buforowania metod, które zasugerowałem, wydaje się prawie nie mieć żadnego efektu.

$ pypy -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
100000 loops, best of 3: 17.8 usec per loop
$ pypy -mtimeit -s 'import test' 'test.thg435(test.l)'
10000 loops, best of 3: 23 usec per loop
$ pypy -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
10000 loops, best of 3: 39.3 usec per loop

Widocznie ten efekt jest związany z „duplikacją” danych wejściowych. Ustawiłem l = [random.randrange(1000000) for i in xrange(10000)]i otrzymałem te wyniki:

$ pypy -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
1000 loops, best of 3: 495 usec per loop
$ pypy -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
1000 loops, best of 3: 499 usec per loop
$ pypy -mtimeit -s 'import test' 'test.thg435(test.l)'
1000 loops, best of 3: 1.68 msec per loop
moooeeeep
źródło
6
Po prostu ciekawy - jaki jest cel seen_add = seen.add tutaj?
Rob
3
@Rob W ten sposób po prostu wywołujesz funkcję, którą kiedyś sprawdziłeś. W przeciwnym razie należy wyszukać (zapytanie słownikowe) funkcję członka za addkażdym razem, gdy konieczne będzie wstawienie.
moooeeeep
sprawdziłem na podstawie własnych danych i% time Ipythona, podczas gdy twoja metoda wygląda najszybciej w teście, ALE: „Najwolniejszy przebieg trwał 4,34 razy dłużej niż najszybszy. Może to oznaczać, że buforowany jest wynik pośredni”
Joop
1
@meeeeeep, dodałem do skryptu kolejną wersję, abyś mógł spróbować :) Spróbuj także, pypyjeśli masz go pod ręką i idziesz na szybkość.
John La Rooy,
@JohnLaRooy Dobra poprawa wydajności! Co ciekawe, kiedy użyłem pypy do oceny wyników, podejście oparte na licznikach znacznie się poprawia.
moooeeeep,
42

Możesz użyć iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

>>> list(duplicates([1,1,2,1,2,3,4,2]))
[1, 1, 2, 2]

lub jeśli chcesz tylko jeden z każdego duplikatu, można to połączyć z iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen

>>> list(unique_everseen(duplicates([1,1,2,1,2,3,4,2])))
[1, 2]

Może także obsługiwać elementy niewymagalne (jednak kosztem wydajności):

>>> list(duplicates([[1], [2], [1], [3], [1]]))
[[1], [1]]

>>> list(unique_everseen(duplicates([[1], [2], [1], [3], [1]])))
[[1]]

Jest to coś, z czym poradzi sobie tylko kilka innych podejść.

Benchmarki

Zrobiłem szybki test porównawczy zawierający większość (ale nie wszystkie) wymienionych tu podejść.

Pierwszy test porównawczy obejmował tylko niewielki zakres długości list, ponieważ niektóre podejścia mają O(n**2)zachowanie.

Na wykresach oś y reprezentuje czas, więc niższa wartość oznacza lepiej. Jest także wykreślany log-log, dzięki czemu można lepiej wizualizować szeroki zakres wartości:

wprowadź opis zdjęcia tutaj

Usuwając O(n**2)podejścia, wykonałem kolejny test porównawczy do pół miliona elementów na liście:

wprowadź opis zdjęcia tutaj

Jak widać, iteration_utilities.duplicatespodejście jest szybsze niż w przypadku innych podejść, a nawet tworzenie łańcuchów unique_everseen(duplicates(...))było szybsze lub równie szybkie niż w przypadku innych podejść.

Jedną dodatkową interesującą rzeczą, którą należy tutaj zauważyć, jest to, że podejścia do pand są bardzo wolne dla małych list, ale mogą z łatwością konkurować o dłuższe listy.

Jednak ponieważ te testy porównawcze pokazują, że większość podejść działa mniej więcej równo, więc nie ma znaczenia, które z nich zostanie użyte (z wyjątkiem 3, które miały O(n**2)czas działania).

from iteration_utilities import duplicates, unique_everseen
from collections import Counter
import pandas as pd
import itertools

def georg_counter(it):
    return [item for item, count in Counter(it).items() if count > 1]

def georg_set(it):
    seen = set()
    uniq = []
    for x in it:
        if x not in seen:
            uniq.append(x)
            seen.add(x)

def georg_set2(it):
    seen = set()
    return [x for x in it if x not in seen and not seen.add(x)]   

def georg_set3(it):
    seen = {}
    dupes = []

    for x in it:
        if x not in seen:
            seen[x] = 1
        else:
            if seen[x] == 1:
                dupes.append(x)
            seen[x] += 1

def RiteshKumar_count(l):
    return set([x for x in l if l.count(x) > 1])

def moooeeeep(seq):
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    seen_twice = set( x for x in seq if x in seen or seen_add(x) )
    # turn the set into a list (as requested)
    return list( seen_twice )

def F1Rumors_implementation(c):
    a, b = itertools.tee(sorted(c))
    next(b, None)
    r = None
    for k, g in zip(a, b):
        if k != g: continue
        if k != r:
            yield k
            r = k

def F1Rumors(c):
    return list(F1Rumors_implementation(c))

def Edward(a):
    d = {}
    for elem in a:
        if elem in d:
            d[elem] += 1
        else:
            d[elem] = 1
    return [x for x, y in d.items() if y > 1]

def wordsmith(a):
    return pd.Series(a)[pd.Series(a).duplicated()].values

def NikhilPrabhu(li):
    li = li.copy()
    for x in set(li):
        li.remove(x)

    return list(set(li))

def firelynx(a):
    vc = pd.Series(a).value_counts()
    return vc[vc > 1].index.tolist()

def HenryDev(myList):
    newList = set()

    for i in myList:
        if myList.count(i) >= 2:
            newList.add(i)

    return list(newList)

def yota(number_lst):
    seen_set = set()
    duplicate_set = set(x for x in number_lst if x in seen_set or seen_set.add(x))
    return seen_set - duplicate_set

def IgorVishnevskiy(l):
    s=set(l)
    d=[]
    for x in l:
        if x in s:
            s.remove(x)
        else:
            d.append(x)
    return d

def it_duplicates(l):
    return list(duplicates(l))

def it_unique_duplicates(l):
    return list(unique_everseen(duplicates(l)))

Benchmark 1

from simple_benchmark import benchmark
import random

funcs = [
    georg_counter, georg_set, georg_set2, georg_set3, RiteshKumar_count, moooeeeep, 
    F1Rumors, Edward, wordsmith, NikhilPrabhu, firelynx,
    HenryDev, yota, IgorVishnevskiy, it_duplicates, it_unique_duplicates
]

args = {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(2, 12)}

b = benchmark(funcs, args, 'list size')

b.plot()

Benchmark 2

funcs = [
    georg_counter, georg_set, georg_set2, georg_set3, moooeeeep, 
    F1Rumors, Edward, wordsmith, firelynx,
    yota, IgorVishnevskiy, it_duplicates, it_unique_duplicates
]

args = {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(2, 20)}

b = benchmark(funcs, args, 'list size')
b.plot()

Zrzeczenie się

1 Jest to z biblioteki trzeciej mam napisane: iteration_utilities.

MSeifert
źródło
1
Mam zamiar wystawić tu szyję i zasugerować, że napisanie specjalnie zaprojektowanej biblioteki do pracy w C zamiast Python prawdopodobnie nie jest duchem szukanej odpowiedzi - ale jest to uzasadnione podejście! Podoba mi się szerokość odpowiedzi i graficzne wyświetlanie wyników - bardzo miło jest widzieć, że są zbieżne, zastanawiam się, czy kiedykolwiek się krzyżują, gdy nakłady rosną dalej! Pytanie: jaki jest wynik z listami najczęściej posortowanymi, a nie listami całkowicie losowymi?
F1Rumors
30

Natknąłem się na to pytanie, patrząc na coś powiązanego - i zastanawiam się, dlaczego nikt nie zaoferował rozwiązania opartego na generatorze? Rozwiązaniem tego problemu byłoby:

>>> print list(getDupes_9([1,2,3,2,1,5,6,5,5,5]))
[1, 2, 5]

Martwiłem się o skalowalność, dlatego przetestowałem kilka podejść, w tym naiwne elementy, które działają dobrze na małych listach, ale skalują się strasznie, gdy listy stają się większe (uwaga - lepiej byłoby użyć timeit, ale jest to przykładowe).

Do porównania dołączyłem @moooeeeep (jest imponująco szybki: najszybszy, jeśli lista wejściowa jest całkowicie losowa) i podejście itertools, które jest jeszcze szybsze dla najczęściej posortowanych list ... Teraz obejmuje podejście pandy z @firelynx - wolno, ale nie okropnie i proste. Uwaga - podejście sort / tee / zip jest konsekwentnie najszybsze na mojej maszynie w przypadku dużych, najczęściej uporządkowanych list, moooeeeep jest najszybszy w przypadku list losowych, ale przebieg może się różnić.

Zalety

  • bardzo szybki prosty do przetestowania pod kątem „dowolnych” duplikatów przy użyciu tego samego kodu

Założenia

  • Duplikaty należy zgłaszać tylko raz
  • Zduplikowane zamówienie nie musi być zachowane
  • Duplikat może znajdować się w dowolnym miejscu na liście

Najszybsze rozwiązanie, 1 milion wpisów:

def getDupes(c):
        '''sort/tee/izip'''
        a, b = itertools.tee(sorted(c))
        next(b, None)
        r = None
        for k, g in itertools.izip(a, b):
            if k != g: continue
            if k != r:
                yield k
                r = k

Podejścia przetestowane

import itertools
import time
import random

def getDupes_1(c):
    '''naive'''
    for i in xrange(0, len(c)):
        if c[i] in c[:i]:
            yield c[i]

def getDupes_2(c):
    '''set len change'''
    s = set()
    for i in c:
        l = len(s)
        s.add(i)
        if len(s) == l:
            yield i

def getDupes_3(c):
    '''in dict'''
    d = {}
    for i in c:
        if i in d:
            if d[i]:
                yield i
                d[i] = False
        else:
            d[i] = True

def getDupes_4(c):
    '''in set'''
    s,r = set(),set()
    for i in c:
        if i not in s:
            s.add(i)
        elif i not in r:
            r.add(i)
            yield i

def getDupes_5(c):
    '''sort/adjacent'''
    c = sorted(c)
    r = None
    for i in xrange(1, len(c)):
        if c[i] == c[i - 1]:
            if c[i] != r:
                yield c[i]
                r = c[i]

def getDupes_6(c):
    '''sort/groupby'''
    def multiple(x):
        try:
            x.next()
            x.next()
            return True
        except:
            return False
    for k, g in itertools.ifilter(lambda x: multiple(x[1]), itertools.groupby(sorted(c))):
        yield k

def getDupes_7(c):
    '''sort/zip'''
    c = sorted(c)
    r = None
    for k, g in zip(c[:-1],c[1:]):
        if k == g:
            if k != r:
                yield k
                r = k

def getDupes_8(c):
    '''sort/izip'''
    c = sorted(c)
    r = None
    for k, g in itertools.izip(c[:-1],c[1:]):
        if k == g:
            if k != r:
                yield k
                r = k

def getDupes_9(c):
    '''sort/tee/izip'''
    a, b = itertools.tee(sorted(c))
    next(b, None)
    r = None
    for k, g in itertools.izip(a, b):
        if k != g: continue
        if k != r:
            yield k
            r = k

def getDupes_a(l):
    '''moooeeeep'''
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    for x in l:
        if x in seen or seen_add(x):
            yield x

def getDupes_b(x):
    '''iter*/sorted'''
    x = sorted(x)
    def _matches():
        for k,g in itertools.izip(x[:-1],x[1:]):
            if k == g:
                yield k
    for k, n in itertools.groupby(_matches()):
        yield k

def getDupes_c(a):
    '''pandas'''
    import pandas as pd
    vc = pd.Series(a).value_counts()
    i = vc[vc > 1].index
    for _ in i:
        yield _

def hasDupes(fn,c):
    try:
        if fn(c).next(): return True    # Found a dupe
    except StopIteration:
        pass
    return False

def getDupes(fn,c):
    return list(fn(c))

STABLE = True
if STABLE:
    print 'Finding FIRST then ALL duplicates, single dupe of "nth" placed element in 1m element array'
else:
    print 'Finding FIRST then ALL duplicates, single dupe of "n" included in randomised 1m element array'
for location in (50,250000,500000,750000,999999):
    for test in (getDupes_2, getDupes_3, getDupes_4, getDupes_5, getDupes_6,
                 getDupes_8, getDupes_9, getDupes_a, getDupes_b, getDupes_c):
        print 'Test %-15s:%10d - '%(test.__doc__ or test.__name__,location),
        deltas = []
        for FIRST in (True,False):
            for i in xrange(0, 5):
                c = range(0,1000000)
                if STABLE:
                    c[0] = location
                else:
                    c.append(location)
                    random.shuffle(c)
                start = time.time()
                if FIRST:
                    print '.' if location == test(c).next() else '!',
                else:
                    print '.' if [location] == list(test(c)) else '!',
                deltas.append(time.time()-start)
            print ' -- %0.3f  '%(sum(deltas)/len(deltas)),
        print
    print

Wyniki testu „wszystkie duplikaty” były spójne, znajdując „pierwszą” duplikat, a następnie „wszystkie” duplikaty w tej tablicy:

Finding FIRST then ALL duplicates, single dupe of "nth" placed element in 1m element array
Test set len change :    500000 -  . . . . .  -- 0.264   . . . . .  -- 0.402  
Test in dict        :    500000 -  . . . . .  -- 0.163   . . . . .  -- 0.250  
Test in set         :    500000 -  . . . . .  -- 0.163   . . . . .  -- 0.249  
Test sort/adjacent  :    500000 -  . . . . .  -- 0.159   . . . . .  -- 0.229  
Test sort/groupby   :    500000 -  . . . . .  -- 0.860   . . . . .  -- 1.286  
Test sort/izip      :    500000 -  . . . . .  -- 0.165   . . . . .  -- 0.229  
Test sort/tee/izip  :    500000 -  . . . . .  -- 0.145   . . . . .  -- 0.206  *
Test moooeeeep      :    500000 -  . . . . .  -- 0.149   . . . . .  -- 0.232  
Test iter*/sorted   :    500000 -  . . . . .  -- 0.160   . . . . .  -- 0.221  
Test pandas         :    500000 -  . . . . .  -- 0.493   . . . . .  -- 0.499  

Kiedy listy są najpierw tasowane, cena tego rodzaju staje się widoczna - wydajność wyraźnie spada i dominuje podejście @moooeeeep, przy czym podejścia set & dict są podobne, ale wykonawca:

Finding FIRST then ALL duplicates, single dupe of "n" included in randomised 1m element array
Test set len change :    500000 -  . . . . .  -- 0.321   . . . . .  -- 0.473  
Test in dict        :    500000 -  . . . . .  -- 0.285   . . . . .  -- 0.360  
Test in set         :    500000 -  . . . . .  -- 0.309   . . . . .  -- 0.365  
Test sort/adjacent  :    500000 -  . . . . .  -- 0.756   . . . . .  -- 0.823  
Test sort/groupby   :    500000 -  . . . . .  -- 1.459   . . . . .  -- 1.896  
Test sort/izip      :    500000 -  . . . . .  -- 0.786   . . . . .  -- 0.845  
Test sort/tee/izip  :    500000 -  . . . . .  -- 0.743   . . . . .  -- 0.804  
Test moooeeeep      :    500000 -  . . . . .  -- 0.234   . . . . .  -- 0.311  *
Test iter*/sorted   :    500000 -  . . . . .  -- 0.776   . . . . .  -- 0.840  
Test pandas         :    500000 -  . . . . .  -- 0.539   . . . . .  -- 0.540  
F1Rumors
źródło
@moooeeeep - zainteresuj się swoimi opiniami na temat podejścia ifilter / izip / tee.
F1Rumors 17.07.15
1
ta odpowiedź jest niesamowicie dobra. Nie rozumiem, że nie miała więcej punktów na wyjaśnienia i testy, które są bardzo przydatne dla tych, którzy będą jej potrzebować.
dlewin
1
sortowanie Pythona to O (n), gdy tylko jeden element jest niesprawny. Powinieneś random.shuffle(c)to uwzględnić. Ponadto nie mogę zreplikować twoich wyników podczas uruchamiania niezmienionego skryptu (zupełnie inna kolejność), więc może zależy to również od procesora.
John La Rooy,
Dziękuję @ John-La-Rooy, sprytna obserwacja procesora / komputera lokalnego ma wpływ - więc powinienem zmienić pozycję YYMV . Użycie sortowania O (n) było celowe: element powielający jest wstawiany w różnych lokalizacjach, aby zobaczyć wpływ podejścia, jeśli istnieje jedyny duplikat w dobrej (na początku listy) lub złej (na końcu listy) lokalizacji z tymi podejścia. Rozważyłem listę losową - np. Losową. Losową - ale zdecydowałem, że byłoby to rozsądne tylko, gdybym wykonał dużo więcej biegów! Będę musiał zwrócić i przetestować ekwiwalent wielokrotnego uruchomienia / przetasowania i zobaczyć, jaki jest wpływ.
F1Rumors
Zmieniono, aby uwzględnić podejście do pand @firelynx i uruchamiać na liście w pełni przetasowanej oraz na liście posortowanej. To dlatego, że rodzimy timsort używane przez Python jest niegodziwy szybko na najczęściej posortowanych danych (najlepszym wypadku) i tasuje listy są jego najgorszy scenariusz - który wstrząsa wyników.
F1Rumors
13

Korzystanie z pand:

>>> import pandas as pd
>>> a = [1, 2, 1, 3, 3, 3, 0]
>>> pd.Series(a)[pd.Series(a).duplicated()].values
array([1, 3, 3])
kowal
źródło
10

collections.Counter jest nowy w Pythonie 2.7:


Python 2.5.4 (r254:67916, May 31 2010, 15:03:39) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
a = [1,2,3,2,1,5,6,5,5,5]
import collections
print [x for x, y in collections.Counter(a).items() if y > 1]
Type "help", "copyright", "credits" or "license" for more information.
  File "", line 1, in 
AttributeError: 'module' object has no attribute 'Counter'
>>> 

We wcześniejszej wersji można zamiast tego użyć konwencjonalnego słownika:

a = [1,2,3,2,1,5,6,5,5,5]
d = {}
for elem in a:
    if elem in d:
        d[elem] += 1
    else:
        d[elem] = 1

print [x for x, y in d.items() if y > 1]
Edward
źródło
9

Oto schludne i zwięzłe rozwiązanie -

for x in set(li):
    li.remove(x)

li = list(set(li))
Nikhil Prabhu
źródło
Oryginalna lista została jednak utracona. Można to naprawić, kopiując zawartość na inną listę - temp = li [:]
Nikhil Prabhu
3
To dość nieprzyjemne ćwiczenie na dużych listach - usuwanie elementów z list jest dość kosztowne!
F1Rumors
7

Bez konwersji na listę i prawdopodobnie najprostszym sposobem byłoby coś takiego jak poniżej. Może to być przydatne podczas wywiadu, gdy proszą o nieużywanie zestawów

a=[1,2,3,3,3]
dup=[]
for each in a:
  if each not in dup:
    dup.append(each)
print(dup)

======= else, aby uzyskać 2 osobne listy unikalnych wartości i zduplikowanych wartości

a=[1,2,3,3,3]
uniques=[]
dups=[]

for each in a:
  if each not in uniques:
    uniques.append(each)
  else:
    dups.append(each)
print("Unique values are below:")
print(uniques)
print("Duplicate values are below:")
print(dups)
Chetan_Vasudevan
źródło
1
Nie powoduje to utworzenia listy duplikatów (lub oryginalnej listy), skutkuje to listą wszystkich unikalnych elementów (lub oryginalnej listy). Co zrobiłby ktoś po zakończeniu tworzenia listy „dup”?
gameCoder95,
6

Zrobiłbym to z pandami, ponieważ często używam pand

import pandas as pd
a = [1,2,3,3,3,4,5,6,6,7]
vc = pd.Series(a).value_counts()
vc[vc > 1].index.tolist()

Daje

[3,6]

Prawdopodobnie nie jest zbyt wydajny, ale z pewnością zawiera mniej kodu niż wiele innych odpowiedzi, więc pomyślałem, że mógłbym się przyłączyć

firelynx
źródło
3
Zwróć też uwagę, że pandy zawierają wbudowaną funkcję duplikatów pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken,
6

trzeci przykład zaakceptowanej odpowiedzi podaje błędną odpowiedź i nie próbuje duplikatów. Oto poprawna wersja:

number_lst = [1, 1, 2, 3, 5, ...]

seen_set = set()
duplicate_set = set(x for x in number_lst if x in seen_set or seen_set.add(x))
unique_set = seen_set - duplicate_set
yota
źródło
6

A może po prostu przejdziemy przez każdy element na liście, sprawdzając liczbę wystąpień, a następnie dodając je do zestawu, który następnie wydrukuje duplikaty. Mam nadzieję, że to pomoże komuś tam.

myList  = [2 ,4 , 6, 8, 4, 6, 12];
newList = set()

for i in myList:
    if myList.count(i) >= 2:
        newList.add(i)

print(list(newList))
## [4 , 6]
HenryDev
źródło
5

Możemy użyć itertools.groupby, aby znaleźć wszystkie przedmioty, które mają duplikaty:

from itertools import groupby

myList  = [2, 4, 6, 8, 4, 6, 12]
# when the list is sorted, groupby groups by consecutive elements which are similar
for x, y in groupby(sorted(myList)):
    #  list(y) returns all the occurences of item x
    if len(list(y)) > 1:
        print x  

Dane wyjściowe będą:

4
6
alfasin
źródło
1
Lub bardziej zwięźle:dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
frnhr
5

Myślę, że najbardziej skutecznym sposobem na znalezienie duplikatów na liście jest:

from collections import Counter

def duplicates(values):
    dups = Counter(values) - Counter(set(values))
    return list(dups.keys())

print(duplicates([1,2,3,6,5,2]))

Wykorzystuje Counterwszystkie elementy i wszystkie unikalne elementy. Odjęcie pierwszego od drugiego spowoduje pominięcie tylko duplikatów.

Anand Chitipothu
źródło
4

Trochę późno, ale dla niektórych może być pomocny. W przypadku obszernej listy uznałem, że to zadziałało dla mnie.

l=[1,2,3,5,4,1,3,1]
s=set(l)
d=[]
for x in l:
    if x in s:
        s.remove(x)
    else:
        d.append(x)
d
[1,3,1]

Pokazuje tylko i wszystkie duplikaty i zachowuje porządek.

użytkownik3109122
źródło
3

Bardzo prosty i szybki sposób znajdowania duplikatów za pomocą jednej iteracji w Pythonie to:

testList = ['red', 'blue', 'red', 'green', 'blue', 'blue']

testListDict = {}

for item in testList:
  try:
    testListDict[item] += 1
  except:
    testListDict[item] = 1

print testListDict

Dane wyjściowe będą następujące:

>>> print testListDict
{'blue': 3, 'green': 1, 'red': 2}

To i więcej w moim blogu http://www.howtoprogramwithpython.com

Igor Wiszniewski
źródło
3

Wchodzę znacznie później w tę dyskusję. Mimo to chciałbym poradzić sobie z tym problemem za pomocą jednej wkładki. Ponieważ taki jest urok Pythona. jeśli chcemy po prostu przenieść duplikaty na osobną listę (lub dowolną kolekcję), sugeruję zrobienie tego jak poniżej. Powiedzmy, że mamy zduplikowaną listę, którą możemy nazwać „docelową”

    target=[1,2,3,4,4,4,3,5,6,8,4,3]

Teraz, jeśli chcemy uzyskać duplikaty, możemy użyć jednej linijki, jak poniżej:

    duplicates=dict(set((x,target.count(x)) for x in filter(lambda rec : target.count(rec)>1,target)))

Ten kod umieści zduplikowane rekordy jako klucze i policzy jako wartość w słowniku „duplikaty”. Słownik „duplikatów” będzie wyglądał jak poniżej:

    {3: 3, 4: 4} #it saying 3 is repeated 3 times and 4 is 4 times

Jeśli chcesz tylko wszystkich rekordów zawierających same duplikaty na liście, jego kod jest znowu znacznie krótszy:

    duplicates=filter(lambda rec : target.count(rec)>1,target)

Dane wyjściowe będą:

    [3, 4, 4, 4, 3, 4, 3]

Działa to doskonale w wersjach Python 2.7.x +

akhil pathirippilly
źródło
3

Jednowierszowy język Python 3.8, jeśli nie chcesz pisać własnego algorytmu lub korzystać z bibliotek:

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

res = [(x, count) for x, g in groupby(sorted(l)) if (count := len(list(g))) > 1]

print(res)

Drukuje element i liczy:

[(1, 2), (2, 2), (5, 4)]

groupbyprzyjmuje funkcję grupowania, dzięki czemu można definiować grupy na różne sposoby i zwracać dodatkowe Tuplepola w razie potrzeby.

groupby jest leniwy, więc nie powinno być zbyt wolno.

yǝsʞǝla
źródło
2

Niektóre inne testy. Oczywiście zrobić ...

set([x for x in l if l.count(x) > 1])

... jest zbyt kosztowne. Jest około 500 razy szybszy (dłuższa tablica daje lepsze wyniki), aby zastosować następną metodę końcową:

def dups_count_dict(l):
    d = {}

    for item in l:
        if item not in d:
            d[item] = 0

        d[item] += 1

    result_d = {key: val for key, val in d.iteritems() if val > 1}

    return result_d.keys()

Tylko 2 pętle, bez bardzo kosztownych l.count()operacji.

Oto kod, na przykład, aby porównać metody. Kod znajduje się poniżej, oto wynik:

dups_count: 13.368s # this is a function which uses l.count()
dups_count_dict: 0.014s # this is a final best function (of the 3 functions)
dups_count_counter: 0.024s # collections.Counter

Kod testowy:

import numpy as np
from time import time
from collections import Counter

class TimerCounter(object):
    def __init__(self):
        self._time_sum = 0

    def start(self):
        self.time = time()

    def stop(self):
        self._time_sum += time() - self.time

    def get_time_sum(self):
        return self._time_sum


def dups_count(l):
    return set([x for x in l if l.count(x) > 1])


def dups_count_dict(l):
    d = {}

    for item in l:
        if item not in d:
            d[item] = 0

        d[item] += 1

    result_d = {key: val for key, val in d.iteritems() if val > 1}

    return result_d.keys()


def dups_counter(l):
    counter = Counter(l)    

    result_d = {key: val for key, val in counter.iteritems() if val > 1}

    return result_d.keys()



def gen_array():
    np.random.seed(17)
    return list(np.random.randint(0, 5000, 10000))


def assert_equal_results(*results):
    primary_result = results[0]
    other_results = results[1:]

    for other_result in other_results:
        assert set(primary_result) == set(other_result) and len(primary_result) == len(other_result)


if __name__ == '__main__':
    dups_count_time = TimerCounter()
    dups_count_dict_time = TimerCounter()
    dups_count_counter = TimerCounter()

    l = gen_array()

    for i in range(3):
        dups_count_time.start()
        result1 = dups_count(l)
        dups_count_time.stop()

        dups_count_dict_time.start()
        result2 = dups_count_dict(l)
        dups_count_dict_time.stop()

        dups_count_counter.start()
        result3 = dups_counter(l)
        dups_count_counter.stop()

        assert_equal_results(result1, result2, result3)

    print 'dups_count: %.3f' % dups_count_time.get_time_sum()
    print 'dups_count_dict: %.3f' % dups_count_dict_time.get_time_sum()
    print 'dups_count_counter: %.3f' % dups_count_counter.get_time_sum()
sergzach
źródło
2

Metoda 1:

list(set([val for idx, val in enumerate(input_list) if val in input_list[idx+1:]]))

Wyjaśnienie: [val dla idx, val w wyliczeniu (lista_wejściowa), jeśli val w liście_wejściowej [idx + 1:]] jest zrozumieniem listy, która zwraca element, jeśli ten sam element jest obecny z bieżącej pozycji, na liście, indeks .

Przykład: lista_wejściowa = [42,31,42,31,3,31,31,5,6,6,6,6,6,6,7,42]

zaczynając od pierwszego elementu na liście, 42, o indeksie 0, sprawdza, czy element 42 jest obecny na liście_wejściowej [1:] (tj. od indeksu 1 do końca listy) Ponieważ 42 jest obecny na liście_wejściowej [1:] , zwróci 42.

Następnie przechodzi do następnego elementu 31 z indeksem 1 i sprawdza, czy element 31 jest obecny na liście_wejściowej [2:] (tj. Od indeksu 2 do końca listy), ponieważ 31 jest obecny na liście_wejściowej [2:], zwróci 31.

podobnie przechodzi przez wszystkie elementy na liście i zwraca tylko powtarzające się / zduplikowane elementy do listy.

Następnie, ponieważ mamy duplikaty, na liście musimy wybrać jeden z każdego duplikatu, tj. Usunąć duplikat między duplikatami, i aby to zrobić, wywołujemy wbudowany w Pythona zestaw o nazwie set (), który usuwa duplikaty,

Następnie pozostaje nam zestaw, ale nie lista, i dlatego do konwersji z zestawu na listę używamy, rzutowania czcionek, list (), i to konwertuje zestaw elementów na listę.

Metoda 2:

def dupes(ilist):
    temp_list = [] # initially, empty temporary list
    dupe_list = [] # initially, empty duplicate list
    for each in ilist:
        if each in temp_list: # Found a Duplicate element
            if not each in dupe_list: # Avoid duplicate elements in dupe_list
                dupe_list.append(each) # Add duplicate element to dupe_list
        else: 
            temp_list.append(each) # Add a new (non-duplicate) to temp_list

    return dupe_list

Objaśnienie: Tutaj tworzymy na początek dwie puste listy. Następnie przechodź dalej przez wszystkie elementy listy, aby sprawdzić, czy istnieje ona w temp_list (początkowo pusta). Jeśli nie ma go na liście temp, dodajemy go do listy temp, używając metody append .

Jeśli już istnieje w temp_list, oznacza to, że bieżący element listy jest duplikatem i dlatego musimy dodać go do dupe_list przy użyciu metody append .

S471
źródło
2
raw_list = [1,2,3,3,4,5,6,6,7,2,3,4,2,3,4,1,3,4,]

clean_list = list(set(raw_list))
duplicated_items = []

for item in raw_list:
    try:
        clean_list.remove(item)
    except ValueError:
        duplicated_items.append(item)


print(duplicated_items)
# [3, 6, 2, 3, 4, 2, 3, 4, 1, 3, 4]

Zasadniczo usuwasz duplikaty, konwertując na set ( clean_list), a następnie iterujesz raw_list, usuwając je itemz czystej listy pod kątem wystąpienia w raw_list. Jeśli itemnie zostanie znaleziony, zgłoszony ValueErrorwyjątek zostanie przechwycony i itemzostanie dodany doduplicated_items listy.

Jeśli potrzebny jest indeks zduplikowanych elementów, wystarczy enumeratelista i zabawa z indeksem. ( for index, item in enumerate(raw_list):), który jest szybszy i zoptymalizowany dla dużych list (takich jak tysiące + elementów)

Wszystko to
źródło
2

użycie list.count()metody na liście, aby znaleźć duplikaty elementów danej listy

arr=[]
dup =[]
for i in range(int(input("Enter range of list: "))):
    arr.append(int(input("Enter Element in a list: ")))
for i in arr:
    if arr.count(i)>1 and i not in dup:
        dup.append(i)
print(dup)
Ravikiran D.
źródło
prosty sposób na znalezienie zduplikowanych elementów na liście za pomocą funkcji zliczania
Ravikiran D
2

jedno-liniowy, dla zabawy i tam, gdzie wymagane jest jedno stwierdzenie.

(lambda iterable: reduce(lambda (uniq, dup), item: (uniq, dup | {item}) if item in uniq else (uniq | {item}, dup), iterable, (set(), set())))(some_iterable)
Wizr
źródło
1
list2 = [1, 2, 3, 4, 1, 2, 3]
lset = set()
[(lset.add(item), list2.append(item))
 for item in list2 if item not in lset]
print list(lset)
Haresh Shyara
źródło
1

Rozwiązanie jednoliniowe:

set([i for i in list if sum([1 for a in list if a == i]) > 1])
ytpillai
źródło
1

Tutaj jest wiele odpowiedzi, ale myślę, że jest to stosunkowo bardzo czytelne i łatwe do zrozumienia podejście:

def get_duplicates(sorted_list):
    duplicates = []
    last = sorted_list[0]
    for x in sorted_list[1:]:
        if x == last:
            duplicates.append(x)
        last = x
    return set(duplicates)

Uwagi:

  • Jeśli chcesz zachować liczbę duplikatów, pozbądź się obsady, ustawiając na dole, aby uzyskać pełną listę
  • Jeśli wolisz używać generatorów, wymienić duplicates.append (x) z wydajnością X oraz sprawozdania powrotnej na dole (można rzutować na późniejszy)
tvt173
źródło
1

Oto szybki generator, który używa słownika do przechowywania każdego elementu jako klucza z wartością logiczną do sprawdzania, czy zduplikowany element został już wydany.

W przypadku list ze wszystkimi elementami typu skrótu:

def gen_dupes(array):
    unique = {}
    for value in array:
        if value in unique and unique[value]:
            unique[value] = False
            yield value
        else:
            unique[value] = True

array = [1, 2, 2, 3, 4, 1, 5, 2, 6, 6]
print(list(gen_dupes(array)))
# => [2, 1, 6]

W przypadku list, które mogą zawierać listy:

def gen_dupes(array):
    unique = {}
    for value in array:
        is_list = False
        if type(value) is list:
            value = tuple(value)
            is_list = True

        if value in unique and unique[value]:
            unique[value] = False
            if is_list:
                value = list(value)

            yield value
        else:
            unique[value] = True

array = [1, 2, 2, [1, 2], 3, 4, [1, 2], 5, 2, 6, 6]
print(list(gen_dupes(array)))
# => [2, [1, 2], 6]
John B.
źródło
1
def removeduplicates(a):
  seen = set()

  for i in a:
    if i not in seen:
      seen.add(i)
  return seen 

print(removeduplicates([1,1,2,2]))
ASHISH RANJAN
źródło
Zwraca zestaw, a nie listę zgodnie z żądaniem. Zestaw zawiera tylko unikalne elementy, dlatego instrukcja if nie jest tak naprawdę konieczna. Powinieneś także wyjaśnić, jakie są zalety twojego rozwiązania w porównaniu do drugiego.
clemens
1

Podczas korzystania z toolz :

from toolz import frequencies, valfilter

a = [1,2,2,3,4,5,4]
>>> list(valfilter(lambda count: count > 1, frequencies(a)).keys())
[2,4] 
Andreas Profous
źródło
0

w ten sposób musiałem to zrobić, ponieważ rzuciłem sobie wyzwanie, aby nie używać innych metod:

def dupList(oldlist):
    if type(oldlist)==type((2,2)):
        oldlist=[x for x in oldlist]
    newList=[]
    newList=newList+oldlist
    oldlist=oldlist
    forbidden=[]
    checkPoint=0
    for i in range(len(oldlist)):
        #print 'start i', i
        if i in forbidden:
            continue
        else:
            for j in range(len(oldlist)):
                #print 'start j', j
                if j in forbidden:
                    continue
                else:
                    #print 'after Else'
                    if i!=j: 
                        #print 'i,j', i,j
                        #print oldlist
                        #print newList
                        if oldlist[j]==oldlist[i]:
                            #print 'oldlist[i],oldlist[j]', oldlist[i],oldlist[j]
                            forbidden.append(j)
                            #print 'forbidden', forbidden
                            del newList[j-checkPoint]
                            #print newList
                            checkPoint=checkPoint+1
    return newList

więc twoja próbka działa jako:

>>>a = [1,2,3,3,3,4,5,6,6,7]
>>>dupList(a)
[1, 2, 3, 4, 5, 6, 7]
Matt S.
źródło
3
Nie tego chciał PO. Chciał listy duplikatów, a nie listy z usuniętymi duplikatami. Sugeruję zrobienie listy z usuniętymi duplikatami duplist = list(set(a)).
zondo