سؤال

لدي قائمتان في بيثون ، مثل هذه:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']

أحتاج إلى إنشاء قائمة ثالثة مع عناصر من القائمة الأولى غير الموجودة في القائمة الثانية. من المثال الذي يجب أن أحصل عليه:

temp3 = ['Three', 'Four']

هل هناك طرق سريعة بدون دورات وفحص؟

هل كانت مفيدة؟

المحلول

In [5]: list(set(temp1) - set(temp2))
Out[5]: ['Four', 'Three']

حذار ذلك

In [5]: set([1, 2]) - set([2, 3])
Out[5]: set([1]) 

حيث قد تتوقع/تريد أن تساوي set([1, 3]). إذا كنت تريد set([1, 3]) كإجابتك ، ستحتاج إلى استخدام set([1, 2]).symmetric_difference(set([2, 3])).

نصائح أخرى

توفر الحلول الموجودة جميعها إما واحدة أو أخرى من:

  • أسرع من الأداء O (n*m).
  • الحفاظ على ترتيب قائمة الإدخال.

ولكن حتى الآن لا يوجد حل على حد سواء. إذا كنت تريد كليهما ، جرب هذا:

s = set(temp2)
temp3 = [x for x in temp1 if x not in s]

تجربة أداء

import timeit
init = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'
print timeit.timeit('list(set(temp1) - set(temp2))', init, number = 100000)
print timeit.timeit('s = set(temp2);[x for x in temp1 if x not in s]', init, number = 100000)
print timeit.timeit('[item for item in temp1 if item not in temp2]', init, number = 100000)

نتائج:

4.34620224079 # ars' answer
4.2770634955  # This answer
30.7715615392 # matt b's answer

الطريقة التي قدمتها وكذلك الحفاظ على الترتيب هي أيضًا أسرع (قليلاً) من الطرح المحدد لأنه لا يتطلب بناء مجموعة غير ضرورية. سيكون اختلاف الأداء أكثر وضوحًا إذا كانت القائمة الأولى أطول بكثير من الثانية وإذا كان التجزئة باهظة الثمن. هذا اختبار ثان يوضح هذا:

init = '''
temp1 = [str(i) for i in range(100000)]
temp2 = [str(i * 2) for i in range(50)]
'''

نتائج:

11.3836875916 # ars' answer
3.63890368748 # this answer (3 times faster!)
37.7445402279 # matt b's answer
temp3 = [item for item in temp1 if item not in temp2]

يمكن العثور على الفرق بين قائمتين (Say List1 و List2) باستخدام الوظيفة البسيطة التالية.

def diff(list1, list2):
    c = set(list1).union(set(list2))  # or c = set(list1) | set(list2)
    d = set(list1).intersection(set(list2))  # or d = set(list1) & set(list2)
    return list(c - d)

أو

def diff(list1, list2):
    return list(set(list1).symmetric_difference(set(list2)))  # or return list(set(list1) ^ set(list2))

باستخدام الوظيفة أعلاه ، يمكن العثور على الفرق باستخدام diff(temp2, temp1) أو diff(temp1, temp2). كلاهما سيعطي النتيجة ['Four', 'Three']. لا داعي للقلق بشأن ترتيب القائمة أو القائمة التي سيتم منحها أولاً.

مرجع بيثون دوك

في حال كنت تريد الفرق بشكل متكرر ، فقد كتبت حزمة لـ Python:https://github.com/seperman/deepdiff

تثبيت

التثبيت من PYPI:

pip install deepdiff

مثال الاستخدام

الاستيراد

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2

نفس الكائن يرجع فارغًا

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

لقد تغير نوع العنصر

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{ 'type_changes': { 'root[2]': { 'newtype': <class 'str'>,
                                 'newvalue': '2',
                                 'oldtype': <class 'int'>,
                                 'oldvalue': 2}}}

تغيرت قيمة عنصر ما

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

تمت إضافة العنصر و/أو إزالته

>>> 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)
{'dic_item_added': ['root[5]', 'root[6]'],
 'dic_item_removed': ['root[4]'],
 'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

اختلاف السلسلة

>>> 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, indent = 2)
{ 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
                      "root[4]['b']": { 'newvalue': 'world!',
                                        'oldvalue': '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, indent = 2)
{ 'values_changed': { "root[4]['b']": { 'diff': '--- \n'
                                                '+++ \n'
                                                '@@ -1,5 +1,4 @@\n'
                                                '-world!\n'
                                                '-Goodbye!\n'
                                                '+world\n'
                                                ' 1\n'
                                                ' 2\n'
                                                ' End',
                                        'newvalue': 'world\n1\n2\nEnd',
                                        'oldvalue': 'world!\n'
                                                    'Goodbye!\n'
                                                    '1\n'
                                                    '2\n'
                                                    'End'}}}

>>> 
>>> print (ddiff['values_changed']["root[4]['b']"]["diff"])
--- 
+++ 
@@ -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, indent = 2)
{ 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
                                      'newvalue': 'world\n\n\nEnd',
                                      'oldtype': <class 'list'>,
                                      'oldvalue': [1, 2, 3]}}}

الفرق قائمة

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

الفرق في القائمة 2:

>>> 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, 3]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'iterable_item_added': {"root[4]['b'][3]": 3},
  'values_changed': { "root[4]['b'][1]": {'newvalue': 3, 'oldvalue': 2},
                      "root[4]['b'][2]": {'newvalue': 2, 'oldvalue': 3}}}

قائمة الفرق تجاهل النظام أو التكرارات: (مع نفس القواميس على النحو الوارد أعلاه)

>>> 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, 3]}}
>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
>>> print (ddiff)
{}

قائمة تحتوي على القاموس:

>>> 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, indent = 2)
{ 'dic_item_removed': ["root[4]['b'][2][2]"],
  'values_changed': {"root[4]['b'][2][1]": {'newvalue': 3, 'oldvalue': 1}}}

مجموعات:

>>> t1 = {1, 2, 8}
>>> t2 = {1, 2, 3, 5}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (DeepDiff(t1, t2))
{'set_item_added': ['root[3]', 'root[5]'], 'set_item_removed': ['root[8]']}

اسمه Tuples:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t1 = Point(x=11, y=22)
>>> t2 = Point(x=11, y=23)
>>> pprint (DeepDiff(t1, t2))
{'values_changed': {'root.y': {'newvalue': 23, 'oldvalue': 22}}}

الكائنات المخصصة:

>>> class ClassA(object):
...     a = 1
...     def __init__(self, b):
...         self.b = b
... 
>>> t1 = ClassA(1)
>>> t2 = ClassA(2)
>>> 
>>> pprint(DeepDiff(t1, t2))
{'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

تمت إضافة سمة الكائن:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

إذا كنت تبحث حقًا في الأداء ، فاستخدم Numpy!

فيما يلي دفتر الملاحظات الكامل باعتباره جوهرًا على github مع المقارنة بين القائمة ، Numpy ، و Pandas.

https://gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451

enter image description here

الطريقة الأكثر بساطة ،

استعمال set (). الاختلاف (set ())

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

الإجابه هي set([1])

يمكن أن تطبع كقائمة ،

print list(set(list_a).difference(set(list_b)))

سأرمي لأن أيا من الحلول الحالية تسفر عن توب:

temp3 = tuple(set(temp1) - set(temp2))

بدلا من ذلك:

#edited using @Mark Byers idea. If you accept this one as answer, just accept his instead.
temp3 = tuple(x for x in temp1 if x not in set(temp2))

مثل الإجابات العائدة الأخرى غير المتقنة في هذا الاتجاه ، فإنه يحافظ على النظام

يمكن القيام به باستخدام مشغل Python Xor.

  • سيؤدي ذلك إلى إزالة التكرارات في كل قائمة
  • سيظهر هذا اختلاف temp1 من temp2 و temp2 من temp1.

set(temp1) ^ set(temp2)

أردت شيئًا من شأنه أن يأخذ قائمتين ويمكنه فعل ما diff في bash يفعل. نظرًا لأن هذا السؤال يظهر أولاً عندما تبحث عن "Python Diff اثنين من قائمتين" وهو ليس محددًا للغاية ، فسوف أنشر ما توصلت إليه.

استخدام SequenceMather من difflib يمكنك مقارنة قائمتين مثل diff يفعل. لن يخبرك أي من الإجابات الأخرى بالموقف الذي يحدث فيه الفرق ، لكن هذا الإجابات. بعض الإجابات تعطي الفرق في اتجاه واحد فقط. بعض إعادة ترتيب العناصر. البعض لا يتعامل مع التكرارات. لكن هذا الحل يمنحك فرقًا حقيقيًا بين قائمتين:

a = 'A quick fox jumps the lazy dog'.split()
b = 'A quick brown mouse jumps over the dog'.split()

from difflib import SequenceMatcher

for tag, i, j, k, l in SequenceMatcher(None, a, b).get_opcodes():
  if tag == 'equal': print('both have', a[i:j])
  if tag in ('delete', 'replace'): print('  1st has', a[i:j])
  if tag in ('insert', 'replace'): print('  2nd has', b[k:l])

هذا المخرجات:

both have ['A', 'quick']
  1st has ['fox']
  2nd has ['brown', 'mouse']
both have ['jumps']
  2nd has ['over']
both have ['the']
  1st has ['lazy']
both have ['dog']

بالطبع ، إذا قام تطبيقك بتوضيح نفس الافتراضات التي تقدمها الإجابات الأخرى ، فستستفيد منها أكثر من غيرها. ولكن إذا كنت تبحث عن حقيقي diff الوظيفة ، هذه هي الطريقة الوحيدة للذهاب.

على سبيل المثال ، لا يمكن لأي من الإجابات الأخرى التعامل معها:

a = [1,2,3,4,5]
b = [5,4,3,2,1]

لكن هذا واحد يفعل:

  2nd has [5, 4, 3, 2]
both have [1]
  1st has [2, 3, 4, 5]

جرب هذا:

temp3 = set(temp1) - set(temp2)

قد يكون هذا أسرع من فهم قائمة مارك:

list(itertools.filterfalse(set(temp2).__contains__, temp1))

ها هو Counter الإجابة عن أبسط حالة.

هذا أقصر من تلك الموجودة في اتجاهين في اتجاهين لأنها تفعل بالضبط ما يطلبه السؤال: إنشاء قائمة بما هو موجود في القائمة الأولى ولكن ليس الثانية.

from collections import Counter

lst1 = ['One', 'Two', 'Three', 'Four']
lst2 = ['One', 'Two']

c1 = Counter(lst1)
c2 = Counter(lst2)
diff = list((c1 - c2).elements())

بدلاً من ذلك ، اعتمادًا على تفضيلات قابلية القراءة ، فإنه يجعل من لائق واحد:

diff = list((Counter(lst1) - Counter(lst2)).elements())

انتاج:

['Three', 'Four']

لاحظ أنه يمكنك إزالة list(...) اتصل إذا كنت تتكرر عليها.

نظرًا لأن هذا الحل يستخدم العدادات ، فإنه يتعامل مع الكميات بشكل صحيح مقابل العديد من الإجابات المستندة إلى مجموعة. على سبيل المثال على هذا الإدخال:

lst1 = ['One', 'Two', 'Two', 'Two', 'Three', 'Three', 'Four']
lst2 = ['One', 'Two']

الإخراج هو:

['Two', 'Two', 'Three', 'Three', 'Four']

يمكنك استخدام طريقة ساذجة إذا تم فرز عناصر difflist ومجموعاتها.

list1=[1,2,3,4,5]
list2=[1,2,3]

print list1[len(list2):]

أو مع أساليب المجموعة الأصلية:

subset=set(list1).difference(list2)

print subset

import timeit
init = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'
print "Naive solution: ", timeit.timeit('temp1[len(temp2):]', init, number = 100000)
print "Native set solution: ", timeit.timeit('set(temp1).difference(temp2)', init, number = 100000)

الحل الساذج: 0.0787101593292

الحل الأصلي: 0.998837615564

لقد تأخرت عن اللعبة في هذه اللعبة ، لكن يمكنك إجراء مقارنة بين أداء بعض الكود المذكور أعلاه مع هذا ، وهما من أسرع المتنافسين ، وهما ،

list(set(x).symmetric_difference(set(y)))
list(set(x) ^ set(y))

أعتذر عن المستوى الابتدائي للترميز.

import time
import random
from itertools import filterfalse

# 1 - performance (time taken)
# 2 - correctness (answer - 1,4,5,6)
# set performance
performance = 1
numberoftests = 7

def answer(x,y,z):
    if z == 0:
        start = time.clock()
        lists = (str(list(set(x)-set(y))+list(set(y)-set(y))))
        times = ("1 = " + str(time.clock() - start))
        return (lists,times)

    elif z == 1:
        start = time.clock()
        lists = (str(list(set(x).symmetric_difference(set(y)))))
        times = ("2 = " + str(time.clock() - start))
        return (lists,times)

    elif z == 2:
        start = time.clock()
        lists = (str(list(set(x) ^ set(y))))
        times = ("3 = " + str(time.clock() - start))
        return (lists,times)

    elif z == 3:
        start = time.clock()
        lists = (filterfalse(set(y).__contains__, x))
        times = ("4 = " + str(time.clock() - start))
        return (lists,times)

    elif z == 4:
        start = time.clock()
        lists = (tuple(set(x) - set(y)))
        times = ("5 = " + str(time.clock() - start))
        return (lists,times)

    elif z == 5:
        start = time.clock()
        lists = ([tt for tt in x if tt not in y])
        times = ("6 = " + str(time.clock() - start))
        return (lists,times)

    else:    
        start = time.clock()
        Xarray = [iDa for iDa in x if iDa not in y]
        Yarray = [iDb for iDb in y if iDb not in x]
        lists = (str(Xarray + Yarray))
        times = ("7 = " + str(time.clock() - start))
        return (lists,times)

n = numberoftests

if performance == 2:
    a = [1,2,3,4,5]
    b = [3,2,6]
    for c in range(0,n):
        d = answer(a,b,c)
        print(d[0])

elif performance == 1:
    for tests in range(0,10):
        print("Test Number" + str(tests + 1))
        a = random.sample(range(1, 900000), 9999)
        b = random.sample(range(1, 900000), 9999)
        for c in range(0,n):
            #if c not in (1,4,5,6):
            d = answer(a,b,c)
            print(d[1])

هذا حل آخر:

def diff(a, b):
    xa = [i for i in set(a) if i not in b]
    xb = [i for i in set(b) if i not in a]
    return xa + xb

إذا واجهت TypeError: unhashable type: 'list' تحتاج إلى تحويل القوائم أو المجموعات إلى tuples ، على سبيل المثال

set(map(tuple, list_of_lists1)).symmetric_difference(set(map(tuple, list_of_lists2)))

أنظر أيضا كيفية مقارنة قائمة القوائم/المجموعات في بيثون؟

إليك بعض البسيط ، الاحتفاظ بالطلب طرق نشر قوائم السلاسل.

شفرة

نهج غير عادي باستخدام pathlib:

import pathlib


temp1 = ["One", "Two", "Three", "Four"]
temp2 = ["One", "Two"]

p = pathlib.Path(*temp1)
r = p.relative_to(*temp2)
list(r.parts)
# ['Three', 'Four']

هذا يفترض أن كلا القائمتين تحتوي على سلاسل مع بدايات مكافئة. انظر مستندات لمزيد من التفاصيل. لاحظ ، أنها ليست سريعة بشكل خاص مقارنة بعمليات المحددة.


تطبيق مباشرة إلى الأمام باستخدام itertools.zip_longest:

import itertools as it


[x for x, y in it.zip_longest(temp1, temp2) if x != y]
# ['Three', 'Four']

نسخة سطر واحد من أرولمر المحلول

def diff(listA, listB):
    return set(listA) - set(listB) | set(listA) -set(listB)

إذا كنت تريد شيئًا أشبه بتغييرات ... يمكن أن تستخدم العداد

from collections import Counter

def diff(a, b):
  """ more verbose than needs to be, for clarity """
  ca, cb = Counter(a), Counter(b)
  to_add = cb - ca
  to_remove = ca - cb
  changes = Counter(to_add)
  changes.subtract(to_remove)
  return changes

lista = ['one', 'three', 'four', 'four', 'one']
listb = ['one', 'two', 'three']

In [127]: diff(lista, listb)
Out[127]: Counter({'two': 1, 'one': -1, 'four': -2})
# in order to go from lista to list b, you need to add a "two", remove a "one", and remove two "four"s

In [128]: diff(listb, lista)
Out[128]: Counter({'four': 2, 'one': 1, 'two': -1})
# in order to go from listb to lista, you must add two "four"s, add a "one", and remove a "two"

يمكننا حساب التقاطع ناقص اتحاد القوائم:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two', 'Five']

set(temp1+temp2)-(set(temp1)&set(temp2))

Out: set(['Four', 'Five', 'Three']) 

يمكن حل هذا بخط واحد. يتم إعطاء السؤال قائمتين (Temp1 و Temp2) لإرجاع الفرق في القائمة الثالثة (Temp3).

temp3 = list(set(temp1).difference(set(temp2)))

لنفترض أن لدينا قائمتان

list1 = [1, 3, 5, 7, 9]
list2 = [1, 2, 3, 4, 5]

يمكننا أن نرى من بين القائمتين أعلاه أن العناصر 1 و 3 و 5 موجودة في القائمة 2 والعناصر 7 و 9 لا. من ناحية أخرى ، توجد العناصر 1 و 3 و 5 في القائمة 1 والبنود 2 ، 4 لا.

ما هو أفضل حل لإرجاع قائمة جديدة تحتوي على العناصر 7 و 9 و 2 و 4؟

جميع الإجابات أعلاه تجد الحل ، والآن ما هو الأكثر مثالية؟

def difference(list1, list2):
    new_list = []
    for i in list1:
        if i not in list2:
            new_list.append(i)

    for j in list2:
        if j not in list1:
            new_list.append(j)
    return new_list

عكس

def sym_diff(list1, list2):
    return list(set(list1).symmetric_difference(set(list2)))

باستخدام TimeIt يمكننا رؤية النتائج

t1 = timeit.Timer("difference(list1, list2)", "from __main__ import difference, 
list1, list2")
t2 = timeit.Timer("sym_diff(list1, list2)", "from __main__ import sym_diff, 
list1, list2")

print('Using two for loops', t1.timeit(number=100000), 'Milliseconds')
print('Using two for loops', t2.timeit(number=100000), 'Milliseconds')

عائدات

[7, 9, 2, 4]
Using two for loops 0.11572412995155901 Milliseconds
Using symmetric_difference 0.11285737506113946 Milliseconds

Process finished with exit code 0

فيما يلي طريقة بسيطة لتمييز قائمتين (مهما كانت المحتويات) ، يمكنك الحصول على النتيجة كما هو موضح أدناه:

>>> from sets import Set
>>>
>>> l1 = ['xvda', False, 'xvdbb', 12, 'xvdbc']
>>> l2 = ['xvda', 'xvdbb', 'xvdbc', 'xvdbd', None]
>>>
>>> Set(l1).symmetric_difference(Set(l2))
Set([False, 'xvdbd', None, 12])

أتمنى أن يكون هذا مفيدًا.

(list(set(a)-set(b))+list(set(b)-set(a)))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top