Bagaimana cara menemukan duplikat dalam daftar dan membuat daftar lain dengannya?

437

Bagaimana saya bisa menemukan duplikat dalam daftar Python dan membuat daftar duplikat lain? Daftar hanya berisi bilangan bulat.

MFB
sumber
1
Anda ingin duplikat satu kali, atau setiap kali terlihat lagi?
moooeeeep
Saya pikir ini telah dijawab dengan banyak efisiensi di sini. persimpangan stackoverflow.com/a/642919/1748045 adalah metode set bawaan dan harus melakukan apa yang diperlukan
Tom Smith

Jawaban:

545

Untuk menghapus duplikat gunakan set(a). Untuk mencetak duplikat, sesuatu seperti:

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]

Perhatikan bahwa Counterini tidak terlalu efisien ( timing ) dan mungkin berlebihan di sini. setakan tampil lebih baik. Kode ini menghitung daftar elemen unik dalam urutan sumber:

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

atau, lebih ringkas:

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

Saya tidak merekomendasikan gaya yang terakhir, karena tidak jelas apa not seen.add(x)yang dilakukan ( add()metode yang ditetapkan selalu kembali None, maka kebutuhan untuk not).

Untuk menghitung daftar elemen yang digandakan tanpa pustaka:

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

Jika elemen daftar tidak hashable, Anda tidak dapat menggunakan set / dicts dan harus menggunakan solusi waktu kuadratik (bandingkan masing-masing dengan masing-masing). Sebagai contoh:

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
sumber
2
@ eric: Saya kira itu O(n), karena hanya mengulangi daftar sekali dan mengatur pencarian O(1).
georg
3
@Hugo, untuk melihat daftar duplikat, kita hanya perlu membuat daftar baru yang disebut dup dan menambahkan pernyataan lain. Misalnya:dup = [] else: dup.append(x)
Chris Nielsen
4
@oxeimon: Anda mungkin dapat ini, tetapi Anda mencetak disebut dengan tanda kurung di python 3print()
Moberg
4
mengonversi jawaban Anda untuk set () untuk mendapatkan duplikat saja. seen = set()laludupe = set(x for x in a if x in seen or seen.add(x))
Ta946
2
Untuk Python 3.x: cetak ([item untuk item, hitung di collections.Counter (a) .items () jika hitung> 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
sumber
2
Apakah ada alasan Anda menggunakan pemahaman daftar alih-alih pemahaman generator?
64
Memang, solusi sederhana, tetapi kompleksitas kuadrat karena setiap hitungan () mem-parsing daftar lagi, jadi jangan gunakan untuk daftar besar.
danuker
4
@JohnJ, semacam gelembung juga sederhana dan berfungsi. Itu tidak berarti kita harus menggunakannya!
John La Rooy
@JohnLaRooy Sebenarnya ini berarti kita tidak boleh menggunakannya, karena hampir selalu ada cara yang lebih efisien (dan lebih sederhana) untuk melakukan sortir.
lostsoul29
1
@watsonic: "saklar sederhana" Anda gagal mengurangi kompleksitas waktu dari kuadrat menjadi kuadrat dalam kasus umum. Mengganti ldengan set(l)hanya mengurangi kompleksitas waktu terburuk dan karenanya tidak melakukan apa pun untuk mengatasi masalah efisiensi skala besar dengan jawaban ini. Mungkin itu tidak sesederhana itu. Singkatnya, jangan lakukan ini.
Cecil Curry
82

Anda tidak perlu menghitung, hanya apakah barang itu dilihat sebelumnya atau tidak. Diadaptasi jawaban itu untuk masalah ini:

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]

Untuk berjaga-jaga soal kecepatan, berikut adalah beberapa timing:

# 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

Inilah hasilnya: (dilakukan dengan baik @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

Menariknya, selain timing itu sendiri, peringkat juga sedikit berubah ketika pypy digunakan. Yang paling menarik, pendekatan berbasis-Counter mendapat manfaat besar dari optimasi pypy, sedangkan metode caching yang saya sarankan tampaknya hampir tidak berpengaruh.

$ 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

Tampaknya efek ini terkait dengan "duplikasi" dari data input. Saya telah menetapkan l = [random.randrange(1000000) for i in xrange(10000)]dan mendapatkan hasil ini:

$ 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
sumber
6
Hanya ingin tahu - apa tujuan seen_add = seen.add di sini?
Rob
3
@Rob Dengan cara ini Anda memanggil fungsi yang pernah Anda lihat sebelumnya. Kalau tidak, Anda perlu mencari (permintaan kamus) fungsi anggota addsetiap kali memasukkan akan diperlukan.
moooeeeep
diperiksa dengan data saya sendiri dan% timeit dari Ipython, metode Anda terlihat paling cepat saat tes TETAPI: "Proses paling lambat butuh 4,34 kali lebih lama daripada yang tercepat. Ini bisa berarti bahwa hasil antara sedang di-cache"
Joop
1
@ooooeeeee, saya menambahkan versi lain ke skrip Anda untuk Anda coba :) Coba juga pypyjika Anda memilikinya dan akan mempercepat.
John La Rooy
@JohnLaRooy Peningkatan kinerja yang bagus! Menariknya, ketika saya menggunakan pypy untuk mengevaluasi hasilnya, pendekatan berbasis Counter meningkat secara signifikan.
moooeeeep
42

Anda bisa menggunakan iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

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

atau jika Anda hanya menginginkan satu dari setiap duplikat ini dapat digabungkan dengan iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen

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

Ia juga dapat menangani elemen yang tidak dapat dihancurkan (namun dengan biaya kinerja):

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

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

Itu adalah sesuatu yang hanya bisa dilakukan oleh beberapa pendekatan lain di sini.

Tolak ukur

Saya melakukan patokan cepat yang mengandung sebagian besar (tetapi tidak semua) pendekatan yang disebutkan di sini.

Tolok ukur pertama hanya mencakup sejumlah kecil daftar panjang karena beberapa pendekatan memiliki O(n**2) perilaku.

Dalam grafik, sumbu y mewakili waktu, sehingga nilai yang lebih rendah berarti lebih baik. Itu juga diplot log-log sehingga berbagai nilai dapat divisualisasikan dengan lebih baik:

masukkan deskripsi gambar di sini

Menghapus O(n**2)pendekatan saya lakukan tolok ukur lain hingga setengah juta elemen dalam daftar:

masukkan deskripsi gambar di sini

Seperti yang Anda lihat, iteration_utilities.duplicatespendekatannya lebih cepat daripada pendekatan lain mana pun dan bahkan rantai unique_everseen(duplicates(...))lebih cepat atau sama cepatnya daripada pendekatan lainnya.

Satu hal tambahan yang menarik untuk dicatat di sini adalah bahwa pendekatan panda sangat lambat untuk daftar kecil tetapi dapat dengan mudah bersaing untuk daftar yang lebih panjang.

Namun karena tolok ukur ini menunjukkan sebagian besar pendekatan melakukan kurang lebih sama, sehingga tidak masalah banyak yang digunakan (kecuali untuk 3 yang memiliki O(n**2)runtime).

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

Tolok Ukur 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()

Tolok Ukur 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()

Penolakan

1 ini adalah dari pihak ketiga perpustakaan saya telah menulis: iteration_utilities.

MSeifert
sumber
1
Saya akan menempelkan leher saya di sini dan menyarankan menulis perpustakaan yang dipesan lebih dahulu untuk melakukan pekerjaan dalam C daripada Python mungkin bukan semangat jawaban yang sedang dicari - tapi itu adalah pendekatan yang sah! Saya suka lebar jawaban dan tampilan grafis dari hasil - sangat bagus untuk melihat mereka sedang berkumpul, membuat saya bertanya-tanya apakah mereka pernah menyeberang karena input meningkat lebih lanjut! Pertanyaan: apa hasilnya dengan sebagian besar daftar yang diurutkan dibandingkan dengan daftar yang sepenuhnya acak?
F1Rumors
30

Saya menemukan pertanyaan ini sambil mencari sesuatu yang berhubungan - dan bertanya-tanya mengapa tidak ada yang menawarkan solusi berbasis generator? Memecahkan masalah ini adalah:

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

Saya prihatin dengan skalabilitas, jadi menguji beberapa pendekatan, termasuk item naif yang bekerja dengan baik pada daftar kecil, tetapi skala mengerikan ketika daftar menjadi lebih besar (catatan - akan lebih baik menggunakan timeit, tetapi ini ilustrasi).

Saya menyertakan @ mooooeeeep untuk perbandingan (sangat cepat: tercepat jika daftar input benar-benar acak) dan pendekatan itertools yang bahkan lebih cepat lagi untuk sebagian besar daftar yang diurutkan ... Sekarang termasuk pendekatan panda dari @firelynx - lambat, tetapi tidak begitu mengerikan, dan sederhana. Catatan - pendekatan sort / tee / zip secara konsisten tercepat di mesin saya untuk daftar yang sebagian besar dipesan, moooeeeep tercepat untuk daftar yang diacak, tetapi jarak tempuh Anda mungkin bervariasi.

Keuntungan

  • sangat cepat dan mudah untuk menguji duplikat 'apa saja' menggunakan kode yang sama

Asumsi

  • Duplikat harus dilaporkan sekali saja
  • Urutan duplikat tidak perlu dipertahankan
  • Duplikat mungkin ada di mana saja dalam daftar

Solusi tercepat, entri 1m:

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

Pendekatan diuji

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

Hasil untuk tes 'semua dupes' konsisten, menemukan duplikat "pertama" lalu duplikat "semua" dalam array ini:

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  

Ketika daftar dikocok terlebih dahulu, harga semacam itu menjadi jelas - efisiensinya turun secara nyata dan pendekatan @moooeeeep mendominasi, dengan pendekatan set & dict menjadi serupa tetapi berkinerja lebih rendah:

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
sumber
@moooeeeep - tertarik untuk melihat pandangan Anda tentang pendekatan ifilter / izip / tee.
F1Rumors
1
jawaban ini sangat bagus. Saya tidak mengerti bahwa itu tidak memiliki poin lebih banyak untuk penjelasan dan tes yang sangat berguna bagi mereka yang membutuhkannya.
dlewin
1
Sortir python adalah O (n) ketika hanya satu item yang rusak. Anda harus random.shuffle(c)memperhitungkannya. Selain itu saya tidak dapat mereplikasi hasil Anda ketika menjalankan skrip yang tidak diubah juga (pemesanan yang sama sekali berbeda), jadi mungkin itu tergantung pada CPU juga.
John La Rooy
Terima kasih @ John-La-Rooy, pengamatan yang cerdik pada CPU / mesin lokal berdampak - jadi saya harus mengubah item YYMV . Menggunakan sortir O (n) disengaja: elemen duplikat disisipkan di lokasi yang berbeda secara khusus untuk melihat dampak pendekatan jika ada duplikat tunggal di lokasi yang baik (awal daftar) atau buruk (akhir daftar) dengan ini pendekatan. Saya menganggap daftar acak - misalnya random.shuffle - tetapi memutuskan bahwa hanya akan masuk akal jika saya melakukan lebih banyak berjalan! Saya harus mengembalikan dan membandingkan beberapa run / shuffle yang setara dan melihat apa dampaknya.
F1Rumors
Diubah untuk memasukkan pendekatan panda @firelynx & untuk dijalankan pada daftar yang dikocok sepenuhnya serta daftar yang diurutkan. Ini karena timsort asli yang digunakan oleh Python jahat dengan cepat pada sebagian besar data yang diurutkan (kasus terbaik) dan daftar acak adalah skenario kasus terburuknya - yang mengguncang hasilnya.
F1Rumors
13

Menggunakan panda:

>>> import pandas as pd
>>> a = [1, 2, 1, 3, 3, 3, 0]
>>> pd.Series(a)[pd.Series(a).duplicated()].values
array([1, 3, 3])
tukang kata
sumber
10

collections.Counter baru di python 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'
>>> 

Dalam versi sebelumnya, Anda dapat menggunakan dikt konvensional sebagai gantinya:

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

Inilah solusi yang rapi dan ringkas -

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

li = list(set(li))
Nikhil Prabhu
sumber
Daftar aslinya hilang. Ini dapat diperbaiki dengan menyalin konten ke daftar lain - temp = li [:]
Nikhil Prabhu
3
Latihan yang cukup buruk pada daftar besar - menghapus elemen dari daftar cukup mahal!
F1Rumors
7

Tanpa mengkonversi ke daftar dan mungkin cara paling sederhana adalah seperti di bawah ini. Ini mungkin berguna selama wawancara ketika mereka meminta untuk tidak menggunakan set

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

======= lain untuk mendapatkan 2 daftar nilai unik dan nilai duplikat yang terpisah

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
sumber
1
Ini tidak menghasilkan daftar duplikat dari (atau daftar asli), ini menghasilkan daftar semua elemen unik dari (atau daftar asli). Apa yang akan dilakukan seseorang setelah mereka selesai membuat daftar "dup"?
gameCoder95
6

Saya akan melakukan ini dengan panda, karena saya sering menggunakan panda

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

Memberi

[3,6]

Mungkin tidak terlalu efisien, tetapi pasti lebih sedikit kode daripada banyak jawaban lain, jadi saya pikir saya akan berkontribusi

firelynx
sumber
3
Perhatikan juga bahwa panda berisi fungsi duplikat pda = pd.Series(a) print list(pda[pda.duplicated()])
bawaan
6

contoh ketiga dari jawaban yang diterima memberikan jawaban yang salah dan tidak berusaha memberikan duplikat. Ini versi yang benar:

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

Bagaimana kalau hanya loop melalui setiap elemen dalam daftar dengan memeriksa jumlah kejadian, kemudian menambahkannya ke set yang kemudian akan mencetak duplikat. Semoga ini bisa membantu seseorang di luar sana.

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

Kita dapat menggunakan itertools.groupbyuntuk menemukan semua item yang memiliki dups:

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  

Outputnya adalah:

4
6
Alfasin
sumber
1
Atau lebih dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
singkatnya
5

Saya kira cara paling efektif untuk menemukan duplikat dalam daftar adalah:

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

Ini menggunakan Countersemua elemen dan semua elemen unik. Mengurangi yang pertama dengan yang kedua hanya akan meninggalkan duplikat.

Anand Chitipothu
sumber
4

Agak terlambat, tapi mungkin bermanfaat untuk beberapa. Untuk daftar yang lebih besar, saya menemukan ini bekerja untuk saya.

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]

Menunjukkan keadilan dan semua duplikat serta menjaga ketertiban.

pengguna3109122
sumber
3

Cara yang sangat sederhana dan cepat untuk menemukan dupes dengan satu iterasi di Python adalah:

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

testListDict = {}

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

print testListDict

Outputnya adalah sebagai berikut:

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

Ini dan lebih banyak lagi di blog saya http://www.howtoprogramwithpython.com

Igor Vishnevskiy
sumber
3

Saya banyak terlambat masuk ke diskusi ini. Meskipun demikian, saya ingin menangani masalah ini dengan satu kalimat. Karena itulah pesona Python. jika kita hanya ingin mendapatkan duplikat ke daftar terpisah (atau koleksi apa pun), saya akan menyarankan untuk melakukan seperti di bawah ini. Katanya kita memiliki daftar duplikat yang dapat kita sebut sebagai 'target'

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

Sekarang jika kita ingin mendapatkan duplikat, kita dapat menggunakan satu liner seperti di bawah ini:

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

Kode ini akan menempatkan catatan yang digandakan sebagai kunci dan menghitung sebagai nilai dalam kamus 'duplikat'. Kamus 'duplikat' akan terlihat seperti di bawah ini:

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

Jika Anda hanya ingin semua catatan dengan duplikat saja dalam daftar, kode yang jauh lebih pendek:

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

Output akan menjadi:

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

Ini berfungsi sempurna dalam versi python 2.7.x +

akhil pathirippilly
sumber
3

Python 3.8 one-liner jika Anda tidak ingin menulis algoritma Anda sendiri atau menggunakan pustaka:

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)

Mencetak item dan menghitung:

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

groupbymengambil fungsi pengelompokan sehingga Anda dapat menentukan pengelompokan Anda dengan cara yang berbeda dan mengembalikan Tuplebidang tambahan sesuai kebutuhan.

groupby malas jadi seharusnya tidak terlalu lambat.

yǝsʞǝla
sumber
2

Beberapa tes lain. Tentu saja ...

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

... terlalu mahal. Ini sekitar 500 kali lebih cepat (array yang lebih panjang memberikan hasil yang lebih baik) untuk menggunakan metode terakhir berikut:

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

Hanya 2 loop, tidak ada l.count()operasi yang sangat mahal .

Berikut ini adalah kode untuk membandingkan metode misalnya. Kode di bawah ini, ini adalah output:

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

Kode pengujian:

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

Metode 1:

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

Penjelasan: [val untuk idx, val in enumerate (input_list) jika val di input_list [idx + 1:]] adalah pemahaman daftar, yang mengembalikan elemen, jika elemen yang sama hadir dari posisi saat ini, dalam daftar, indeks .

Contoh: input_list = [42,31,42,31,3,31,31,5,6,6,6,6,6,7,42]

dimulai dengan elemen pertama dalam daftar, 42, dengan indeks 0, memeriksa apakah elemen 42, ada di input_list [1:] (yaitu, dari indeks 1 hingga akhir daftar) Karena 42 ada di input_list [1:] , itu akan mengembalikan 42.

Kemudian ia pergi ke elemen 31 berikutnya, dengan indeks 1, dan memeriksa apakah elemen 31 ada di input_list [2:] (yaitu, dari indeks 2 hingga akhir daftar), Karena 31 ada di input_list [2:], itu akan mengembalikan 31.

sama halnya ia menelusuri semua elemen dalam daftar, dan hanya akan mengembalikan elemen berulang / duplikat ke dalam daftar.

Kemudian karena kita memiliki duplikat, dalam daftar, kita perlu memilih satu dari setiap duplikat, yaitu menghapus duplikat di antara duplikat, dan untuk melakukannya, kita memanggil python built-in set bernama (), dan menghapus duplikat,

Kemudian kita dibiarkan dengan satu set, tetapi bukan daftar, dan karenanya untuk mengkonversi dari satu set ke daftar, kita menggunakan, typecasting, list (), dan yang mengubah set elemen ke daftar.

Metode 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

Penjelasan: Di sini Kami membuat dua daftar kosong, untuk memulai. Lalu teruslah menelusuri semua elemen daftar, untuk melihat apakah ada di temp_list (awalnya kosong). Jika tidak ada di temp_list, maka kami menambahkannya ke temp_list, menggunakan metode append .

Jika sudah ada di temp_list, itu berarti, elemen daftar saat ini adalah duplikat, dan karenanya kita perlu menambahkannya ke dupe_list menggunakan metode append .

S471
sumber
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]

Anda pada dasarnya menghapus duplikat dengan mengonversi ke set ( clean_list), lalu iterasi raw_list, sambil menghapus masing-masing itemdalam daftar bersih untuk terjadinya di raw_list. Jika itemtidak ditemukan, ValueErrorPengecualian yang diangkat ditangkap dan itemditambahkan ke duplicated_itemsdaftar.

Jika indeks item duplikat diperlukan, cukup enumeratedaftar dan mainkan dengan indeks. ( for index, item in enumerate(raw_list):) yang lebih cepat dan dioptimalkan untuk daftar besar (seperti ribuan + elemen)

Semuanya
sumber
2

penggunaan list.count()metode dalam daftar untuk mengetahui elemen duplikat dari daftar yang diberikan

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
sumber
cara mudah untuk menemukan elemen duplikat dalam daftar menggunakan fungsi hitung
Ravikiran D
2

satu garis, untuk bersenang-senang, dan di mana diperlukan satu pernyataan.

(lambda iterable: reduce(lambda (uniq, dup), item: (uniq, dup | {item}) if item in uniq else (uniq | {item}, dup), iterable, (set(), set())))(some_iterable)
Wizr
sumber
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
sumber
1

Solusi satu baris:

set([i for i in list if sum([1 for a in list if a == i]) > 1])
ytpillai
sumber
1

Ada banyak jawaban di sini, tapi saya pikir ini relatif sangat mudah dibaca dan mudah dipahami:

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)

Catatan:

  • Jika Anda ingin mempertahankan jumlah duplikasi, singkirkan para pemain untuk 'mengatur' di bagian bawah untuk mendapatkan daftar lengkap
  • Jika Anda lebih suka menggunakan generator, ganti duplikat. Tambahkan (x) dengan hasil x dan pernyataan pengembalian di bagian bawah (Anda dapat memilih untuk mengatur nanti)
tvt173
sumber
1

Berikut adalah generator cepat yang menggunakan dict untuk menyimpan setiap elemen sebagai kunci dengan nilai boolean untuk memeriksa apakah item duplikat telah dihasilkan.

Untuk daftar dengan semua elemen yang merupakan tipe hashable:

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]

Untuk daftar yang mungkin berisi daftar:

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
sumber
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
sumber
Anda mengembalikan satu set dan bukan daftar seperti yang diminta. Satu set hanya berisi elemen unik, dengan demikian pernyataan if tidak benar-benar diperlukan. Anda juga harus menjelaskan, apa keuntungan dari solusi Anda dibandingkan dengan yang lain.
clemens
1

Saat menggunakan 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
sumber
0

inilah cara saya harus melakukannya karena saya menantang diri saya sendiri untuk tidak menggunakan metode lain:

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

jadi sampel Anda berfungsi sebagai:

>>>a = [1,2,3,3,3,4,5,6,6,7]
>>>dupList(a)
[1, 2, 3, 4, 5, 6, 7]
Matt S
sumber
3
Ini bukan yang diinginkan OP. Dia ingin daftar duplikat, bukan daftar dengan duplikat dihapus. Untuk membuat daftar dengan duplikat dihapus, saya sarankan duplist = list(set(a)).
zondo