Получить разницу между двумя списками - PullRequest
671 голосов
/ 11 августа 2010

У меня есть два списка в Python, например:

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

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

temp3 = ['Three', 'Four']

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

Ответы [ 26 ]

1033 голосов
/ 11 августа 2010
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])).

429 голосов
/ 11 августа 2010

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

  • Быстрее, чем 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
61 голосов
/ 11 августа 2010
temp3 = [item for item in temp1 if item not in temp2]
19 голосов
/ 17 августа 2012

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

17 голосов
/ 28 сентября 2014

Если вы хотите рекурсивное различие, я написал пакет для 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}}}
15 голосов
/ 07 августа 2015

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

Вот полный блокнот на github со сравнением между list, numpy и pandas.

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

enter image description here

13 голосов
/ 30 ноября 2017

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

использовать 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)))
12 голосов
/ 07 июля 2016

Может быть сделано с помощью оператора XOR python.

  • Это удалит дубликаты в каждом списке
  • Это покажет разницу temp1 от temp2 и temp2 от temp1.

set(temp1) ^ set(temp2)
12 голосов
/ 11 августа 2010

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

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

Как и другие ответы, не содержащие кортежей в этом направлении, он сохраняет порядок

10 голосов
/ 08 марта 2016

Я хотел что-то, что заняло бы два списка и могло бы делать то, что делает 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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...