Получить разницу между двумя списками

у меня есть два списка в Python, как это:

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

мне нужно создать третий список с пунктами из первого списка, которых нет во втором. Из примера я должен получить:

temp3 = ['Three', 'Four']

существуют ли быстрые способы без циклов и проверок?

18 ответов


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


существующие решения предлагают либо одно, либо другое из:

  • более быстро чем представление о(н*м).
  • сохранить порядок ввода списка.

но пока решения не имеет. Если вы хотите оба, попробуйте следующее:

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]

если вы хотите разницу рекурсивно, я написал пакет для 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]']}

имени Кортежи:

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

разница между двумя списками (скажем, 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 doc


Если вы действительно ищете производительность, используйте numpy!

вот полная записная книжка как суть на github с сравнением между list, numpy и pandas.

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

enter image description here


я подброшу, так как ни одно из настоящих решений не дает кортежа:

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)

самый простой способ,

использовать 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)))

это может быть даже быстрее, чем понимание списка Марка:

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

попробуйте это:

temp3 = set(temp1) - set(temp2)

Я хотел что-то, что займет два списка и может сделать то, что diff на bash делает. Поскольку этот вопрос появляется первым, когда вы ищете "python diff two lists" и не очень конкретный, я опубликую то, что я придумал.

используя 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]

вы можете использовать наивный метод, если элементы диффлиста отсортированы и установлены.

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


здесь 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']

Если вы столкнетесь с TypeError: unhashable type: 'list' вам нужно повернуть списки или наборы в кортежи, например,

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

см. также как сравнить список списков / наборов в python?


Это другое решение:

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

однострочная версия arulmr решение

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