Получить разницу между двумя списками
у меня есть два списка в 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
