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


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

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

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

temp3 = ['Three', 'Four']

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

18 536

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

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

  • быстрее, чем производительность 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]

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

Если вы действительно ищете производительность, то используйте 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]

вы можете использовать наивный метод, если элементы 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

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