Как получить симметричную разницу более 2 списков? - PullRequest
4 голосов
/ 01 мая 2019

Я хочу получить все эксклюзивные элементы между всеми моими списками. Так что, если у меня есть 3 списка, как:

list1 = [1, 3, 2]
list2 = ["a", 1, 3]
list3 = [2, 0]

Мой вывод должен быть:

['a', 0]

Я попытался провести симметричное различие со всеми списками, такими как:

set(list1) ^ set(list2) ^ set(list3)

Но это не очень хорошо работает.

Также я попробовал:

def exclusive(*lista):
    excl = set(lista[0])
    for idx in range(len(lista)):
        excl ^= set(lista[idx])
    return excl

Это работает так же, как первый метод, но не дает того, что я хочу.

Тогда я попробовал (set(list1) ^ set(list2)) ^ (set(list2) ^ (set(list3)) и обнаружил, что это не то же самое, что я впервые попробовал.

РЕДАКТИРОВАТЬ:

Я привожу 3 списка в качестве примера, но функция принимает неопределенное количество аргументов

Ответы [ 3 ]

5 голосов
/ 01 мая 2019

Вы также можете использовать не заданный подход, используя collections.Counter:

from itertools import chain
from collections import Counter

res = [k for k, v in Counter(chain(list1, list2, list3)).items() if v==1]
print(res)
#['a', 0]

Используйте itertools.chain, чтобы сгладить списки вместе, и используйте Counter, чтобы сосчитать вхождения. Храните только те, где количество равно 1.


Обновление : Вот лучший пример, который демонстрирует, почему другие методы не работают.

list1 = [1, 3, 2]
list2 = ["a", 1, 3]
list3 = [2, 0]
list4 = [1, 4]
all_lists = [list1, list2, list3, list4]

Правильный ответ на основании ваших критериев:

print([k for k, v in Counter(chain(*all_lists)).items() if v==1])
#['a', 4, 0]

Использование reduce(set.symmetric_difference, ...):

sets = map(set, all_lists)
print(reduce(set.symmetric_difference, sets))
#{0, 1, 4, 'a'}

Использование симметричной разности минус пересечение:

set1 = set(list1)
set2 = set(list2)
set3 = set(list3)
set4 = set(list4)

print((set1 ^ set2 ^ set3 ^ set4) - (set1 & set2 & set3 & set4))
#{0, 1, 4, 'a'}
0 голосов
/ 01 мая 2019

Это можно сделать в первую очередь с помощью операций над множествами, но я предпочитаю простоту ответа из @ pault . Чтобы получить симметричную разность произвольного числа наборов, вы можете найти пересечение среди всех комбинаций множеств, а затем получить симметричную разность этого комбинированного пересечения из объединения всех множеств.

from itertools import combinations

def symdiff(*sets):
    union = set()
    union.update(*sets)

    intersect = set()
    for a, b in combinations(sets, 2):
        intersect.update(a.intersection(b))

    return intersect.symmetric_difference(union)

distincts = symdiff(set([1, 3, 2]), set(['a', 1, 3]), set([2, 0]))
print(distincts)
# {0, 'a'}

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

distincts = symdiff(set([1, 3, 2, 0]), set(['a', 1, 3, 0]), set([2, 0]))
print(distincts)
# {'a'}
0 голосов
/ 01 мая 2019

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

set1 = set(list1)
set2 = set(list2)
set3 = set(list3)

(set1 ^ set2 ^ set3) - (set1 & set2 & set3)

так, чтобы получилось:

list1 = [1,3,2]
list2 = ["a",1,3]
list3 = [2,0,1]

это возвращает:

{0, 'a'}

, тогда как ваша попытка set1 ^ set2 ^ set3 неверно вернет:

{0, 1, 'a'}
...