Как найти дубликаты в списке и создать еще один список с ними?

439

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

MFB
источник
1
Вы хотите дубликаты один раз или каждый раз, когда их снова видят?
moooeeeep
Я думаю, что здесь ответили с большей эффективностью. stackoverflow.com/a/642919/1748045 пересечение является встроенным методом набора и должно делать именно то, что требуется
Том Смит

Ответы:

546

Для удаления дубликатов используйте 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()метод set всегда возвращает 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

Если элементы списка не могут быть хешируемыми, вы не можете использовать наборы / dicts и вынуждены прибегать к решению с квадратичным временем (сравните каждый с каждым). Например:

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).
Георг
3
@Hugo, чтобы увидеть список дубликатов, нам просто нужно создать новый список с именем dup и добавить оператор else. Например:dup = [] else: dup.append(x)
Крис Нильсен
4
@oxeimon: вы, вероятно, получили это, но вы печатаете, вызывается в круглых скобках в Python 3print()
Моберг,
4
преобразование вашего ответа для set (), чтобы получить только дубликаты. seen = set()тогдаdupe = set(x for x in a if x in seen or seen.add(x))
Ta946
2
Для Python 3.x: печатать ([элемент для элемента, считать в коллекциях. Counter (a) .items (), если count> 1])
kibitzforu
328
>>> 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 () анализирует список заново, поэтому не используйте для больших списков.
Данукер
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
moooeeeep
источник
6
Просто любопытно - какова цель seen_add = seen.add здесь?
Роб
3
@Rob Таким образом, вы просто вызываете функцию, которую вы искали один раз раньше. В противном случае вам нужно будет искать (словарный запрос) функцию-член addкаждый раз, когда потребуется вставка.
moooeeeep
проверил мои собственные данные и% time Ipython: ваш метод выглядит быстрее всего на тесте, НО: «Самый медленный прогон занимал 4,34 раза дольше самого быстрого. Это может означать, что промежуточный результат кэшируется»
Joop
1
@moooeeeep, я добавил еще одну версию в твой скрипт, чтобы ты попробовал :) Также попробуй, pypyесли она тебе пригодится и ты собираешься на скорость.
Джон Ла Рой
@JohnLaRooy Хорошее улучшение производительности! Интересно, что когда я использовал pypy для оценки результатов, подход на основе счетчиков значительно улучшился.
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 представляет время, поэтому меньшее значение означает лучшее. Он также построил log-log, так что широкий диапазон значений можно лучше визуализировать:

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

Удаляя 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.

MSeifert
источник
1
Я собираюсь высунуть свою шею здесь и предложить написать на заказ библиотеку для работы на С, а не на Python, вероятно, не тот дух ответа, который искали, - но это законный подход! Мне нравится ширина ответа и графическое отображение результатов - очень приятно видеть, что они сходятся, заставляет задуматься, не пересекаются ли они, когда входы увеличиваются дальше! Вопрос: каков результат с в основном отсортированными списками, в отличие от полностью случайных списков?
F1Rumors
30

Я сталкивался с этим вопросом, глядя на что-то связанное - и удивляюсь, почему никто не предложил решение на основе генератора? Решение этой проблемы будет:

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

Я был обеспокоен масштабируемостью, поэтому протестировал несколько подходов, включая наивные элементы, которые хорошо работают в небольших списках, но ужасно масштабируются по мере увеличения списков (примечание - было бы лучше использовать timeit, но это иллюстративно).

Я включил @moooeeeep для сравнения (он впечатляюще быстр: самый быстрый, если входной список полностью случайный) и подход itertools, который еще быстрее снова для большинства отсортированных списков ... Теперь включает в себя подход панд из @firelynx - медленно, но не ужасно так и просто. Примечание. Подход сортировки / тройника / почтового индекса на моей машине всегда самый быстрый для больших, в основном, упорядоченных списков, 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

Результаты теста «все дубликаты» были согласованы, и в этом массиве были обнаружены «сначала» дубликаты, а затем «все дубликаты»:

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  
F1Rumors
источник
@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 подход и работать в полностью перемешанном списке, а также отсортированный список. Это происходит потому , что родной timsort используется 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'
>>> 

В более ранней версии вы можете использовать вместо этого обычный dict:

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

Я бы сделал это с пандами, потому что я часто использую панд

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]

Возможно, он не очень эффективен, но в нем меньше кода, чем во многих других ответах, поэтому я решил внести свой вклад

firelynx
источник
4
Также обратите внимание, что pandas содержит встроенную функцию дубликатов pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken
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)
Chetan_Vasudevan
источник
1
Это не приводит к списку дубликатов (или первоначальному списку), но приводит к списку всех уникальных элементов (или исходному списку). Что бы кто-то сделал после того, как они закончили формирование списка «dup»?
gameCoder95
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]
HenryDev
источник
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
alfasin
источник
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]

Показывает как раз и все дубликаты и сохраняет порядок.

user3109122
источник
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

Я вступаю намного позже в эту дискуссию. Хотя я бы хотел решить эту проблему с одним вкладышем. Потому что это прелесть Python. если мы просто хотим поместить дубликаты в отдельный список (или любую коллекцию), я бы предложил сделать это следующим образом. Скажем, у нас есть дублированный список, который мы можем назвать «целевым»

    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 ленивый, поэтому он не должен быть слишком медленным

yǝsʞǝla
источник
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()
sergzach
источник
2

Способ 1:

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

Объяснение: [val для idx, val в enumerate (input_list), если 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.

Точно так же он проходит через все элементы в списке и возвращает в список только повторяющиеся / повторяющиеся элементы.

Затем, поскольку у нас есть дубликаты, в списке нам нужно выбрать один из каждого дубликата, то есть удалить дубликаты среди дубликатов, и для этого мы вызываем встроенный в python named с именем set (), и он удаляет дубликаты,

Затем у нас остается набор, но не список, и, следовательно, для преобразования из набора в список мы используем typecasting, 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, используя метод append .

Если он уже существует в temp_list, это означает, что текущий элемент списка является дубликатом, и, следовательно, нам нужно добавить его в dupe_list с помощью метода append .

S471
источник
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)
Wizr
источник
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])
ytpillai
источник
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) на yield x и оператор return внизу (вы можете привести его к установке позже).
tvt173
источник
1

Вот быстрый генератор, который использует dict для хранения каждого элемента в качестве ключа с логическим значением для проверки, был ли дублированный элемент уже получен.

Для списков со всеми элементами, которые являются типами 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]

Для списков, которые могут содержать списки:

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]))
ASHISH RANJAN
источник
Вы возвращаете набор, а не список в соответствии с запросом. Набор содержит только уникальные элементы, поэтому оператор if не является действительно необходимым. Вы также должны объяснить, в чем преимущество вашего решения по сравнению с другим.
Клеменс
1

При использовании 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] 
Андреас Профус
источник
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