Làm cách nào để tìm các bản sao trong danh sách và tạo danh sách khác với chúng?


437

Làm cách nào tôi có thể tìm thấy các bản sao trong danh sách Python và tạo một danh sách trùng lặp khác? Danh sách chỉ chứa số nguyên.



1
Bạn có muốn các bản sao một lần, hoặc mỗi lần nó được nhìn thấy lại không?
moooeeeep

Tôi nghĩ rằng điều này đã được trả lời với hiệu quả nhiều mmore ở đây. Giao lộ stackoverflow.com/a/642919/1748045 là một phương thức được thiết lập sẵn và nên thực hiện chính xác những gì được yêu cầu
Tom Smith

Câu trả lời:


544

Để loại bỏ trùng lặp sử dụng set(a). Để in các bản sao, một cái gì đó như:

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]

Lưu ý rằng Counterkhông đặc biệt hiệu quả ( thời gian ) và có thể quá mức cần thiết ở đây. setsẽ thực hiện tốt hơn. Mã này tính toán một danh sách các yếu tố duy nhất theo thứ tự nguồn:

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

hoặc, chính xác hơn:

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

Tôi không đề xuất kiểu thứ hai, vì không rõ ràng những gì not seen.add(x)đang làm ( add()phương thức thiết lập luôn trả về None, do đó cần phải có not).

Để tính toán danh sách các phần tử trùng lặp không có thư viện:

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

Nếu các thành phần danh sách không thể băm được, bạn không thể sử dụng bộ / dicts và phải sử dụng giải pháp thời gian bậc hai (so sánh từng bộ với nhau). Ví dụ:

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: Tôi đoán vậy O(n), bởi vì nó chỉ lặp lại danh sách một lần và thiết lập tra cứu O(1).
georg

3
@Hugo, để xem danh sách trùng lặp, chúng ta chỉ cần tạo một danh sách mới gọi là dup và thêm một câu lệnh khác. Ví dụ:dup = [] else: dup.append(x)
Chris Nielsen

4
@oxeimon: bạn có thể đã nhận được cái này, nhưng bạn in được gọi bằng dấu ngoặc đơn trong python 3print()
Moberg

4
chuyển đổi câu trả lời của bạn cho set () để chỉ nhận các bản sao. seen = set()sau đódupe = set(x for x in a if x in seen or seen.add(x))
Ta946

2
Đối với Python 3.x: print ([mục cho mục, tính trong bộ sưu tập. Bộ đếm (a) .items () nếu tính> 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
Có bất kỳ lý do bạn sử dụng một sự hiểu biết danh sách thay vì một sự hiểu biết máy phát điện?

64
Thật vậy, một giải pháp đơn giản, nhưng độ phức tạp được bình phương vì mỗi lần đếm () phân tích lại danh sách một lần nữa, vì vậy đừng sử dụng cho các danh sách lớn.
danuker

4
@JohnJ, sắp xếp bong bóng cũng đơn giản và hiệu quả. Điều đó không có nghĩa là chúng ta nên sử dụng nó!
John La Rooy

@JohnLaRooy Điều đó thực sự có nghĩa là chúng ta không nên sử dụng nó, bởi vì hầu như luôn luôn có một cách hiệu quả hơn (và đơn giản hơn) để sắp xếp.
mấtsoul29

1
@watsonic: "Công tắc đơn giản" của bạn không thể giảm độ phức tạp thời gian từ bậc hai sang bình phương trong trường hợp chung. Thay thế lbằng set(l)chỉ làm giảm độ phức tạp thời gian trong trường hợp xấu nhất và do đó không có gì để giải quyết các mối quan tâm hiệu quả quy mô lớn hơn với câu trả lời này. Có lẽ nó không đơn giản như vậy. Nói tóm lại, đừng làm điều này.
Cecil Curry

82

Bạn không cần đếm, chỉ là liệu món đồ đó có được nhìn thấy trước đó hay không. Điều chỉnh câu trả lời cho vấn đề này:

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]

Chỉ trong trường hợp vấn đề tốc độ, đây là một số thời gian:

# 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

Đây là kết quả: (hoàn thành tốt @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

Thật thú vị, bên cạnh thời gian, chính thứ hạng cũng thay đổi một chút khi sử dụng pypy. Thú vị nhất, cách tiếp cận dựa trên Counter có lợi rất nhiều từ việc tối ưu hóa pypy, trong khi phương pháp lưu trữ phương thức mà tôi đã đề xuất dường như hầu như không có hiệu quả.

$ 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

Một cách rõ ràng hiệu ứng này có liên quan đến "sự trùng lặp" của dữ liệu đầu vào. Tôi đã thiết lập l = [random.randrange(1000000) for i in xrange(10000)]và nhận được những kết quả này:

$ 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
Chỉ tò mò - mục đích của saw_add = saw.add ở đây là gì?
Cướp

3
@Rob Cách này bạn chỉ cần gọi chức năng bạn đã tìm kiếm một lần trước đây. Nếu không, bạn sẽ cần tra cứu (truy vấn từ điển) chức năng thành viên addmỗi khi cần chèn.
moooeeeep

đã kiểm tra dữ liệu của riêng tôi và% thời gian của Ipython phương pháp của bạn trông nhanh nhất trong bài kiểm tra NHƯNG: "Chạy chậm nhất mất 4,34 lần so với nhanh nhất. Điều này có thể có nghĩa là kết quả trung gian đang được lưu trong bộ nhớ cache"
Bắt đầu

1
@moooeeeep, tôi đã thêm một phiên bản khác vào kịch bản của bạn để bạn thử :) Ngoài ra, hãy thử pypynếu bạn có nó tiện dụng và sẽ tăng tốc.
John La Rooy

@JohnLaRooy Cải thiện tốt về hiệu suất! Thật thú vị, khi tôi sử dụng pypy để đánh giá kết quả, phương pháp dựa trên Counter cải thiện đáng kể.
moooeeeep

42

Bạn có thể sử dụng iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

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

hoặc nếu bạn chỉ muốn một trong mỗi bản sao thì có thể kết hợp với iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen

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

Nó cũng có thể xử lý các yếu tố không thể xóa được (tuy nhiên với chi phí hiệu năng):

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

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

Đó là điều mà chỉ một vài cách tiếp cận khác ở đây có thể xử lý.

Điểm chuẩn

Tôi đã làm một điểm chuẩn nhanh chứa hầu hết (nhưng không phải tất cả) các phương pháp được đề cập ở đây.

Điểm chuẩn đầu tiên chỉ bao gồm một phạm vi nhỏ độ dài danh sách vì một số cách tiếp cận có O(n**2)hành vi.

Trong các biểu đồ, trục y biểu thị thời gian, vì vậy giá trị thấp hơn có nghĩa là tốt hơn. Nó cũng được vẽ sơ đồ log-log để phạm vi rộng của các giá trị có thể được hình dung rõ hơn:

nhập mô tả hình ảnh ở đây

Xóa các O(n**2)cách tiếp cận tôi đã thực hiện một điểm chuẩn khác lên tới nửa triệu phần tử trong danh sách:

nhập mô tả hình ảnh ở đây

Như bạn có thể thấy iteration_utilities.duplicatescách tiếp cận nhanh hơn bất kỳ cách tiếp cận nào khác và thậm chí chuỗi unique_everseen(duplicates(...))còn nhanh hơn hoặc nhanh hơn so với các cách tiếp cận khác.

Một điều thú vị nữa cần lưu ý ở đây là cách tiếp cận của gấu trúc rất chậm đối với các danh sách nhỏ nhưng có thể dễ dàng cạnh tranh cho các danh sách dài hơn.

Tuy nhiên, vì các điểm chuẩn này cho thấy hầu hết các cách tiếp cận thực hiện gần như bằng nhau, do đó, không có vấn đề nào được sử dụng (ngoại trừ 3 phương pháp có O(n**2)thời gian chạy).

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

Điểm chuẩn 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()

Điểm chuẩn 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()

Khước từ

1 Đây là từ thư viện của bên thứ ba mà tôi đã viết : iteration_utilities.


1
Tôi sẽ thò cổ ra đây và đề nghị viết thư viện bespoke để thực hiện công việc trong C chứ không phải Python có lẽ không phải là tinh thần của câu trả lời đang được tìm kiếm - nhưng đó là một cách tiếp cận hợp pháp! Tôi thích độ rộng của câu trả lời và hiển thị đồ họa của kết quả - rất tuyệt khi thấy chúng được hội tụ, khiến tôi tự hỏi liệu chúng có bao giờ giao nhau khi đầu vào tăng hơn nữa không! Câu hỏi: kết quả với các danh sách được sắp xếp chủ yếu trái ngược với danh sách hoàn toàn ngẫu nhiên là gì?
F1Rumors

30

Tôi bắt gặp câu hỏi này trong khi tìm kiếm một cái gì đó liên quan - và tự hỏi tại sao không ai đưa ra giải pháp dựa trên máy phát điện? Giải quyết vấn đề này sẽ là:

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

Tôi quan tâm đến khả năng mở rộng, vì vậy đã thử nghiệm một số cách tiếp cận, bao gồm các mục ngây thơ hoạt động tốt trên các danh sách nhỏ, nhưng quy mô khủng khiếp khi danh sách trở nên lớn hơn (lưu ý - sẽ tốt hơn khi sử dụng thời gian, nhưng điều này mang tính minh họa).

Tôi đã đưa vào @moooeeeep để so sánh (nhanh chóng ấn tượng: nhanh nhất nếu danh sách đầu vào hoàn toàn ngẫu nhiên) và cách tiếp cận itertools thậm chí còn nhanh hơn nữa cho các danh sách được sắp xếp chủ yếu ... Bây giờ bao gồm cách tiếp cận gấu trúc từ @firelynx - chậm, nhưng không khủng khiếp như vậy, và đơn giản. Lưu ý - cách tiếp cận sắp xếp / tee / zip luôn nhanh nhất trên máy của tôi đối với các danh sách lớn được sắp xếp theo thứ tự, moooeeeep là nhanh nhất cho danh sách được xáo trộn, nhưng số dặm của bạn có thể thay đổi.

Ưu điểm

  • rất nhanh chóng đơn giản để kiểm tra các bản sao 'bất kỳ' bằng cách sử dụng cùng một mã

Giả định

  • Các bản sao chỉ được báo cáo một lần
  • Thứ tự trùng lặp không cần phải được bảo tồn
  • Bản sao có thể ở bất cứ đâu trong danh sách

Giải pháp nhanh nhất, 1m mục:

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

Phương pháp thử nghiệm

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

Các kết quả cho thử nghiệm 'tất cả các bản sao' đều nhất quán, tìm các bản sao "đầu tiên" sau đó "tất cả" trong mảng này:

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  

Khi các danh sách được xáo trộn đầu tiên, giá của loại sắp xếp trở nên rõ ràng - hiệu quả giảm đáng kể và cách tiếp cận @moooeeeep chiếm ưu thế, với các cách tiếp cận tập hợp và chính tả là tương tự nhưng người thực hiện bên cho thuê:

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 - hãy quan tâm để xem quan điểm của bạn về phương pháp ifilter / izip / tee.
F1Rumors

1
Câu trả lời này cực kỳ tốt. Tôi không hiểu rằng nó không có nhiều điểm hơn cho các giải thích và kiểm tra rất hữu ích cho những người cần nó.
dlewin

1
sắp xếp của python là O (n) khi chỉ có một mặt hàng bị lỗi. Bạn nên tính random.shuffle(c)đến điều đó. Ngoài ra, tôi không thể sao chép kết quả của bạn khi chạy tập lệnh chưa được thay đổi (thứ tự hoàn toàn khác nhau), vì vậy có lẽ nó cũng phụ thuộc vào CPU.
John La Rooy

Cảm ơn bạn @ John-La-Rooy, quan sát sắc sảo trên CPU / máy cục bộ đang bị ảnh hưởng - vì vậy tôi nên sửa đổi mục YYMV . Việc sử dụng sắp xếp O (n) là có chủ ý: phần tử trùng lặp được chèn vào các vị trí khác nhau để xem tác động của cách tiếp cận nếu có một bản sao duy nhất ở vị trí tốt (bắt đầu danh sách) hoặc xấu (cuối danh sách) với các vị trí này cách tiếp cận. Tôi đã xem xét một danh sách ngẫu nhiên - ví dụ Random.shuffle - nhưng quyết định rằng sẽ chỉ hợp lý nếu tôi thực hiện nhiều lần chạy hơn! Tôi sẽ phải trả lại và điểm chuẩn tương đương nhiều lần chạy / xáo trộn và xem tác động là gì.
F1Rumors

Được sửa đổi để bao gồm cách tiếp cận gấu trúc @firelynx & để chạy trên danh sách được xáo trộn hoàn toàn cũng như danh sách được sắp xếp. Điều này là do bộ đếm thời gian riêng được sử dụng bởi Python rất tệ đối với dữ liệu được sắp xếp chủ yếu (trường hợp tốt nhất) và danh sách bị xáo trộn là trường hợp xấu nhất của nó - làm rung chuyển kết quả.
F1Rumors

13

Sử dụng gấu trúc:

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

bộ sưu tập. Bộ đếm là mới trong 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'
>>> 

Trong phiên bản cũ hơn, bạn có thể sử dụng một lệnh chính quy thay thế:

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

Đây là một giải pháp gọn gàng và súc tích -

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

li = list(set(li))

Danh sách ban đầu bị mất, mặc dù. Điều này có thể được khắc phục bằng cách sao chép nội dung vào danh sách khác - temp = li [:]
Nikhil Mitchhu

3
Đó là một bài tập khó chịu trong danh sách lớn - loại bỏ các yếu tố khỏi danh sách khá tốn kém!
F1Rumors

7

Nếu không chuyển đổi thành danh sách và có lẽ cách đơn giản nhất sẽ là một cái gì đó như dưới đây. Điều này có thể hữu ích trong một cuộc phỏng vấn khi họ yêu cầu không sử dụng các bộ

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

======= khác để có 2 danh sách riêng biệt các giá trị duy nhất và giá trị trùng lặp

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
Điều này không dẫn đến một danh sách các bản sao của một (hoặc danh sách gốc), điều này dẫn đến một danh sách tất cả các yếu tố duy nhất của một (hoặc danh sách ban đầu). Ai đó sẽ làm gì sau khi họ hoàn thành việc tạo danh sách "dup"?
gameCoder95

6

Tôi sẽ làm điều này với gấu trúc, vì tôi sử dụng gấu trúc rất nhiều

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

Tặng

[3,6]

Có lẽ không hiệu quả lắm, nhưng chắc chắn là ít mã hơn rất nhiều câu trả lời khác, vì vậy tôi nghĩ rằng mình sẽ đóng góp


3
Cũng lưu ý rằng gấu trúc có chức năng sao chép tích hợp pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken

6

ví dụ thứ ba của câu trả lời được chấp nhận đưa ra một câu trả lời sai và không cố gắng đưa ra các bản sao. Đây là phiên bản chính xác:

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

Làm thế nào về đơn giản lặp qua từng phần tử trong danh sách bằng cách kiểm tra số lần xuất hiện, sau đó thêm chúng vào một bộ sau đó sẽ in các bản sao. Hy vọng điều này sẽ giúp ai đó ngoài kia.

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

Chúng tôi có thể sử dụng itertools.groupbyđể tìm tất cả các mục có dups:

from itertools import groupby

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

Đầu ra sẽ là:

4
6

1
Hoặc chính xác hơn:dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
frnhr

5

Tôi đoán cách hiệu quả nhất để tìm các bản sao trong danh sách là:

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

Nó sử dụng Countertất cả các yếu tố và tất cả các yếu tố độc đáo. Trừ đi cái đầu tiên với cái thứ hai sẽ chỉ còn lại các bản sao.


4

Một chút muộn, nhưng có thể hữu ích cho một số. Đối với một danh sách lớn, tôi thấy điều này làm việc cho tôi.

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]

Hiển thị chỉ và tất cả các bản sao và bảo tồn trật tự.


3

Cách rất đơn giản và nhanh chóng để tìm bản sao với một lần lặp trong Python là:

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

testListDict = {}

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

print testListDict

Đầu ra sẽ như sau:

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

Điều này và hơn thế nữa trong blog của tôi http://www.howtoprogramwithpython.com


3

Tôi đang tham gia nhiều vào cuối cuộc thảo luận này. Mặc dù, tôi muốn giải quyết vấn đề này với một lớp lót. Bởi vì đó là sự quyến rũ của Python. nếu chúng tôi chỉ muốn đưa các bản sao vào một danh sách riêng (hoặc bất kỳ bộ sưu tập nào), tôi sẽ đề nghị làm như dưới đây. Chúng tôi có một danh sách trùng lặp mà chúng tôi có thể gọi là 'mục tiêu'

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

Bây giờ nếu chúng ta muốn có được các bản sao, chúng ta có thể sử dụng một lớp lót như sau:

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

Mã này sẽ đặt các bản ghi trùng lặp làm khóa và được tính là giá trị trong từ điển 'trùng lặp'. Từ điển 'trùng lặp' sẽ giống như dưới đây:

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

Nếu bạn chỉ muốn tất cả các bản ghi với các bản sao một mình trong một danh sách, mã lại ngắn hơn nhiều:

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

Đầu ra sẽ là:

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

Điều này hoạt động hoàn hảo trong các phiên bản python 2.7.x +


3

Python 3,8 one-liner nếu bạn không muốn viết thuật toán của riêng mình hoặc sử dụng các thư viện:

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)

In mục và đếm:

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

groupbycó chức năng nhóm để bạn có thể xác định các nhóm của mình theo các cách khác nhau và trả về các Tupletrường bổ sung khi cần.

groupby là lười biếng nên không nên quá chậm.


2

Một số xét nghiệm khác. Tất nhiên là phải làm ...

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

... Quá tốn kém. Nhanh hơn khoảng 500 lần (mảng dài hơn cho kết quả tốt hơn) để sử dụng phương pháp cuối cùng tiếp theo:

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

Chỉ có 2 vòng, không tốn kém lắm l.count() hoạt động .

Dưới đây là một mã để so sánh các phương pháp chẳng hạn. Mã dưới đây, đây là đầu ra:

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

Mã kiểm tra:

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

Cách 1:

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

Giải trình: [val cho idx, val in enum Cả (input_list) nếu val trong input_list [idx + 1:]] là một sự hiểu biết danh sách, trả về một phần tử, nếu cùng một phần tử xuất hiện từ vị trí hiện tại của nó, trong danh sách, chỉ mục .

Ví dụ: input_list = [42,31,42,31,3,31,31,5,6,6,6,6,6,7,42]

bắt đầu với phần tử đầu tiên trong danh sách, 42, với chỉ số 0, nó kiểm tra xem phần tử 42, có trong input_list [1:] (tức là, từ chỉ mục 1 đến cuối danh sách) Bởi vì 42 có trong input_list [1:] , nó sẽ trả về 42.

Sau đó, nó đi đến phần tử tiếp theo 31, với chỉ mục 1 và kiểm tra xem phần tử 31 có trong input_list [2:] (tức là, từ chỉ mục 2 đến cuối danh sách), bởi vì 31 có trong input_list [2:], nó sẽ trở lại 31.

tương tự, nó đi qua tất cả các phần tử trong danh sách và sẽ chỉ trả về các phần tử lặp lại / trùng lặp vào một danh sách.

Sau đó, vì chúng ta có các bản sao, trong một danh sách, chúng ta cần chọn một trong mỗi bản sao, nghĩa là loại bỏ trùng lặp giữa các bản sao và để làm như vậy, chúng ta gọi một python có tên là set () và nó sẽ loại bỏ các bản sao,

Sau đó, chúng tôi chỉ còn lại một tập hợp, nhưng không phải là một danh sách và do đó để chuyển đổi từ một tập hợp thành danh sách, chúng tôi sử dụng, typecasting, list () và chuyển đổi tập hợp các thành phần thành một danh sách.

Cách 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

Giải thích: Ở đây chúng tôi tạo hai danh sách trống, để bắt đầu. Sau đó tiếp tục duyệt qua tất cả các thành phần của danh sách, để xem liệu nó có tồn tại trong temp_list (ban đầu trống không). Nếu nó không có trong temp_list, thì chúng ta thêm nó vào temp_list, sử dụng phương thức chắp thêm .

Nếu nó đã tồn tại trong temp_list, điều đó có nghĩa là, phần tử hiện tại của danh sách là một bản sao và do đó chúng ta cần thêm nó vào dupe_list bằng phương thức chắp thêm .


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]

Về cơ bản, bạn loại bỏ các bản sao bằng cách chuyển đổi thành set ( clean_list), sau đó lặp lại raw_list, trong khi xóa từng bản itemtrong danh sách sạch để xảy ra raw_list. Nếu itemkhông được tìm thấy, ValueErrorNgoại lệ được nâng lên sẽ bị bắt và itemđược thêm vào duplicated_itemsdanh sách.

Nếu chỉ mục của các mục trùng lặp là cần thiết, chỉ cần enumeratedanh sách và chơi xung quanh với chỉ mục. ( for index, item in enumerate(raw_list):) nhanh hơn và được tối ưu hóa cho các danh sách lớn (như hàng ngàn + phần tử)


2

sử dụng list.count()phương thức trong danh sách để tìm ra các phần tử trùng lặp của danh sách đã cho

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)

cách đơn giản để tìm các phần tử trùng lặp trong danh sách bằng hàm đếm
Ravikiran D

2

một lót, cho vui, và khi cần một tuyên bố.

(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

Giải pháp một dòng:

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

1

Có rất nhiều câu trả lời ở đây, nhưng tôi nghĩ rằng đây là một cách tiếp cận rất dễ đọc và dễ hiểu:

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)

Ghi chú:

  • Nếu bạn muốn duy trì số lượng trùng lặp, hãy loại bỏ các diễn viên thành 'set' ở dưới cùng để có danh sách đầy đủ
  • Nếu bạn thích sử dụng trình tạo, thay thế trùng lặp.append (x) bằng suất x và câu lệnh return ở phía dưới (bạn có thể truyền để đặt sau)

1

Đây là một trình tạo nhanh sử dụng một lệnh để lưu trữ từng phần tử làm khóa với giá trị boolean để kiểm tra xem mục trùng lặp đã được mang lại chưa.

Đối với danh sách có tất cả các yếu tố là loại có thể băm:

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]

Đối với danh sách có thể chứa danh sách:

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

Bạn trả lại một bộ và không phải là một danh sách theo yêu cầu. Một tập hợp chỉ chứa các phần tử duy nhất, do đó, câu lệnh if không thực sự cần thiết. Bạn cũng nên giải thích, lợi thế từ giải pháp của bạn so với giải pháp khác là gì.
clemens

1

Khi sử dụng 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

đây là cách tôi phải làm vì tôi đã thử thách bản thân không sử dụng các phương pháp khác:

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

vì vậy mẫu của bạn hoạt động như:

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

3
Đây không phải là những gì OP muốn. Anh ta muốn một danh sách các bản sao, không phải một danh sách với các bản sao được loại bỏ. Để tạo một danh sách với các bản sao được loại bỏ, tôi sẽ đề nghị duplist = list(set(a)).
zondo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.