Hướng dẫn non duplicate list python - python danh sách không trùng lặp


Tìm hiểu làm thế nào để loại bỏ các bản sao khỏi danh sách trong Python.


Thí dụ

Xóa bất kỳ bản sao nào khỏi danh sách:

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)
mylist = list(dict.fromkeys(mylist))
print(mylist)

Hãy tự mình thử »

Ví dụ giải thích

Đầu tiên chúng tôi có một danh sách có chứa các bản sao:

Một danh sách với các bản sao

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)
mylist = list(dict.fromkeys(mylist))
print(mylist)

Tạo một từ điển, sử dụng các mục danh sách làm khóa. Điều này sẽ tự động xóa bất kỳ bản sao nào vì từ điển không thể có các khóa trùng lặp.

Tạo một từ điển

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)
mylist = list(dict.fromkeys(mylist))
print(mylist)

Hãy tự mình thử »

Ví dụ giải thích

Đầu tiên chúng tôi có một danh sách có chứa các bản sao:
mylist = list(dict.fromkeys(mylist))
print(mylist)

Một danh sách với các bản sao

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)

Tạo một từ điển, sử dụng các mục danh sách làm khóa. Điều này sẽ tự động xóa bất kỳ bản sao nào vì từ điển không thể có các khóa trùng lặp.

Tạo một từ điển
mylist = list(dict.fromkeys(mylist))
print(mylist)



Sau đó, chuyển đổi từ điển trở lại thành một danh sách:

Chuyển đổi thành một danh sách

Thí dụ

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)
  return list(dict.fromkeys(x))

Bây giờ chúng tôi có một danh sách mà không có bất kỳ bản sao nào, và nó có cùng thứ tự như danh sách ban đầu.

In danh sách để chứng minh kết quả

Hãy tự mình thử »

Ví dụ giải thích

Đầu tiên chúng tôi có một danh sách có chứa các bản sao:

Sau đó, chuyển đổi từ điển trở lại thành một danh sách:

Chuyển đổi thành một danh sách
  return list(dict.fromkeys(x))

Bây giờ chúng tôi có một danh sách mà không có bất kỳ bản sao nào, và nó có cùng thứ tự như danh sách ban đầu.

In danh sách để chứng minh kết quả

In danh sách

Tạo một từ điển

Chuyển đổi thành một danh sách
  return list(
dict.fromkeys(x))

Bây giờ chúng tôi có một danh sách mà không có bất kỳ bản sao nào, và nó có cùng thứ tự như danh sách ban đầu.

In danh sách để chứng minh kết quả

In danh sách

Ví dụ giải thích

Chuyển đổi thành một danh sách
  return
list(dict.fromkeys(x))

Bây giờ chúng tôi có một danh sách mà không có bất kỳ bản sao nào, và nó có cùng thứ tự như danh sách ban đầu.

In danh sách để chứng minh kết quả

In danh sách

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)

Chuyển đổi thành một danh sách
 
return list(dict.fromkeys(x))

Bây giờ chúng tôi có một danh sách mà không có bất kỳ bản sao nào, và nó có cùng thứ tự như danh sách ban đầu.

In danh sách để chứng minh kết quả

In danh sách

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)

Tạo một chức năng
  return list(dict.fromkeys(x))
mylist = my_function(["a", "b", "a", "c", "c"])print(mylist)

Nếu bạn muốn có một chức năng nơi bạn có thể gửi danh sách của mình và lấy lại chúng mà không cần sao chép, bạn có thể tạo một chức năng và chèn mã từ ví dụ trên.

def my_function (x): & nbsp; Danh sách trả lại (Dict.FromKeys (x))

Chuyển đổi thành một danh sách
  return list(dict.fromkeys(x))

mylist = ["a", "b", "a", "c", "c"] mylist = list (dict.fromKeys (mylist)) in (mylist)

print(mylist)



Nếu sử dụng gói bên thứ ba sẽ ổn thì bạn có thể sử dụng iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen
>>> l = [{'a': 123}, {'b': 123}, {'a': 123}]
>>> list(unique_everseen(l))
[{'a': 123}, {'b': 123}]

Nó bảo tồn thứ tự của Danh sách ban đầu và UT cũng có thể xử lý các mục không thể không có được như từ điển bằng cách rơi trở lại trên thuật toán chậm hơn (O(n*m) trong đó n là các yếu tố trong danh sách ban đầu và m Các yếu tố duy nhất trong danh sách ban đầu thay vì

>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
0). Trong trường hợp cả hai khóa và giá trị đều có thể băm, bạn có thể sử dụng đối số
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
1 của hàm đó để tạo các mục có thể băm cho "thử nghiệm tính duy nhất" (để nó hoạt động trong ____10).

Trong trường hợp từ điển (so sánh độc lập với thứ tự), bạn cần ánh xạ nó cho một cấu trúc dữ liệu khác so sánh như thế, ví dụ

>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
3:

>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]

Lưu ý rằng bạn không nên sử dụng cách tiếp cận

>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
4 đơn giản (không sắp xếp) vì từ điển bằng nhau không nhất thiết phải có cùng thứ tự (ngay cả trong Python 3.7 trong đó thứ tự chèn - không phải thứ tự tuyệt đối - được đảm bảo):insertion order - not absolute order - is guaranteed):

>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False

Và thậm chí sắp xếp bộ tuple có thể không hoạt động nếu các phím không thể sắp xếp:

>>> d3 = {1: 1, 'a': 'a'}
>>> tuple(sorted(d3.items()))
TypeError: '<' not supported between instances of 'str' and 'int'

Điểm chuẩn

Tôi nghĩ rằng có thể hữu ích khi xem hiệu suất của các phương pháp này so sánh như thế nào, vì vậy tôi đã thực hiện một điểm chuẩn nhỏ. Các biểu đồ điểm chuẩn là thời gian so với kích thước danh sách dựa trên danh sách không có bản sao (được chọn tùy ý, thời gian chạy không thay đổi đáng kể nếu tôi thêm một số hoặc nhiều bản sao). Đó là một cốt truyện log-log nên phạm vi hoàn chỉnh được bao phủ.

Thời gian tuyệt đối:

Thời gian liên quan đến cách tiếp cận nhanh nhất:

Cách tiếp cận thứ hai từ Thefourtheye là nhanh nhất ở đây. Cách tiếp cận

>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
5 với hàm
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
1 là ở vị trí thứ hai, tuy nhiên đó là cách tiếp cận nhanh nhất bảo tồn trật tự. Các cách tiếp cận khác từ Jcollado và Thefourtheye gần như nhanh chóng. Cách tiếp cận sử dụng
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
5 mà không có chìa khóa và các giải pháp từ Emmanuel và Scorpil rất chậm đối với danh sách dài hơn và hoạt động tồi tệ hơn nhiều
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
8 thay vì
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
0. Cách tiếp cận của STPK với
>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False
0 không phải là
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
8 nhưng nó chậm hơn nhiều so với các phương pháp tương tự ____10.

Mã để tái tạo điểm chuẩn:

from simple_benchmark import benchmark
import json
from collections import OrderedDict
from iteration_utilities import unique_everseen

def jcollado_1(l):
    return [dict(t) for t in {tuple(d.items()) for d in l}]

def jcollado_2(l):
    seen = set()
    new_l = []
    for d in l:
        t = tuple(d.items())
        if t not in seen:
            seen.add(t)
            new_l.append(d)
    return new_l

def Emmanuel(d):
    return [i for n, i in enumerate(d) if i not in d[n + 1:]]

def Scorpil(a):
    b = []
    for i in range(0, len(a)):
        if a[i] not in a[i+1:]:
            b.append(a[i])

def stpk(X):
    set_of_jsons = {json.dumps(d, sort_keys=True) for d in X}
    return [json.loads(t) for t in set_of_jsons]

def thefourtheye_1(data):
    return OrderedDict((frozenset(item.items()),item) for item in data).values()

def thefourtheye_2(data):
    return {frozenset(item.items()):item for item in data}.values()

def iu_1(l):
    return list(unique_everseen(l))

def iu_2(l):
    return list(unique_everseen(l, key=lambda inner_dict: frozenset(inner_dict.items())))

funcs = (jcollado_1, Emmanuel, stpk, Scorpil, thefourtheye_1, thefourtheye_2, iu_1, jcollado_2, iu_2)
arguments = {2**i: [{'a': j} for j in range(2**i)] for i in range(2, 12)}
b = benchmark(funcs, arguments, 'list size')

%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.style.use('ggplot')
mpl.rcParams['figure.figsize'] = '8, 6'

b.plot(relative_to=thefourtheye_2)

Đối với tính đầy đủ ở đây là thời gian cho một danh sách chỉ chứa các bản sao:

# this is the only change for the benchmark
arguments = {2**i: [{'a': 1} for j in range(2**i)] for i in range(2, 12)}

Thời gian không thay đổi đáng kể ngoại trừ

>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
5 mà không có chức năng
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
1, trong trường hợp này là giải pháp nhanh nhất. Tuy nhiên, đó chỉ là trường hợp tốt nhất (vì vậy không phải là đại diện) cho chức năng đó với các giá trị không thể đo được vì thời gian chạy phụ thuộc vào số lượng giá trị duy nhất trong danh sách: O(n*m) trong trường hợp này chỉ là 1 và do đó nó chạy trong ____10.


Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của

>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False
7.