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

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

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

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

temp3 = ['Three', 'Four']

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

вопрос задан 11.08.2010
Ockonal
18881 репутация

24 ответов


  • 843 рейтинг
    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])).

    ответ дан ars, с репутацией 79125, 11.08.2010
  • 394 рейтинг

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

    • Быстрее чем 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
    
    ответ дан Mark Byers, с репутацией 567994, 11.08.2010
  • 52 рейтинг
    temp3 = [item for item in temp1 if item not in temp2]
    
    ответ дан matt b, с репутацией 108230, 11.08.2010
  • 16 рейтинг

    Если вы хотите рекурсивное различие, я написал пакет для 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': ,
                                     'newvalue': '2',
                                     'oldtype': ,
                                     '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': ,
                                          'newvalue': 'world\n\n\nEnd',
                                          'oldtype': ,
                                          '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}}}
    
    ответ дан Seperman, с репутацией 1993, 27.09.2014
  • 15 рейтинг

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

    ответ дан arulmr, с репутацией 5614, 17.08.2012
  • 14 рейтинг

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

    Вот полная тетрадь в виде github со сравнением между list, numpy и pandas.

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

    enter image description here

    ответ дан denfromufa, с репутацией 3159, 7.08.2015
  • 12 рейтинг

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

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

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

    ответ дан aaronasterling, с репутацией 46346, 11.08.2010
  • 11 рейтинг

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

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

    set(temp1) ^ set(temp2)
    
    ответ дан Vikram S, с репутацией 4741, 7.07.2016
  • 10 рейтинг

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

    использовать 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)))
    
    ответ дан Mohideen ibn Mohammed, с репутацией 6554, 30.11.2017
  • 9 рейтинг

    это может быть даже быстрее, чем понимание списка Марка:

    list(itertools.filterfalse(set(temp2).__contains__, temp1))
    
    ответ дан Mohammed, с репутацией 91, 18.08.2011
  • 8 рейтинг

    Попробуйте это:

    temp3 = set(temp1) - set(temp2)
    
    ответ дан Maciej Kucharz, с репутацией 1000, 11.08.2010
  • 8 рейтинг

    Я хотел что-то, что заняло бы два списка и могло бы сделать то, что делает 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]
    
    ответ дан arekolek, с репутацией 4026, 7.03.2016
  • 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

    ответ дан soundcorner, с репутацией 71, 29.05.2014
  • 5 рейтинг

    Вот 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']
    
    ответ дан Taylor Edmiston, с репутацией 3709, 7.02.2017
  • 4 рейтинг

    Если вы столкнетесь с TypeError: unhashable type: 'list', вам нужно превратить списки или наборы в кортежи, e. г.

    set(map(tuple, list_of_lists1)).symmetric_difference(set(map(tuple, list_of_lists2)))
    

    См. Также Как сравнить список списков / наборов в Python?

    ответ дан guaka, с репутацией 9536, 10.07.2014
  • 3 рейтинг

    однолинейная версия arulmr решение

    def diff(listA, listB):
        return set(listA) - set(listB) | set(listA) -set(listB)
    
    ответ дан sreemanth pulagam, с репутацией 727, 26.06.2014
  • 3 рейтинг

    , если вы хотите что-то более похожее на набор изменений. , , мог бы использовать Счетчик

    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 a "two"
    
    ответ дан Nick Franceschina, с репутацией 3071, 17.12.2015
  • 3 рейтинг

    Это еще одно решение:

    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
    
    ответ дан manhgd, с репутацией 39, 29.03.2014
  • 3 рейтинг

    Я немного опоздал в игре для этого, но вы можете сравнить производительность некоторых из вышеупомянутого кода с этим, два из самых быстрых соперников,

    list(set(x).symmetric_difference(set(y)))
    list(set(x) ^ set(y))
    

    Прошу прощения за элементарный уровень кодирования.

    import time
    import random
    from itertools import filterfalse
    
    # 1 - performance (time taken)
    # 2 - correctness (answer - 1,4,5,6)
    # set performance
    performance = 1
    numberoftests = 7
    
    def answer(x,y,z):
        if z == 0:
            start = time.clock()
            lists = (str(list(set(x)-set(y))+list(set(y)-set(y))))
            times = ("1 = " + str(time.clock() - start))
            return (lists,times)
    
        elif z == 1:
            start = time.clock()
            lists = (str(list(set(x).symmetric_difference(set(y)))))
            times = ("2 = " + str(time.clock() - start))
            return (lists,times)
    
        elif z == 2:
            start = time.clock()
            lists = (str(list(set(x) ^ set(y))))
            times = ("3 = " + str(time.clock() - start))
            return (lists,times)
    
        elif z == 3:
            start = time.clock()
            lists = (filterfalse(set(y).__contains__, x))
            times = ("4 = " + str(time.clock() - start))
            return (lists,times)
    
        elif z == 4:
            start = time.clock()
            lists = (tuple(set(x) - set(y)))
            times = ("5 = " + str(time.clock() - start))
            return (lists,times)
    
        elif z == 5:
            start = time.clock()
            lists = ([tt for tt in x if tt not in y])
            times = ("6 = " + str(time.clock() - start))
            return (lists,times)
    
        else:    
            start = time.clock()
            Xarray = [iDa for iDa in x if iDa not in y]
            Yarray = [iDb for iDb in y if iDb not in x]
            lists = (str(Xarray + Yarray))
            times = ("7 = " + str(time.clock() - start))
            return (lists,times)
    
    n = numberoftests
    
    if performance == 2:
        a = [1,2,3,4,5]
        b = [3,2,6]
        for c in range(0,n):
            d = answer(a,b,c)
            print(d[0])
    
    elif performance == 1:
        for tests in range(0,10):
            print("Test Number" + str(tests + 1))
            a = random.sample(range(1, 900000), 9999)
            b = random.sample(range(1, 900000), 9999)
            for c in range(0,n):
                #if c not in (1,4,5,6):
                d = answer(a,b,c)
                print(d[1])
    
    ответ дан Alex Jacob, с репутацией 44, 18.10.2017
  • 2 рейтинг

    Это можно решить одной строкой. Вопрос задается двумя списками (temp1 и temp2), возвращающими их разницу в третьем списке (temp3).

    temp3 = list(set(temp1).difference(set(temp2)))
    
    ответ дан fgaim, с репутацией 106, 9.06.2017
  • 2 рейтинг

    Мы можем вычислить пересечение минус объединение списков:

    temp1 = ['One', 'Two', 'Three', 'Four']
    temp2 = ['One', 'Two', 'Five']
    
    set(temp1+temp2)-(set(temp1)&set(temp2))
    
    Out: set(['Four', 'Five', 'Three']) 
    
    ответ дан Mohammad Etemaddar, с репутацией 194, 21.04.2017
  • 2 рейтинг

    Вот несколько простых, сохраняющих порядок способов разграничения двух списков строк.

    код

    Необычный подход с использованием pathlib :

    import pathlib
    
    
    temp1 = ["One", "Two", "Three", "Four"]
    temp2 = ["One", "Two"]
    
    p = pathlib.Path(*temp1)
    r = p.relative_to(*temp2)
    list(r.parts)
    # ['Three', 'Four']
    

    Это предполагает, что оба списка содержат строки с эквивалентными началами. См. документы для более подробной информации. Обратите внимание, что это не особенно быстро по сравнению с операциями над множествами.


    Прямая реализация с использованием itertools.zip_longest :

    import itertools as it
    
    
    [x for x, y in it.zip_longest(temp1, temp2) if x != y]
    # ['Three', 'Four']
    
    ответ дан pylang, с репутацией 11968, 10.05.2018
  • -1 рейтинг
    (list(set(a)-set(b))+list(set(b)-set(a)))
    
    ответ дан nadhem, с репутацией 32, 6.07.2017
  • -1 рейтинг

    Вот простой способ различить два списка (независимо от содержимого), вы можете получить результат, как показано ниже:

    >>> from sets import Set
    >>>
    >>> l1 = ['xvda', False, 'xvdbb', 12, 'xvdbc']
    >>> l2 = ['xvda', 'xvdbb', 'xvdbc', 'xvdbd', None]
    >>>
    >>> Set(l1).symmetric_difference(Set(l2))
    Set([False, 'xvdbd', None, 12])
    

    Надеюсь, это будет полезно.

    ответ дан S.K. Venkat, с репутацией 1007, 29.05.2018