Python คำนวณรายการผลต่าง


195

ใน Python วิธีที่ดีที่สุดในการคำนวณความแตกต่างระหว่างสองรายการคืออะไร

ตัวอย่าง

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

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

คำตอบ:


206

ใช้setถ้าคุณไม่สนใจเกี่ยวกับการสั่งซื้อหรือการทำซ้ำรายการ ใช้รายการความเข้าใจหากคุณ:

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

32
พิจารณาใช้set(b)เพื่อให้แน่ใจว่าอัลกอริทึมคือ O (nlogn) แทน Theta (n ^ 2)
Neil G

8
@ Pencilcheck - ไม่ใช่ถ้าคุณสนใจสั่งซื้อหรือทำซ้ำใน A. การใช้setกับ B นั้นไม่เป็นอันตราย แต่นำไปใช้กับAและใช้ผลลัพธ์แทนแบบดั้งเดิมAไม่ใช่
Mark Reed

1
@NeilG คุณพิจารณาเวลาที่ใช้ในการสร้างชุดหรือไม่ ในกรณีของฉัน (ทั้งสองรายการมีประมาณ 10M สตริง) เวลาในการสร้างสองชุดและลบออกมีขนาดใหญ่กว่าการสร้างหนึ่งชุดและวนซ้ำในรายการ
dimril

@dimril ถ้านั่นคือสิ่งที่คุณต้องการทำบางทีคุณควรจะใช้สิ่งที่ซับซ้อนกว่านี้ ตัวอย่างเช่นคุณสามารถเรียงลำดับทั้งสองรายการ O (n log n + m log m) แล้ววนซ้ำรายการที่สอง แต่ใช้การค้นหาแบบไบนารีเพื่อค้นหารายการในรายการแรก มันจะออกมาที่การดำเนินงาน O (n บันทึก n + m บันทึก m + m บันทึก n) (แทนการดำเนินงาน O (n * m)) ซึ่งดูเหมือนจะไม่เลวร้ายนัก เพียงให้แน่ใจว่าได้ตรวจสอบเพื่อนบ้านเพื่อกำจัดรายการที่ซ้ำกันในการใช้งานการค้นหาแบบไบนารีของคุณ อาจมีแพคเกจที่ใช้อุปกรณ์นี้อยู่แล้ว แต่ฉันไม่ได้ตรวจสอบ
jaaq

366

หากคำสั่งซื้อไม่สำคัญคุณสามารถคำนวณความแตกต่างของเซตได้:

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

9
นี่คือทางออกที่ดีที่สุด กรณีทดสอบในรายการที่มี ~ 6,000 สตริงแต่ละรายการแสดงให้เห็นว่าวิธีนี้เร็วกว่า 100 เท่าของความเข้าใจในรายการ
perrygeo

15
ขึ้นอยู่กับการใช้งาน: หากการเก็บรักษาคำสั่งซื้อหรือการทำสำเนาเป็นสิ่งสำคัญ Roman Bodnarchuk อาจมีแนวทางที่ดีกว่า สำหรับความเร็วและพฤติกรรมการตั้งค่าที่เหมือนจริงอันนี้ดูเหมือนดีกว่า
ไบรอัน P

7
หากคุณมีองค์ประกอบเท่ากันหลายรายการในรายการโซลูชันนี้จะไม่ทำงาน
รัน

ดียิ่งกว่าความเข้าใจในรายการ
ทวาย

4
วิธีนี้ดูเหมือนชัดเจน แต่ไม่ถูกต้อง ฉันขอโทษ. แน่นอนเราหมายความว่ารายการสามารถมีองค์ประกอบที่เท่ากันซ้ำได้ มิฉะนั้นเราจะถามเกี่ยวกับความแตกต่างระหว่างชุดไม่ใช่เกี่ยวกับความแตกต่างของรายการ
sergzach

67

คุณสามารถทำ

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

และ

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

7
แต่ถ้า A = [1,1,1] และ B = [0] ดังนั้นสิ่งนี้จะส่งคืน [1]
Mark Bell

1
@ Mark Bell: นั่นเป็นเพราะเซตเป็นรายการที่แตกต่าง (ลบรายการที่ซ้ำกัน)
มีเมฆมาก

1
@cloudy แล้วนี่ไม่ตอบคำถาม
samm82

@ samm82 หาก A = [1,1,1] กว่าชุด (A) คือ [1] เพราะชุดเป็นรายการที่แตกต่างและลบรายการที่ซ้ำกัน นั่นคือเหตุผลถ้า A = [1,1,1] และ B = [0] มันจะส่งกลับ [1]
มีเมฆมาก

29

หนึ่งในสายการบิน:

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

หรือ:

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

14

Python 2.7.3 (ค่าเริ่มต้น 27 ก.พ. 2014, 19:58:35) - IPython 1.1.0 - timeit: (gistub 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())))

ผล:

# 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 def diff (a, b)ดูเหมือนจะเร็วขึ้น


14

ตัวอย่างข้างต้นทำให้เกิดปัญหาการคำนวณความแตกต่างเล็กน้อย สมมติว่าการเรียงลำดับหรือยกเลิกการทำซ้ำแน่นอนทำให้ง่ายต่อการคำนวณความแตกต่าง แต่ถ้าการเปรียบเทียบของคุณไม่สามารถจัดหาสมมติฐานเหล่านั้นได้คุณจะต้องมีการใช้อัลกอริทึมแบบกระจายเล็กน้อย ดู difflib ในไลบรารี่มาตรฐานของไพ ธ อน

#! /usr/bin/python2
from difflib import SequenceMatcher

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

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
คุณได้รับ +1 สำหรับ difflib ซึ่งฉันไม่เคยเห็นมาก่อน แต่ผมไม่เห็นว่าคำตอบข้างต้นทำเป็นปัญหาตามที่ระบุไว้
rbp

ขอบคุณที่ใช้ difflib - ฉันกำลังมองหาวิธีใช้ไลบรารีมาตรฐาน แต่นี้ไม่ได้ทำงานในหลาม 3 ขณะที่printมีการเปลี่ยนแปลงจากคำสั่งไปยังฟังก์ชั่นและreduce, filterและmapได้รับการประกาศ unpythonic (และฉันคิดว่าGuido อาจจะถูกต้อง - ฉันไม่เข้าใจreduceเช่นกัน)
Post169

ไม่ใช่การเปลี่ยนแปลงครั้งใหญ่ที่จะทำให้ใช้งานได้สำหรับ py3 ฉันได้อ่านการถกเถียงเรื่องตัวกรองแผนที่การลดและเห็นด้วยกับตัวเลือกที่จะผลักดันการลดและและสลับการกรองตัวกรองลงในฟังก์ชั่น ฟังก์ชันการทำงานที่หลากหลาย, OO และธรรมชาติของโพรซีเดอร์นั้นคือ IMO ซึ่งเป็นหนึ่งในจุดแข็งของมัน
Kevin



5

ในกรณีที่คุณต้องการความแตกต่างที่จะลึกเข้าไปในรายการของคุณซ้ำฉันเขียนแพ็คเกจสำหรับไพ ธ อน: https://github.com/erasmose/deepdiff

การติดตั้ง

ติดตั้งจาก PyPi:

pip install deepdiff

หากคุณเป็น Python3 คุณต้องติดตั้งด้วย:

pip install future six

ตัวอย่างการใช้งาน

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

วัตถุเดียวกันกลับว่างเปล่า

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

ประเภทของรายการมีการเปลี่ยนแปลง

>>> 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'>"]}

มูลค่าของรายการมีการเปลี่ยนแปลง

>>> 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']}

รายการที่เพิ่มและ / หรือลบออก

>>> 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']}

ความแตกต่างของสตริง

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

ความแตกต่างของสตริง 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

เปลี่ยนประเภท

>>> 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'>"]}

ความแตกต่างของรายการ

>>> 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]"]}

ความแตกต่างของรายการ 2: โปรดทราบว่าไม่คำนึงถึงการสั่งซื้อ

>>> # 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)
    { }

รายการที่มีพจนานุกรม:

>>> 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"]}


2

ในกรณีของรายการพจนานุกรมโซลูชันการทำความเข้าใจรายการแบบสมบูรณ์จะทำงานในขณะที่setโซลูชันยกขึ้น

TypeError: unhashable type: 'dict'

กรณีทดสอบ

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

รหัสอย่างง่ายที่ให้ความแตกต่างกับสินค้าหลายรายการหากคุณต้องการ:

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

เมื่อดูที่TimeComplexityของ In-operator ในกรณีที่แย่ที่สุดมันจะทำงานกับ O (n) แม้กระทั่งสำหรับชุด

ดังนั้นเมื่อเปรียบเทียบสองอาร์เรย์เราจะมี TimeComplexity ของ O (n) ในกรณีที่ดีที่สุดและ O (n ^ 2) ในกรณีที่แย่ที่สุด

ทางเลือก (แต่น่าเสียดายที่ซับซ้อนกว่า) ซึ่งทำงานร่วมกับ O (n) ในกรณีที่ดีที่สุดและแย่ที่สุดคืออันนี้:

# 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

เช่น

>>> a=[1,2,3]
>>> b=[2,4,6]
>>> diff(a, b, cmp)
[1, 3]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.