Получить разницу между двумя списками
у меня есть два списка в 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
если вы хотите разницу рекурсивно, я написал пакет для 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']
. Вам не нужно беспокоиться о порядке списка или о том, какой список должен быть дан первым.
Если вы действительно ищете производительность, используйте numpy!
вот полная записная книжка как суть на github с сравнением между list, numpy и pandas.
https://gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451
я подброшу, так как ни одно из настоящих решений не дает кортежа:
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))
Я хотел что-то, что займет два списка и может сделать то, что 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)))
Это другое решение:
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