【问题标题】:How to get the symmetric difference of more than 2 lists?如何获得两个以上列表的对称差异?
【发布时间】:2019-05-01 16:52:34
【问题描述】:

我想获取所有列表之间的所有专有元素。因此,如果我有 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 个列表为例,但函数的参数数量不定

【问题讨论】:

  • “效果不好”是什么意思? set(list1) ^ set(list2) ^ set(list3) 工作得很好。
  • 如果您想要一个列表作为结果,请将结果转换为一个列表:list(set(list1) ^...)
  • @Aran-Fey 不,它不能完全正常工作。 3 个集合上的对称差异将返回 3 个集合的交集以及独占项。
  • 更改输入值将更好地突出超过 2 个集合的顺序对称差分导致的问题。例如,按以下顺序尝试使用以下输入的建议解决方案:[1, 3, 2, 0]['a', 1, 3, 0][2, 0]。在这种情况下,唯一应该产生的排他元素是“a”。
  • @DYZ 我更喜欢套装。我只想要元素组

标签: python set-operations


【解决方案1】:

您也可以使用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'}

【讨论】:

  • 如果我在一个列表中有两个或更多相等的元素,它将不起作用:[1][1, 2, 2] 将返回一个空列表而不是 [2, 2]
  • @MykolaZotko 这是一个真实的陈述,但这不是我解释问题要求的方式,因为 OP 在他们的尝试中将他们的列表转换为集合。
  • @pault 我将列表转换为设置以消除重复元素的任何问题
  • @pault 我几乎明白你的回答,但不明白 Counter().items()
  • @DanielMuñoz - collections.Counterdict 的子类,所以Counter.items() 返回键值对的视图(有点像列表)。在Counter的情况下,键是元素,值是计数。
【解决方案2】:

你应该从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'}

【讨论】:

  • 我仍然认为这通常不起作用 - 我更新了我的答案以包含一个演示这一点的示例。 编辑:在我更新的示例中,问题是set1 & set2 & set3 & set4 返回一个空集。
  • 是的,我应该注意到该方法仅适用于 3 套。
【解决方案3】:

这可以主要通过集合操作来完成,但我更喜欢@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'}

【讨论】:

    猜你喜欢
    • 2016-07-12
    • 2015-02-03
    • 2017-07-27
    • 1970-01-01
    • 2020-10-09
    • 2022-07-23
    • 2022-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多