Як знайти дублікати у списку та створити з ними інший список?


437

Як я можу знайти дублікати у списку Python та створити інший список дублікатів? Список містить лише цілі числа.



1
чи хочете дублікати один раз, або кожен раз, коли він знову бачиться?
moooeeeep

Я думаю, що тут відповіли набагато ефективніше. stackoverflow.com/a/642919/1748045 перехрестя - це вбудований метод множини і повинен робити саме те, що потрібно
Том Сміт

Відповіді:


545

Для видалення дублікатів використовуйте set(a). Для друку дублікатів:

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]

Зауважте, що Counterце не особливо ефективно ( таймінги ) і, ймовірно, тут надмір. setвиступить краще. Цей код обчислює список унікальних елементів у вихідному порядку:

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

або, більш коротко:

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

Я не рекомендую останній стиль, тому що не очевидно, що not seen.add(x)робиться (заданий add()метод завжди повертається None, звідси і потреба not).

Для обчислення списку дублюваних елементів без бібліотек:

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

Якщо елементи списку не є доступними для використання, ви не можете використовувати набори / дикти, і вам доведеться вдаватися до рішення квадратичного часу (порівняйте кожен з кожним). Наприклад:

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: Я думаю, що це так O(n), тому що він лише повторює список один раз і встановлює пошукові запити O(1).
georg

3
@Hugo, щоб побачити список дублікатів, нам просто потрібно створити новий список під назвою dup і додати інше твердження. Наприклад:dup = [] else: dup.append(x)
Кріс Нільсен

4
@oxeimon: ви, мабуть, це отримали, але ви друкуєтесь, що викликається дужками в python 3print()
Moberg

4
перетворення вашої відповіді для set (), щоб отримати лише дублікати. seen = set()потімdupe = set(x for x in a if x in seen or seen.add(x))
Ta946

2
Для Python 3.x: print ([предмет для елемента, рахуйте у колекціях. Counter (a) .items (), якщо кількість> 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
Чи є якась причина, що ви використовуєте розуміння списку замість розуміння генератора?

64
Дійсно, просте рішення, але складність складається у квадраті, оскільки кожен count () розбирає список заново, тому не використовуйте для великих списків.
danuker

4
@JohnJ, сортування міхурів також просте і працює. Це не означає, що ми повинні ним користуватися!
Джон Ла Руй

@JohnLaRooy Насправді це означає, що ми не повинні його використовувати, оскільки майже завжди існує більш ефективний (і простіший) спосіб сортування.
lostsoul29

1
@watsonic: Ваш "простий перемикач" не вдається зменшити складність у часі від квадратичного до квадратного в загальному випадку. Заміна lз set(l)тільки скорочує час якої складності в гіршому випадку і , отже , не чинить нічого для вирішення більш масштабних проблем ефективності з цією відповіддю. Зрештою, це було не так просто. Словом, не робіть цього.
Сесіль Карі

82

Кількість вам не потрібна, лише те, чи бачили предмет раніше. Адаптовано відповідь на цю проблему:

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]

На випадок, якщо швидкість має значення, ось кілька моментів:

# 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

Ось результати: (молодець @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

Цікаво, що, крім самих таймінгів, також ранжирування дещо змінюється при використанні pypy. Найцікавіше, що підхід на основі Counter значно виграє від оптимізації pypy, тоді як метод кешування методів, який я запропонував, здається, майже не впливає.

$ 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

Очевидно, цей ефект пов'язаний з "дублюванням" вхідних даних. Я встановив l = [random.randrange(1000000) for i in xrange(10000)]і отримав такі результати:

$ 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
Просто цікаво - яка мета тут see_add = saw.add?
Роб

3
@Rob Таким чином ви просто викликаєте функцію, яку ви шукали один раз раніше. Інакше вам потрібно буде шукати (запит словника) функцію члена addкожного разу, коли вставка буде необхідна.
moooeeeep

перевіряють мої власні дані, і Ipython's% timeit ваш метод виглядає найшвидше на тесті, АЛЕ: "Найповільніший запуск займає в 4,34 рази довше, ніж найшвидший. Це може означати, що проміжний результат кешується"
Joop

1
@moooeeeep, я додав ще одну версію до вашого сценарію, щоб ви спробували :) Також спробуйте, pypyякщо вам це зручно і збираєтесь на швидкість.
Джон Ла Руй

@JohnLaRooy Приємне поліпшення продуктивності! Цікаво, що коли я використовував pypy для оцінки результатів, підхід на основі Counter значно покращується.
moooeeeep

42

Ви можете використовувати iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

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

або якщо ви хочете лише один дублікат, це можна комбінувати з iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen

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

Він також може обробляти незмінні елементи (правда, ціною продуктивності):

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

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

Це те, з чим тут впораються лише деякі інші підходи.

Орієнтири

Я зробив швидкий орієнтир, що містить більшість (але не всі) підходів, згаданих тут.

Перший орієнтир включав лише невеликий діапазон довжин списків, оскільки деякі підходи мають O(n**2) поведінку.

У графіках вісь y представляє час, тому нижнє значення означає краще. Це також графічний журнал журналу, щоб широкий діапазон значень можна було краще візуалізувати:

введіть тут опис зображення

Видаляючи O(n**2)підходи, я зробив ще один показник до півмільйона елементів у списку:

введіть тут опис зображення

Як ви бачите, iteration_utilities.duplicatesпідхід швидший, ніж будь-який з інших підходів, і навіть ланцюжок unique_everseen(duplicates(...))був швидшим або однаково швидким, ніж інші підходи.

Тут слід зазначити ще одну цікаву річ, що підходи до панд дуже повільні для невеликих списків, але можуть легко конкурувати за довші списки.

Однак, як показують ці орієнтири, більшість підходів виконує приблизно однаково, тому не має великого значення, який з них використовується (за винятком 3, які мали O(n**2)час виконання).

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

Орієнтир 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()

Орієнтир 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()

Відмова від відповідальності

1 Це з бібліотеки третьою стороною я написав: iteration_utilities.


1
Я збираюся висунути тут шию і запропоную написати бібліотеку на замовлення, щоб виконати роботу на C, а не на Python, мабуть, це не той дух відповіді, який шукали - але це законний підхід! Мені подобається ширина відповіді та графічне відображення результатів - дуже приємно бачити, що вони збігаються, змушує мене замислитись, чи коли-небудь вони будуть перетинатися, коли введення збільшуються далі! Запитання: що є результатом в основному відсортованих списків на відміну від повністю випадкових списків?
F1Rumors

30

Я натрапив на це питання, шукаючи щось пов'язане - і цікаво, чому ніхто не пропонував рішення на основі генератора? Вирішити цю проблему було б:

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

Мене хвилює масштабованість, тому перевіряв декілька підходів, включаючи наївні елементи, які добре працюють у невеликих списках, але жахливо масштабуються, оскільки списки збільшуються (примітка - краще було б використовувати timeit, але це ілюстративно).

Я включив @moooeeeep для порівняння (це вражаюче швидко: найшвидший, якщо вхідний список є абсолютно випадковим) і підхід до itertools, який ще швидше знову для в основному відсортованих списків ... Тепер включає підхід панди від @firelynx - повільний, але не жахливо так, і просто. Примітка - підхід сортування / tee / zip послідовно найшвидший на моїй машині для великих впорядкованих списків, moooeeeep найшвидший для перетасованих списків, але ваш пробіг може змінюватися.

Переваги

  • дуже швидко перевірити наявність "будь-яких" дублікатів, використовуючи той самий код

Припущення

  • Про дублікати потрібно повідомляти лише один раз
  • Подвійний порядок зберігати не потрібно
  • Дублікат може бути в будь-якому місці списку

Найшвидше рішення, 1 м записів:

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

Підходи перевірені

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" були узгодженими, знайшовши "first" дублікат, а потім "all" у цьому масиві:

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  

Коли перші списки перетасовуються, ціна сортування стає очевидною - ефективність помітно падає, а підхід @moooeeeep домінує, при цьому підходи set & dict є схожими, але меншими виконавцями:

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

1
ця відповідь неймовірно хороша. Я не розумію, що у неї не було більше балів для пояснень і тестів, які дуже корисні для тих, хто цього потребував.
dlewin

1
Сорт python - це O (n), коли лише один елемент вийшов з ладу. Ви повинні random.shuffle(c)це врахувати. Крім того, я не можу копіювати ваші результати під час запуску незмінного сценарію (абсолютно іншого впорядкування), тому, можливо, це залежить і від процесора.
Джон Ла Руй

Дякую @ John-La-Rooy, проникливе спостереження за процесором / локальною машиною є вражаючим - тому я повинен внести зміни до пункту YYMV . Використання сортування O (n) було навмисним: дублюючий елемент вставляється в різних місцях, спеціально для того, щоб побачити вплив підходу, якщо є єдиний дублікат у хорошому (початок списку) або поганому (кінець списку) розташуванні з цими підходи. Я розглядав випадковий список - наприклад, random.shuffle - але вирішив, що розумним буде лише, якщо я пророблю набагато більше пробіжок! Мені доведеться повернути та порівняти еквівалент декількох пробіг / перемішання і подивитися, який вплив буде.
F1Rumors

Змінено, щоб включити підхід @firelynx pandas & працювати за повністю перетасованим списком, а також відсортованим списком. Це пояснюється тим, що нативний таймсорт, який використовується Python, швидко лукавить за в основному відсортованими даними (найкращий випадок), а перетасовані списки - це його найгірший сценарій - який похитує результати.
F1Rumors

13

Використання панд:

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

collection.Counter є новим у 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'
>>> 

У більш ранній версії замість цього можна використовувати звичайний дикт:

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

Ось акуратне і стисле рішення -

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

li = list(set(li))

Однак початковий список втрачено. Це можна виправити, скопіювавши вміст до іншого списку - temp = li [:]
Nikhil Prabhu

3
Це досить неприємна вправа у великих списках - видалення елементів зі списків досить дороге!
F1Rumors

7

Без перетворення в список і, мабуть, найпростішим способом було б щось подібне нижче. Це може бути корисно під час інтерв'ю, коли вони просять не використовувати набори

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

======= інше, щоб отримати 2 окремих списку унікальних значень і дублюючих значень

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
Це не призводить до списку дублікатів (або оригінального списку), однак це призводить до переліку всіх унікальних елементів (або оригінального списку). Що б хтось зробив після того, як закінчив формувати список "дуб"?
gameCoder95

6

Я би робив це з пандами, тому що я багато використовую панди

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

Дає

[3,6]

Напевно, це не дуже ефективно, але, безумовно, це менше коду, ніж багато інших відповідей, тому я подумав, що буду сприяти


3
Зауважимо також, що панди містять вбудовану функцію дублікатів pda = pd.Series(a) print list(pda[pda.duplicated()])
Лен Блокен

6

третій приклад прийнятої відповіді дає помилкову відповідь і не намагається дати дублікати. Ось правильна версія:

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

Як щодо простого перегляду циклу кожного елемента у списку, перевіривши кількість подій, а потім додати їх до набору, який потім надрукує дублікати. Сподіваюся, це допомагає комусь там.

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.groupbyдля того, щоб знайти всі предмети, у яких є дупи:

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  

Вихід буде:

4
6

1
Або більш коротко:dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
frnhr

5

Я думаю, що найефективнішим способом пошуку дублікатів у списку є:

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

Тут використовуються Counterвсі елементи та всі унікальні елементи. Віднімання першого з другим не омине лише дублікатів.


4

Трохи запізнюємось, але може бути корисним для деяких. Для великого списку я виявив, що це працює для мене.

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]

Показує просто і всі дублікати та зберігає порядок.


3

Дуже простий і швидкий спосіб знайти дупи з однією ітерацією в Python:

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

testListDict = {}

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

print testListDict

Вихід буде таким:

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

Про це та інше в моєму блозі http://www.howtoprogramwithpython.com


3

Я вступаю набагато пізніше в цю дискусію. Хоча я хотів би вирішити цю проблему одним лайнером. Тому що в цьому і є принадність Пітона. якщо ми просто хочемо передати дублікати до окремого списку (або будь-якої колекції), я б запропонував зробити так, як нижче. Скажімо, у нас є дубльований список, який ми можемо називати "цільовим"

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

Тепер, якщо ми хочемо отримати дублікати, ми можемо використовувати один вкладиш, як показано нижче:

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

Цей код додасть дублювані записи як ключові та зараховуватиметься як значення у словник "дублікати". Словник "дублікат" виглядатиме, як показано нижче:

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

Якщо ви просто хочете, щоб усі записи з дублікатами були одними лише в списку, його знову набагато коротший код:

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

Вихід буде:

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

Це чудово працює у версіях python 2.7.x +


3

Python 3.8 однолінійний, якщо вам не все одно писати власний алгоритм або використовувати бібліотеки:

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)

Друкує елемент і рахує:

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

groupby бере функцію групування, щоб ви могли визначати свої групи різними способами та повертати додаткові Tuple поля за потребою.

groupby лінивий, тому він не повинен бути надто повільним.


2

Деякі інші тести. Звичайно робити ...

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

... занадто дорого. Це приблизно в 500 разів швидше (тим довший масив дає кращі результати) використовувати наступний остаточний метод:

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

Всього 2 петлі, не дуже затратні l.count()операції.

Ось код для порівняння методів, наприклад. Код нижче, ось вихід:

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

Код тестування:

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

Спосіб 1:

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

Пояснення: [val для idx, val в перерахуванні (вхід_ список), якщо val в input_list [idx + 1:]] - це розуміння списку, яке повертає елемент, якщо той самий елемент присутній із його поточного положення, у списку, індекс .

Приклад: input_list = [42,31,42,31,3,31,31,5,6,6,6,6,6,7,42]

починаючи з першого елемента в списку 42, з індексом 0, він перевіряє, чи присутній елемент 42 в input_list [1:] (тобто від індексу 1 до кінця списку) Оскільки 42 присутній у input_list [1:] , вона повернеться 42.

Потім він переходить до наступного елемента 31 з індексом 1 і перевіряє, чи присутній елемент 31 у input_list [2:] (тобто від індексу 2 до кінця списку), оскільки 31 присутній у input_list [2:], воно повернеться 31.

аналогічно він проходить через усі елементи в списку і повертає лише повторювані / дублюючі елементи в список.

Тоді, оскільки у нас є дублікати, у списку нам потрібно вибрати один з кожного дубліката, тобто видалити дублікат серед дублікатів, і для цього ми викликаємо вбудований іменем set () пітон, і він видаляє дублікати,

Тоді нам залишається набір, але не список, і, отже, для перетворення з набору в список, ми використовуємо, вводимо клавіші, list (), і це перетворює набір елементів у список.

Спосіб 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

Пояснення: Тут ми для початку створимо два порожні списки. Потім продовжуйте проїжджати через усі елементи списку, щоб побачити, чи існує він у temp_list (спочатку порожній). Якщо його немає в temp_list, тоді ми додаємо його до temp_list, використовуючи метод додавання .

Якщо він вже існує в temp_list, це означає, що поточний елемент списку є дублікатом, і тому нам потрібно додати його до dupe_list методом додавання .


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]

Ви, як правило, видаляєте дублікати, перетворюючи на set ( clean_list), потім повторюйте raw_list, видаляючи кожен itemіз чистого списку для появи в raw_list. Якщо itemйого не знайдено, піднятий ValueErrorвиняток перехоплюється та itemдодається доduplicated_items списку.

Якщо потрібен індекс дублюваних елементів, просто enumerateсписок і пограйте разом з індексом. ( for index, item in enumerate(raw_list):) яка швидша і оптимізована для великих списків (наприклад, тисяч + елементів)


2

використання list.count()методу в списку, щоб з'ясувати дублюючі елементи даного списку

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)

простий спосіб знайти дублікати елементів у списку за допомогою функції count
Ravikiran D

2

однолінійний, для розваги та там, де потрібна одна заява.

(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

Рішення з однієї лінії:

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

1

Тут є багато відповідей, але я думаю, що це порівняно дуже читабельний і простий для розуміння підхід:

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)

Примітки:

  • Якщо ви хочете зберегти кількість дублювань, позбавтеся від анотації "встановити" внизу, щоб отримати повний список
  • Якщо ви віддаєте перевагу використанню генераторів, замініть duplicates.append (x) на вихід x та оператор return в нижній частині (ви можете вказати, щоб встановити пізніше)

1

Ось швидкий генератор, який використовує dict для зберігання кожного елемента як ключа з булевим значенням для перевірки, чи дублікат вже отриманий елемент.

Для списків з усіма елементами, які є хешируемими типами:

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]

Для списків, які можуть містити списки:

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

Ви повертаєте набір, а не список, як вимагали. Набір містить лише унікальні елементи, тому твердження if насправді не є необхідним. Ви також повинні пояснити, яка перевага від вашого рішення порівняно з іншими.
clemens


0

саме так мені довелося це зробити, тому що я кинув виклик собі не використовувати інші методи:

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

тому ваш зразок працює як:

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

3
Це не те, чого хотіла ОП. Він хотів список дублікатів, а не список із видаленими дублікатами. Щоб зробити список із видаленими дублікатами, я б запропонував duplist = list(set(a)).
zondo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.