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

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

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

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

temp3 = ['Three', 'Four']

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

Ответы [ 26 ]

9 голосов
/ 18 августа 2011

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

list(itertools.filterfalse(set(temp2).__contains__, temp1))
9 голосов
/ 11 августа 2010

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

temp3 = set(temp1) - set(temp2)
6 голосов
/ 07 февраля 2017

Вот ответ 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']
5 голосов
/ 29 мая 2014

Вы можете использовать простой метод, если элементы списка рассылки отсортированы и установлены.

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

5 голосов
/ 18 октября 2017

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

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])
4 голосов
/ 29 марта 2014

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

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
4 голосов
/ 10 июля 2014

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

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

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

3 голосов
/ 26 июня 2014

однострочная версия arulmr решение

def diff(listA, listB):
    return set(listA) - set(listB) | set(listA) -set(listB)
3 голосов
/ 18 декабря 2015

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

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"
3 голосов
/ 10 мая 2018

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

Код

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