Bir listedeki kopyaları nasıl bulabilir ve onlarla başka bir liste nasıl oluşturabilirim?


437

Bir Python listesindeki kopyaları nasıl bulabilir ve kopyaların başka bir listesini nasıl oluşturabilirim? Liste yalnızca tamsayılar içerir.



1
kopyaları bir kez mi, yoksa her tekrar görüldüğünde mi istiyorsun?
moooeeeep

Bence bu çok daha fazla verimlilikle cevaplandı. stackoverflow.com/a/642919/1748045 kesişme yerleşik bir küme yöntemidir ve tam olarak ne gerekiyorsa yapmalıdır
Tom Smith

Yanıtlar:


545

Yinelenenleri kaldırmak için kullanın set(a). Kopyaları yazdırmak için aşağıdakine benzer:

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]

Bunun Counterözellikle verimli ( zamanlamalar ) olmadığını ve muhtemelen aşırıya kaçtığını unutmayın. setdaha iyi performans gösterecektir. Bu kod, kaynak siparişindeki benzersiz öğelerin bir listesini hesaplar:

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

veya daha kısaca:

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

İkinci stili tavsiye etmiyorum, çünkü ne not seen.add(x)olduğu açık değil (set add()yöntemi her zaman geri döner None, bu nedenle ihtiyaç vardır not).

Kütüphanesiz çoğaltılan öğelerin listesini hesaplamak için:

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

Liste öğeleri yıkanamazsa, set / dicts kullanamazsınız ve ikinci dereceden bir zaman çözümüne başvurmanız gerekmez (her birini her biri ile karşılaştırın). Örneğin:

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

2
@eric: Sanırım öyle O(n), çünkü listeyi yalnızca bir kez yineliyor ve aramaları ayarlıyor O(1).
georg

3
@Hugo, kopyaların listesini görmek için dup adında yeni bir liste oluşturmanız ve başka bir ifade eklememiz gerekiyor. Örneğin:dup = [] else: dup.append(x)
Chris Nielsen

4
@oxeimon: Muhtemelen bunu aldınız, ancak baskı python 3'te parantez ile çağrıldıprint()
Moberg

4
set () için cevabınızı yalnızca kopyalar elde etmek için dönüştürmek. seen = set()sonradupe = set(x for x in a if x in seen or seen.add(x))
Ta946

2
Python 3.x için: print ([öğe için öğe, koleksiyonlarda sayın. (A) .items () eğer sayılır> 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])

2
Jeneratör kavrayışı yerine liste kavraması kullanmanız için herhangi bir neden var mı?

64
Aslında, basit bir çözümdür, ancak karmaşıklık karedir, çünkü her count () listeyi tekrar tekrar ayrıştırır, bu nedenle büyük listeler için kullanmayın.
danuker

4
@JohnJ, kabarcık sıralama da basit ve çalışıyor. Bu onu kullanmamız gerektiği anlamına gelmez!
John La Rooy

@JohnLaRooy Aslında kullanmamamız gerektiği anlamına geliyor, çünkü sıralama yapmak için neredeyse her zaman daha verimli (ve daha basit) bir yol var.
lostsoul29 13

1
@watsonic: "Basit anahtarınız" genel durumda zaman karmaşıklığını ikinci dereceden kareye indirmiyor. Değiştirme lile set(l)yok dolayısıyla kötü durum zaman karmaşıklığını azaltır ve sadece hiçbir şey bu cevap daha büyük ölçekli verimlilik endişelerini gidermek için. Sonuçta muhtemelen bu kadar basit değildi. Kısacası, bunu yapma.
Cecil Curry

82

Sayıya ihtiyacınız yoktur, sadece öğenin daha önce görülüp görülmediği. Bu soruyu bu soruna uyarladı :

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]

Hızın önemli olduğu durumlarda, işte bazı zamanlamalar:

# 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

İşte sonuçlar: (aferin @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

İlginç bir şekilde, zamanlamaların yanı sıra, pypy kullanıldığında sıralama biraz değişir. En ilgi çekici olanı, Sayaç temelli yaklaşımın, pypy'nin optimizasyonlarından büyük ölçüde faydalanmasına rağmen, önerdiğim yöntem önbellekleme yaklaşımının neredeyse hiçbir etkisi yok gibi görünüyor.

$ 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

Görünüşe göre bu etki giriş verilerinin "çoğaltılması" ile ilgilidir. l = [random.randrange(1000000) for i in xrange(10000)]Bu sonuçları belirledim ve aldım:

$ 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

6
Sadece merak ediyorum - seen_add = seen.add'nin amacı nedir?
Rob

3
@Rob Bu şekilde, daha önce bir kez aradığınız işlevi çağırırsınız. Aksi takdirde, addher ekleme işlemi gerektiğinde üye işlevini aramanız gerekir (sözlük sorgusu) .
moooeeeep

kendi verilerimle kontrol edildi ve Ipython'un% timeit yöntemi yönteminiz testte en hızlı görünüyor AMA: "En yavaş çalışma en hızlıdan 4,34 kat daha uzun sürdü. Bu, ara sonucun önbelleğe alındığı anlamına gelebilir"
Joop

1
@moooeeeep, denemeniz için betiğinize başka bir sürüm ekledim :) Ayrıca pypykullanışlı olup olmadığını ve hız için gidiyoruz deneyin .
John La Rooy

@JohnLaRooy Performansta iyi gelişme! İlginç bir şekilde, sonuçları değerlendirmek için pypy kullandığımda, Sayaç temelli yaklaşım önemli ölçüde gelişir.
moooeeeep

42

Şunları kullanabilirsiniz iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

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

veya her bir kopyadan yalnızca birini istiyorsanız, bu aşağıdakilerle birleştirilebilir iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen

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

Ayrıca, paylaşılamayan öğeleri de işleyebilir (ancak performans pahasına):

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

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

Bu, buradaki diğer yaklaşımların sadece birkaçının üstesinden gelebileceği bir şey.

Deneyler

Burada bahsedilen yaklaşımların çoğunu (ancak hepsini değil) içeren hızlı bir karşılaştırma yaptım.

İlk karşılaştırma ölçütü sadece küçük bir liste uzunluğu aralığı içermekteydi, çünkü bazı yaklaşımların O(n**2)davranışları vardır.

Grafiklerde y ekseni zamanı temsil eder, bu nedenle daha düşük bir değer daha iyi anlamına gelir. Ayrıca, geniş bir değer aralığı daha iyi görüntülenebilmesi için günlük günlüğü olarak çizilir:

resim açıklamasını buraya girin

O(n**2)Yaklaşımları kaldırarak bir listede yarım milyon öğeye kadar başka bir karşılaştırma yaptım:

resim açıklamasını buraya girin

Gördüğünüz gibi iteration_utilities.duplicatesyaklaşım diğer yaklaşımlardan unique_everseen(duplicates(...))daha hızlı ve zincirleme bile diğer yaklaşımlardan daha hızlı veya eşit derecede hızlıydı.

Burada dikkat edilmesi gereken bir başka ilginç şey de panda yaklaşımlarının küçük listeler için çok yavaş olması ancak daha uzun listeler için kolayca rekabet edebilmesidir.

Bununla birlikte, bu kriterler yaklaşımların çoğunun kabaca eşit performans gösterdiğinden, hangisinin kullanıldığının pek önemi yoktur ( O(n**2)çalışma zamanı olan 3 hariç ).

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

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

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

feragat

1 Bu yazdım bir üçüncü taraf kitaplığından geçerli: iteration_utilities.


1
Boynumu buraya yapıştıracağım ve Python'dan ziyade C'de çalışmak için ısmarlama bir kütüphane yazmayı önereceğim, muhtemelen aranan cevabın ruhu değil - ama bu meşru bir yaklaşım! Cevabın genişliğini ve sonuçların grafik görüntüsünü seviyorum - yakınsak olduklarını görmek çok güzel, girişler daha da arttıkça geçip geçmediklerini merak ediyor! Soru: Tamamen rastgele listelerin aksine çoğunlukla sıralanmış listelerin sonucu nedir?
F1Rumors

30

Bu soruyla ilgili bir şeye bakarken karşılaştım - ve neden kimsenin neden jeneratör tabanlı bir çözüm sunmadığını merak ettim? Bu sorunu çözmek:

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

Ölçeklenebilirlikle ilgilendim, bu yüzden küçük listelerde iyi çalışan naif öğeler de dahil olmak üzere çeşitli yaklaşımları test ettim, ancak listeler büyüdükçe korkunç bir şekilde ölçeklendi (not - timeit'i kullanmak daha iyi olurdu, ancak bu açıklayıcıdır).

Karşılaştırma için @moooeeeep'i dahil ettim (etkileyici bir şekilde hızlı: giriş listesi tamamen rastgele ise en hızlı) ve çoğunlukla sıralanmış listeler için daha da hızlı olan bir itertools yaklaşımı ... Şimdi @firelynx'ten panda yaklaşımı içeriyor - yavaş, ama değil çok korkunç ve basit. Not - sıralama / tee / zip yaklaşımı büyük çoğunlukla sıralı listeler için makinemde sürekli olarak en hızlıdır, moooeeeep karıştırılmış listeler için en hızlıdır, ancak kilometreniz değişebilir.

Avantajları

  • aynı kodu kullanarak 'herhangi bir' kopyalarını test etmek için çok hızlı basit

Varsayımlar

  • Kopyalar yalnızca bir kez bildirilmelidir
  • Yinelenen siparişin korunması gerekmez
  • Yinelenen listenin herhangi bir yerinde olabilir

En hızlı çözüm, 1m girişler:

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

Test edilen yaklaşımlar

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

'All dupes' testinin sonuçları tutarlıydı ve bu dizideki "ilk" yinelenen sonra "all" yinelenenlerini buldu:

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  

İlk önce listeler karıştırıldığında, türün fiyatı belirgin hale gelir - verimlilik belirgin bir şekilde düşer ve @moooeeeep yaklaşımı hakim olur, set & dict yaklaşımları benzer ancak daha az performans gösteren kişilerdir:

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  

@moooeeeep - ifilter / izip / tee yaklaşımı hakkındaki görüşlerinizi görmek ister.
F1Rumors

1
Bu cevap inanılmaz derecede iyi. İhtiyaç duyacaklar için çok yararlı olan açıklamalar ve testler için daha fazla puanı olmadığını anlamıyorum.
dlewin

1
sadece bir öğe arızalı olduğunda python'un sıralaması O (n) 'dir. Bunu random.shuffle(c)hesaba katmalısınız. Ayrıca değiştirilmemiş komut dosyasını (tamamen farklı sipariş) çalıştırırken sonuçlarınızı çoğaltamam, bu yüzden belki de CPU'ya bağlıdır.
John La Rooy

Teşekkür ederim @ John-La-Rooy, CPU / yerel makinede zeki gözlem etkili - bu yüzden YYMV öğesini değiştirmeliyim . Kullanılması O (n) sıralama kasıtlı edildi: iyi bir tek yinelenen varsa mükerrer eleman farklı yerlerde yerleştirilir özellikle yaklaşımın etkisini görmek için bu ile konumunu (listenin başlangıç) ya da kötü (listenin sonunda) yaklaşımlar. Rastgele bir liste düşündüm - örneğin random.shuffle - ancak çok daha fazla çalışma yaptığımda mantıklı olacağına karar verdim! Bir çoklu çalışma / karıştırma eşdeğerini iade edip karşılaştırmalı ve etkinin ne olduğunu görmeliyim.
F1Rumors

@Firelynx pandalar yaklaşımını içerecek şekilde değiştirilmiş ve sıralı listenin yanı sıra tamamen karıştırılmış listede çalışacak şekilde değiştirildi. Bu Python tarafından kullanılan yerli timsort çünkü kötü çoğunlukla sıralanmış verilere hızlı (en iyi durumda) ve karıştırılan listeleri en kötü durum senaryosu vardır - sonuçlarında yukarı sallar söyledi.
F1Rumors

13

Pandaları kullanma:

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

10

Sayaç python 2.7'de yeni:


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

Önceki bir sürümde bunun yerine geleneksel bir diksiyon kullanabilirsiniz:

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]

9

İşte düzgün ve özlü bir çözüm -

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

li = list(set(li))

Orijinal liste kaybolur. Bu, içerik başka bir listeye kopyalanarak düzeltilebilir - temp = li [:]
Nikhil Prabhu

3
Bu büyük listelerde oldukça kötü bir alıştırmadır - listelerden öğeleri kaldırmak oldukça pahalıdır!
F1Rumors

7

Listeye dönüştürülmeden ve muhtemelen en basit yol aşağıdaki gibi bir şey olacaktır. Bu, bir röportaj sırasında set kullanmamayı istedikleri zaman yararlı olabilir

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

======= benzersiz değerlerin ve yinelenen değerlerin 2 ayrı listesini almak için

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)

1
Bu, a'nın (veya orijinal listenin) kopyalarının bir listesi ile sonuçlanmaz, bu, a'nın (veya orijinal listenin) tüm benzersiz öğelerinin bir listesi ile sonuçlanır. Birisi "dup" listesini oluşturmayı bitirdikten sonra ne yapardı?
gameCoder95

6

Bunu pandalarla yapardım çünkü pandaları çok kullanırım

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

verir

[3,6]

Muhtemelen çok verimli değil, ama diğer cevapların çoğundan daha az kod olduğundan eminim, bu yüzden katkıda bulunacağımı düşündüm


3
Pandaların yerleşik bir çoğaltma işlevi içerdiğini de unutmayın pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken

6

kabul edilen cevabın üçüncü örneği hatalı bir cevap verir ve tekrarlar vermeye çalışmaz. İşte doğru sürüm:

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

6

Örneklerin sayısını kontrol edip, ardından kopyaları yazdıracak bir kümeye ekleyerek listedeki her öğeyi basitçe döngüye sokmaya ne dersiniz? Umarım bu birisine yardım eder.

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]

5

itertools.groupbyDups olan tüm öğeleri bulmak için kullanabiliriz :

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  

Çıktı şöyle olacaktır:

4
6

1
Veya daha kısaca:dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
frnhr

5

Bir listede yinelenen bulmak için en etkili yolu sanırım:

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

O kullandığı Countertüm elemanları ve tüm benzersiz unsurları. Birincisini ikincisi ile çıkarmak sadece kopyaları dışarıda bırakacaktır.


4

Biraz geç, ama belki bazıları için yararlı. Büyük bir liste için, bunun benim için işe yaradığını buldum.

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]

Sadece ve tüm kopyaları gösterir ve düzeni korur.


3

Python'da tek bir yineleme ile dupes bulmanın çok basit ve hızlı yolu:

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

testListDict = {}

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

print testListDict

Çıktı aşağıdaki gibi olacaktır:

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

Blogumda bu ve daha fazlası http://www.howtoprogramwithpython.com


3

Bu tartışmaya çok geç giriyorum. Yine de, bu sorunu bir gömlekle ele almak istiyorum. Çünkü bu Python'un cazibesi. sadece kopyaları ayrı bir listeye (veya herhangi bir koleksiyona) almak istiyorsak, aşağıdaki gibi yapmayı öneriyorum.

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

Şimdi kopyaları almak istiyorsak, bir astarı aşağıdaki gibi kullanabiliriz:

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

Bu kod, yinelenen kayıtları anahtar olarak koyacaktır ve 'yinelenen' sözlüğüne değer olarak sayılacaktır. 'Yinelenen' sözlük aşağıdaki gibi görünecektir:

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

Yalnızca bir listede yinelenen tüm kayıtları istiyorsanız, yine çok daha kısa kod:

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

Çıktı:

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

Bu, python 2.7.x + sürümlerinde mükemmel çalışır


3

Kendi algoritmanızı yazmak veya kitaplıkları kullanmak istemiyorsanız Python 3.8 tek katmanlı:

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)

Öğeyi ve sayıyı yazdırır:

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

groupbygruplamalarınızı farklı şekillerde tanımlayabilmeniz ve Tuplegerektiğinde ek alanları döndürebilmeniz için bir gruplama işlevi alır .

groupby tembeldir, bu yüzden çok yavaş olmamalıdır.


2

Diğer bazı testler. Tabii ki ...

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

... çok pahalı. Bir sonraki son yöntemi kullanmak yaklaşık 500 kat daha hızlıdır (daha uzun dizi daha iyi sonuçlar verir):

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

Sadece 2 döngü, çok maliyetli l.count()işlemler yok.

Örneğin, yöntemleri karşılaştırmak için bir kod. Kod aşağıda, işte çıktı:

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

Test kodu:

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

2

Yöntem 1:

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

Açıklama: [id_ için val, numaralandırmada val (giriş_listesi) input_list'deki val [idx + 1:]], aynı öğenin geçerli konumundan, listede, dizinden varsa bir öğeyi döndüren bir liste kavramadır .

Örnek: giriş_listesi = [42,31,42,31,3,31,31,5,6,6,6,6,6,7,42]

listedeki ilk elemandan (42) indeks 0 ile başlayarak, 42 ​​elemanının input_list [1:] 'de mevcut olup olmadığını kontrol eder (yani indeks 1'den listenin sonuna kadar) Çünkü input_list [1:]' de 42 mevcut , 42 döndürür.

Daha sonra dizin 1 ile bir sonraki öğeye 31 gider ve giriş_listesi [2:] 'de eleman 31 olup olmadığını kontrol eder (yani, dizin 2'den listenin sonuna kadar), çünkü giriş_listesi [2:]' de 31 mevcut, 31 dönecektir.

benzer şekilde listedeki tüm öğelerden geçer ve yalnızca tekrarlanan / yinelenen öğeleri bir listeye döndürür.

Sonra, kopyalarımız olduğu için, bir listede, her bir kopyadan birini seçmemiz, yani kopyalar arasında kopyayı kaldırmamız ve bunu yapmak için set () adlı yerleşik bir python çağırıyoruz ve kopyaları kaldırıyor,

Sonra bir kümeyle bırakılır, ancak bir liste kalmaz ve bu nedenle bir kümeden listeye dönüştürmek için, typecasting, list () kullanırız ve bu da öğeler kümesini bir listeye dönüştürür.

Yöntem 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

Açıklama: Burada başlamak üzere iki boş liste oluşturuyoruz. Daha sonra temp_list'te (başlangıçta boş) olup olmadığını görmek için listenin tüm öğelerinde dolaşmaya devam edin. Temp_list'de yoksa, append yöntemini kullanarak temp_list'e ekleriz .

Temp_list'te zaten varsa, listenin geçerli öğesinin bir kopya olduğu anlamına gelir ve bu nedenle append yöntemini kullanarak dupe_list'e eklememiz gerekir .


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]

Temel olarak yinelenenleri set ( clean_list) biçimine dönüştürerek kaldırırsınız , ardından temiz listedeki raw_listher birini itemoluşum için kaldırırken yineleyin raw_list. Eğer itembulunamazsa, yükseltilmiş ValueErrorİstisna yakalanır ve itemeklenir duplicated_itemslisteye.

Çoğaltılan öğelerin dizini gerekiyorsa, yalnızca enumeratelisteyi ekleyin ve dizinle oynatın. ( for index, item in enumerate(raw_list):) büyük listeler için daha hızlı ve optimize edilmiş (binlerce + öğe gibi)


2

list.count()Belirli bir listenin yinelenen öğelerini bulmak için listedeki yöntemin kullanılması

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)

işlevini kullanarak listedeki yinelenen öğeleri bulmak için basit bir yol
Ravikiran D

2

tek astar, eğlence için ve tek bir ifadenin gerekli olduğu yerlerde.

(lambda iterable: reduce(lambda (uniq, dup), item: (uniq, dup | {item}) if item in uniq else (uniq | {item}, dup), iterable, (set(), set())))(some_iterable)

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)

1

Tek hat çözümü:

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

1

Burada birçok cevap var, ancak bence bu nispeten okunabilir ve anlaşılması kolay bir yaklaşım:

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)

Notlar:

  • Çoğaltma sayısını korumak istiyorsanız, tam listeyi almak için alta 'altta' ayarlanacak dökümden kurtulun
  • Jeneratör kullanmayı tercih ediyorsanız, duplicates.append (x) öğesini x verimi ve alttaki return deyimi ile değiştirin ( daha sonra ayarlamak için yayınlayabilirsiniz)

1

Burada, yinelenen öğenin zaten verilip verilmediğini kontrol etmek için her öğeyi boole değerine sahip bir anahtar olarak depolamak için bir diksiyon kullanan hızlı bir jeneratör bulunmaktadır.

Yıkanabilir tipte tüm elemanlara sahip listeler için:

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]

Liste içerebilecek listeler için:

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]

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

İstediğiniz gibi bir liste değil bir küme döndürürsünüz. Bir küme yalnızca benzersiz öğeler içerir, bu nedenle if ifadesi gerçekten gerekli değildir. Ayrıca, çözümünüzden diğerine kıyasla avantajının ne olduğunu da açıklamalısınız.
clemens

1

Toolz kullanırken :

from toolz import frequencies, valfilter

a = [1,2,2,3,4,5,4]
>>> list(valfilter(lambda count: count > 1, frequencies(a)).keys())
[2,4] 

0

kendimi başka yöntemler kullanmamaya meydan okuduğum için bunu yapmam gerekti:

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

böylece örnek olarak çalışır:

>>>a = [1,2,3,3,3,4,5,6,6,7]
>>>dupList(a)
[1, 2, 3, 4, 5, 6, 7]

3
OP'nin istediği bu değil. Kopyaların kaldırıldığı bir liste değil, kopyaların bir listesini istedi. Yinelenenleri kaldırılmış bir liste yapmak için öneririm duplist = list(set(a)).
zondo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.