Получить разницу между двумя списками
у меня есть два списка в 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]))
.
существующие решения предлагают либо одно, либо другое из:
- быстрее, чем производительность 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
в случае, если вы хотите разницу рекурсивно, я написал пакет для 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]
вы можете использовать наивный метод, если элементы 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)))
Это другое решение:
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