Python, tính toán sự khác biệt danh sách


195

Trong Python, cách tốt nhất để tính toán sự khác biệt giữa hai danh sách là gì?

thí dụ

A = [1,2,3,4]
B = [2,5]

A - B = [1,3,4]
B - A = [5]

Câu trả lời:


206

Sử dụng setnếu bạn không quan tâm đến thứ tự các mặt hàng hoặc lặp đi lặp lại. Sử dụng hiểu danh sách nếu bạn làm:

>>> def diff(first, second):
        second = set(second)
        return [item for item in first if item not in second]

>>> diff(A, B)
[1, 3, 4]
>>> diff(B, A)
[5]
>>> 

31
Cân nhắc sử dụng set(b)để đảm bảo thuật toán là O (nlogn) thay vì Theta (n ^ 2)
Neil G

8
@Ppsoncheck - không phải nếu bạn quan tâm đến việc đặt hàng hoặc sao chép trong A. Áp dụng setcho B là vô hại, nhưng áp dụng nó Avà sử dụng kết quả thay vì ban đầu Athì không.
Mark Reed

1
@NeilG Bạn có xem xét thời gian tiêu thụ để xây dựng bộ không? Trong trường hợp của tôi (cả hai danh sách có khoảng 10 triệu chuỗi) thời gian để xây dựng hai bộ và trừ chúng lớn hơn đáng kể so với xây dựng một bộ và lặp qua danh sách.
dimril

@dimril nếu đó là những gì bạn muốn làm có lẽ bạn nên thực hiện một cái gì đó tinh vi hơn. Ví dụ, bạn có thể sắp xếp cả hai danh sách O (n log n + m log m) và sau đó lặp qua danh sách thứ hai nhưng sử dụng tìm kiếm nhị phân để tìm các mục trong danh sách đầu tiên. Nó sẽ đi ra các hoạt động O (n log n + m log m + m log n) (thay vì các hoạt động O (n * m)), điều này dường như không quá tệ. Chỉ cần đảm bảo kiểm tra hàng xóm để loại bỏ trùng lặp trong triển khai tìm kiếm nhị phân của bạn. Thậm chí có thể có một gói đã thực hiện điều này, nhưng tôi đã không kiểm tra.
jaaq

365

Nếu thứ tự không quan trọng, bạn chỉ cần tính chênh lệch đã đặt:

>>> set([1,2,3,4]) - set([2,5])
set([1, 4, 3])
>>> set([2,5]) - set([1,2,3,4])
set([5])

9
Đây là giải pháp tốt nhất. Trường hợp thử nghiệm trên các danh sách với ~ 6000 chuỗi mỗi chuỗi cho thấy phương pháp này nhanh hơn gần 100 lần so với hiểu danh sách.
perrygeo

15
Phụ thuộc vào ứng dụng: nếu bảo tồn trật tự hoặc sao chép là quan trọng thì Bodnarchuk của La Mã có thể có cách tiếp cận tốt hơn. Đối với tốc độ và hành vi giống như thiết lập thuần túy, điều này có vẻ tốt hơn.
Bryan P

7
Nếu bạn có nhiều yếu tố bằng nhau trong danh sách, giải pháp này sẽ không hoạt động.
karantan

Tốt hơn nhiều so với việc hiểu danh sách.
Dawei

4
Giải pháp này có vẻ quá rõ ràng nhưng không chính xác. Tôi xin lỗi. Tất nhiên chúng tôi có nghĩa là một danh sách có thể có các yếu tố lặp lại bằng nhau. Mặt khác, chúng tôi hỏi về sự khác biệt giữa các bộ, không phải về sự khác biệt danh sách.
sergzach

67

Bạn có thể làm một

list(set(A)-set(B))

list(set(B)-set(A))

7
Nhưng nếu A = [1,1,1] và B = [0] thì điều này trả về [1]
Mark Bell

1
@Mark Bell: Đó là vì một bộ là một danh sách riêng biệt. (loại bỏ trùng lặp)
nhiều mây vào

1
@cloudy Sau đó, điều này không trả lời câu hỏi.
samm82

@ samm82 nếu A = [1,1,1] so với tập (A) là [1] vì tập hợp là một danh sách riêng biệt và loại bỏ trùng lặp. Đó là lý do tại sao, nếu A = [1,1,1] và B = [0], nó sẽ trả về [1].
nhiều mây vào

29

Lót:

diff = lambda l1,l2: [x for x in l1 if x not in l2]
diff(A,B)
diff(B,A)

Hoặc là:

diff = lambda l1,l2: filter(lambda x: x not in l2, l1)
diff(A,B)
diff(B,A)

14

Các ví dụ trên tầm thường hóa vấn đề tính toán sự khác biệt. Giả sử sắp xếp hoặc khử trùng lặp chắc chắn giúp tính toán sự khác biệt dễ dàng hơn, nhưng nếu sự so sánh của bạn không đủ khả năng cho những giả định đó thì bạn sẽ cần một triển khai thuật toán khác không tầm thường. Xem difflib trong thư viện chuẩn python.

from difflib import SequenceMatcher 

squeeze=SequenceMatcher( None, A, B )

print "A - B = [%s]"%( reduce( lambda p,q: p+q, 
                               map( lambda t: squeeze.a[t[1]:t[2]], 
                                    filter(lambda x:x[0]!='equal', 
                                           squeeze.get_opcodes() ) ) ) )

A - B = [[1, 3, 4]]


1
bạn nhận được +1 cho difflib, điều mà tôi chưa từng thấy trước đây. tuy nhiên, tôi không đồng ý rằng các câu trả lời trên tầm thường hóa vấn đề như đã nêu .
rbp

Cảm ơn bạn đã sử dụng difflib - Tôi đã tìm kiếm một giải pháp bằng thư viện chuẩn. Tuy nhiên, điều này không hoạt động trong Python 3, vì printđã thay đổi từ một lệnh thành một hàm và reduce, filtermapđã được tuyên bố là unpythonic. (Và tôi nghĩ Guido có thể đúng - tôi cũng không hiểu điều gì reducexảy ra.)
Post169

Không phải là một sự thay đổi lớn để làm cho nó hoạt động cho py3. Tôi đã đọc cuộc tranh luận về bộ lọc, bản đồ, giảm và đồng ý với lựa chọn đẩy giảm và thay thế bộ lọc của bộ lọc vào các hàm. Chức năng hỗn hợp, OO và tính chất thủ tục của trăn luôn là IMO, một trong những thế mạnh của nó.
Kevin

14

Python 2.7.3 (mặc định, ngày 27 tháng 2 năm 2014, 19:58:35) - IPython 1.1.0 - timeit: (github gist)

def diff(a, b):
  b = set(b)
  return [aa for aa in a if aa not in b]

def set_diff(a, b):
  return list(set(a) - set(b))

diff_lamb_hension = lambda l1,l2: [x for x in l1 if x not in l2]

diff_lamb_filter = lambda l1,l2: filter(lambda x: x not in l2, l1)

from difflib import SequenceMatcher
def squeezer(a, b):
  squeeze = SequenceMatcher(None, a, b)
  return reduce(lambda p,q: p+q, map(
    lambda t: squeeze.a[t[1]:t[2]],
      filter(lambda x:x[0]!='equal',
        squeeze.get_opcodes())))

Các kết quả:

# Small
a = range(10)
b = range(10/2)

timeit[diff(a, b)]
100000 loops, best of 3: 1.97 µs per loop

timeit[set_diff(a, b)]
100000 loops, best of 3: 2.71 µs per loop

timeit[diff_lamb_hension(a, b)]
100000 loops, best of 3: 2.1 µs per loop

timeit[diff_lamb_filter(a, b)]
100000 loops, best of 3: 3.58 µs per loop

timeit[squeezer(a, b)]
10000 loops, best of 3: 36 µs per loop

# Medium
a = range(10**4)
b = range(10**4/2)

timeit[diff(a, b)]
1000 loops, best of 3: 1.17 ms per loop

timeit[set_diff(a, b)]
1000 loops, best of 3: 1.27 ms per loop

timeit[diff_lamb_hension(a, b)]
1 loops, best of 3: 736 ms per loop

timeit[diff_lamb_filter(a, b)]
1 loops, best of 3: 732 ms per loop

timeit[squeezer(a, b)]
100 loops, best of 3: 12.8 ms per loop

# Big
a = xrange(10**7)
b = xrange(10**7/2)

timeit[diff(a, b)]
1 loops, best of 3: 1.74 s per loop

timeit[set_diff(a, b)]
1 loops, best of 3: 2.57 s per loop

timeit[diff_lamb_filter(a, b)]
# too long to wait for

timeit[diff_lamb_filter(a, b)]
# too long to wait for

timeit[diff_lamb_filter(a, b)]
# TypeError: sequence index must be integer, not 'slice'

@ roman-bodnarchuk danh sách hiểu chức năng def diff (a, b) dường như nhanh hơn.


9
A = [1,2,3,4]
B = [2,5]

#A - B
x = list(set(A) - set(B))
#B - A 
y = list(set(B) - set(A))

print x
print y 


5

Trong trường hợp bạn muốn sự khác biệt theo cách đệ quy đi sâu vào các mục trong danh sách của bạn, tôi đã viết một gói cho python: https://github.com/erasmose/deepdiff

Cài đặt

Cài đặt từ PyPi:

pip install deepdiff

Nếu bạn là Python3, bạn cũng cần cài đặt:

pip install future six

Ví dụ sử dụng

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function

Cùng một đối tượng trả về sản phẩm nào

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {}

Loại vật phẩm đã thay đổi

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {'type_changes': ["root[2]: 2=<type 'int'> vs. 2=<type 'str'>"]}

Giá trị của một mặt hàng đã thay đổi

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {'values_changed': ['root[2]: 2 ====>> 4']}

Mục được thêm và / hoặc loại bỏ

>>> t1 = {1:1, 2:2, 3:3, 4:4}
>>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes)
    {'dic_item_added': ['root[5, 6]'],
     'dic_item_removed': ['root[4]'],
     'values_changed': ['root[2]: 2 ====>> 4']}

Chênh lệch chuỗi

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
>>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'values_changed': [ 'root[2]: 2 ====>> 4',
                          "root[4]['b']:\n--- \n+++ \n@@ -1 +1 @@\n-world\n+world!"]}
>>>
>>> print (ddiff.changes['values_changed'][1])
    root[4]['b']:
    --- 
    +++ 
    @@ -1 +1 @@
    -world
    +world!

Chuỗi chênh lệch 2

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!\nGoodbye!\n1\n2\nEnd"}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n1\n2\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'values_changed': [ "root[4]['b']:\n--- \n+++ \n@@ -1,5 +1,4 @@\n-world!\n-Goodbye!\n+world\n 1\n 2\n End"]}
>>>
>>> print (ddiff.changes['values_changed'][0])
    root[4]['b']:
    --- 
    +++ 
    @@ -1,5 +1,4 @@
    -world!
    -Goodbye!
    +world
     1
     2
     End

Thay đổi loại

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n\n\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'type_changes': [ "root[4]['b']: [1, 2, 3]=<type 'list'> vs. world\n\n\nEnd=<type 'str'>"]}

Danh sách khác biệt

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'list_removed': ["root[4]['b']: [3]"]}

Liệt kê sự khác biệt 2: Lưu ý rằng nó KHÔNG đặt hàng vào tài khoản

>>> # Note that it DOES NOT take order into account
... t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { }

Danh sách có chứa từ điển:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'dic_item_removed': ["root[4]['b'][2][2]"],
      'values_changed': ["root[4]['b'][2][1]: 1 ====>> 3"]}

5

cách đơn giản nhất,

sử dụng set (). sự khác biệt (set ())

list_a = [1,2,3]
list_b = [2,3]
print set(list_a).difference(set(list_b))

câu trả lời là set([1])


2

Trong trường hợp danh sách từ điển , giải pháp hiểu danh sách đầy đủ hoạt động trong khi setgiải pháp tăng

TypeError: unhashable type: 'dict'

Trường hợp thử nghiệm

def diff(a, b):
    return [aa for aa in a if aa not in b]

d1 = {"a":1, "b":1}
d2 = {"a":2, "b":2}
d3 = {"a":3, "b":3}

>>> diff([d1, d2, d3], [d2, d3])
[{'a': 1, 'b': 1}]
>>> diff([d1, d2, d3], [d1])
[{'a': 2, 'b': 2}, {'a': 3, 'b': 3}]

0

Mã đơn giản cung cấp cho bạn sự khác biệt với nhiều mục nếu bạn muốn:

a=[1,2,3,3,4]
b=[2,4]
tmp = copy.deepcopy(a)
for k in b:
    if k in tmp:
        tmp.remove(k)
print(tmp)

-1

Khi nhìn vào TimeComplexity của In-Toán tử, trong trường hợp xấu nhất, nó hoạt động với O (n). Ngay cả đối với Bộ.

Vì vậy, khi so sánh hai mảng, chúng ta sẽ có TimeComplexity của O (n) trong trường hợp tốt nhất và O (n ^ 2) trong trường hợp xấu nhất.

Một giải pháp thay thế (nhưng không may phức tạp hơn), hoạt động với O (n) trong trường hợp tốt nhất và tồi tệ nhất là giải pháp này:

# Compares the difference of list a and b
# uses a callback function to compare items
def diff(a, b, callback):
  a_missing_in_b = []
  ai = 0
  bi = 0

  a = sorted(a, callback)
  b = sorted(b, callback)

  while (ai < len(a)) and (bi < len(b)):

    cmp = callback(a[ai], b[bi])
    if cmp < 0:
      a_missing_in_b.append(a[ai])
      ai += 1
    elif cmp > 0:
      # Item b is missing in a
      bi += 1
    else:
      # a and b intersecting on this item
      ai += 1
      bi += 1

  # if a and b are not of same length, we need to add the remaining items
  for ai in xrange(ai, len(a)):
    a_missing_in_b.append(a[ai])


  return a_missing_in_b

ví dụ

>>> a=[1,2,3]
>>> b=[2,4,6]
>>> diff(a, b, cmp)
[1, 3]
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.