Python 集合列表上的减法

Python 集合列表上的减法,python,algorithm,list,set,list-comprehension,Python,Algorithm,List,Set,List Comprehension,给定一个集合列表: allsets = [set([1, 2, 4]), set([4, 5, 6]), set([4, 5, 7])] 什么是pythonic方法来计算与其他集合没有重叠的元素集合的对应列表 only = [set([1, 2]), set([6]), set([7])] 有没有办法通过列表理解来做到这一点?是的,它可以做到,但很难做到 >>> [(i-set.union(*[j for j in allsets if j!= i])) for i in

给定一个集合列表:

allsets = [set([1, 2, 4]), set([4, 5, 6]), set([4, 5, 7])]
什么是pythonic方法来计算与其他集合没有重叠的元素集合的对应列表

only = [set([1, 2]), set([6]), set([7])]

有没有办法通过列表理解来做到这一点?

是的,它可以做到,但很难做到

>>> [(i-set.union(*[j for j in allsets if j!= i])) for i in allsets]   
[set([1, 2]), set([6]), set([7])]

可以找到有关集合的一些参考。调用
*
运算符

为了避免二次运行时,您需要进行初始传递,以确定哪些元素出现在多个集合中:

import itertools
import collections
element_counts = collections.Counter(itertools.chain.from_iterable(allsets))
然后,您可以简单地创建一个集合列表,其中包含仅出现一次的所有元素:

nondupes = [{elem for elem in original if element_counts[elem] == 1}
            for original in allsets]

或者,我们不必直接从
element\u counts
构造
nondupes
,而是可以进行额外的传递来构造一组恰好出现在一个输入中的所有元素。这需要一个额外的语句,但它允许我们利用设置交叉点的
&
运算符,使列表理解更短、更有效:

element_counts = collections.Counter(itertools.chain.from_iterable(allsets))
all_uniques = {elem for elem, count in element_counts.items() if count == 1}
#                                                     ^ viewitems() in Python 2.7
nondupes = [original & all_uniques for original in allsets]
计时似乎表明,使用
all_uniques
集合可以大大加快整个重复消除过程。对于大量重复的输入集,Python3上的值大约为a,但对于Python2上的整个重复消除过程,值大约为a,因为运行时更多的时间是由构造计数器来控制的。这种加速是相当可观的,尽管不如首先使用
元素计数来避免二次运行时那么重要。如果您使用的是Python 2,并且此代码对速度至关重要,那么您可能希望使用普通的
dict
collections.defaultdict
而不是
计数器


另一种方法是从
element\u counts
构建一个
dupes
集合,并使用
original-dupes
,而不是像munk那样在列表理解中使用
original&all\u uniques
。这比使用
all_uniques
集合和
&
的性能更好还是更差取决于输入中的重复程度和所使用的Python版本,但这两种方法都会产生很大的差异。

另一种使用
itertools.chain
的解决方案:

>>> from itertools import chain
>>> [x - set(chain(*(y for y in allsets if y!=x))) for x in allsets]
[set([1, 2]), set([6]), set([7])]

也可以不用解包并使用
链。取而代之的是from\u iterable

使用计数器和理解的稍微不同的解决方案,以利用
-
操作符进行设置差异

from itertools import chain
from collections import Counter

allsets = [{1, 2, 4}, {4, 5, 6}, {4, 5, 7}]
element_counts = Counter(chain.from_iterable(allsets))

dupes = {key for key in element_counts 
         if element_counts[key] > 1}

only = [s - dupes for s in allsets]

eww同意。像避免瘟疫一样避免这种情况。更喜欢一些冗长的循环(但伟大的工作Bhargav!)你不需要内部的list@PadraicCunningham你更喜欢那里的genexp?当然是更好的方式。OP 1的一些链接。2.字面语法可能会更好一些[{elem for elem in original…}]@munk:哦,对了。我总是忘记使用集合文字和集合理解。与唯一元素相交比在真实数据集中减去重复项快6倍左右。在我的数据集中,唯一的元素很少,重复的元素也很多。在发布我的原始解决方案后,我确实考虑过这一点,尽管我使用了
&
并创建了
唯一的元素集,而不是
重复集。显示
&
比每次运行Python级别集的速度快30%左右。无论是
&
还是
-
执行得更好,都可能取决于元素重复的程度以及您使用的Python版本。选择此解决方案作为最佳答案,因为1)它非常可读,2)比我的真实数据上的user2357112解决方案快15%-30%。非常好且可读的解决方案。基于可读性和速度,我最初选择这个作为最佳答案。后来改为user2357112的答案,经过进一步测试,答案明显更快。相关: