python中多集的并集

python中多集的并集,python,list,python-3.x,set-union,Python,List,Python 3.x,Set Union,我有一份清单。我的目的是检查是否有任何一个子列表与其他子列表有任何共同之处(不包括要比较的第一个索引对象)。如果它有什么共同点,那么就统一这些子列表 例如,对于这个例子,我的最终答案应该是: [[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']] 我可以理解,我应该将子列表转换为集合,然

我有一份清单。我的目的是检查是否有任何一个子列表与其他子列表有任何共同之处(不包括要比较的第一个索引对象)。如果它有什么共同点,那么就统一这些子列表

例如,对于这个例子,我的最终答案应该是:

[[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]
我可以理解,我应该将子列表转换为集合,然后使用union()和intersection()操作。但我一直坚持的是如何比较每个集合/子列表。我无法在列表上运行循环并逐个比较每个子列表,因为列表的内容将被修改,这将导致错误

我想知道的是,是否有任何有效的方法来比较所有子列表(转换为集合)并将它们合并?

使用:

(感谢雷蒙德·海廷格和暗影游侠的评论!)

(注意

将拆包到

set.union(*tup)
)

更新:


感谢您的评论

您可以使用itertools执行此操作。让我们假设您的列表有一个变量名a

In [20]: s
Out[20]: 
[[1, '34', '44'],
 [1, '40', '30', '41'],
 [1, '41', '40', '42'],
 [1, '42', '41', '43'],
 [1, '43', '42', '44'],
 [1, '44', '34', '43']]
In [31]: list({x for _list in s for x in _list})
Out[31]: [1, '44', '30', '42', '43', '40', '41', '34']
如果你真的想要一个列表作为最终结果

>>> big = [[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]
>>> set(reduce ( lambda l,a : l + a, big))
set([1, '44', '30', '42', '43', '40', '41', '34'])
如果您不喜欢为列表添加重新编码lambda函数:

>>>>[list(set(reduce ( lambda l,a : l + a, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]
编辑:在您建议使用itertools.chain而不是list之后。\uuuu add\uuuu我使用原始海报使用的原始变量运行了一次timeit

似乎timeit times list.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

我在这一页上查过了,是的,您对itertools的使用是正确的。该链包含一个from_iterable方法,可以极大地提高性能。请参见下面的列表。uuu添加、itertools.chain和itertools.chain.from

>>>>[list(set(reduce ( list.__add__, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]
非常感谢您的建议:)

本模块简要介绍了此问题:

>>> timeit.timeit("[list(set(reduce ( list.__add__, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
16.051744650801993
>>> timeit.timeit("[list(set(reduce ( itertools.chain, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
54.721315866467194
>>> timeit.timeit("list(set(itertools.chain.from_iterable(big)))", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
0.040056066849501804
另一种方法是将列表解压为union()的单独参数:


后一种方法消除了所有重复项,并且不需要先将输入转换为集合。而且,它不需要导入。

仅使用Python2进行测试:我个人喜欢
reduce
的可读性,以及一个简单的条件函数,比如

>>> list(set().union(*d))
[1, '41', '42', '43', '40', '34', '30', '44']
当然,如果需要
list([reduce(…

我会注意到这比
链慢3倍。fromiterable
答案

# PYTHON 2 ONLY!
somelists = [[1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']] # your original lists
somesets = map(set,somelists) #your lists as sets

def condition(s1,s2): # condition to apply recursively to the sets
    if s1.intersection(s2):
        return s1.union(s2)
reduce( condition,somesets)
#{1, '30', '34', '40', '41', '42', '43', '44'}
只要
iterable
的元素至少有第一个是集合,否则

from functools import reduce

out = list(reduce(set.union, iterable))

您需要相同的顺序吗?不,不需要保留顺序。是否有其他列表以“
2
”开头,不应该合并?您想要这样吗?实际上,我忘了强调一个重要条件。对不起,我的错误。我还提到,子列表应该是统一的,当且仅当它们有共同的内容时所以,首先需要检查交叉点()如果不是空的,则只应执行并集。@Peter Wood不,不会有任何子列表具有单独的起始索引元素,如“2”或“3”。我的意思是,在列表列表中,所有子列表都具有相同的第一个索引元素。您不需要理解列表,因为集合构造函数可以使用生成器。@PeterWood OP要求使用l这将是他的最终答案否,理解正在传递给
集合
。你不需要它。用集合理解替换列表理解可以将这一问题归结为一个漂亮、清晰的答案:
列表({x代表s中的列表,x代表列表中的x})
。这个方法很好用。谢谢。你能解释一下“*”在代码中的用法吗?或者提供一个链接,让我学习与此相关的内容以了解更多。FWIW,元组步骤没有效果,因为星号解包可以在任何iterable上工作。你也可以用
map(set,a)来代替列表理解
。结果可以归结为
列表(set.union(*map(set,a)))
@TapojyotiMandal请参见答案中的解释。通过将
set.union(*map(set,a))
更改为
set().union(*a)
,可以显著减少临时
set
s的数量。唯一原因是
map(set,
之所以需要,是因为您正在调用
set.union
,并且第一个参数变成了它被调用的“self”,但是如果您将一个空的
set
作为基础,
union
接受剩余参数的任意iterables。这很好。不过有一些改进。1)一个
链(*it)
应始终更改为
chain。from iterable(it)
.2)不需要
sort()
,因为在创建集合时顺序会丢失。3)没有排序,在创建集合之前不需要转换为列表。通过这些更改,它可以归结为
set(chain.from iterable(d))
。像这样将列表添加到一起是一种低效的O(n**2)操作,几乎总是一个坏主意。请改用
itertools.chain
。itertools通常是如何扩展的?根据您的经验,这种操作能否处理数千万或数亿个项目长的列表(“项目”在这里是字符串)?甚至更大?
链。from_iterable()
步骤是缩放不变的。在任何给定时间,它的整个状态都存储在指向迭代器的两个指针中。
set()
list()
部分消耗的内存与唯一输入的总数成比例。在我的64位机器上,一亿个唯一输入对set对象占用4.3 GB内存,对list对象占用0.9 GB内存。您最好将
set.union()
写成
set()
使用空集初始化。在这种情况下,这是可以的,但我花了很多时间搜索错误,因为我假设这会推广到交叉点。使用
集。
可以同时进行并集和交叉!@RadioControlled:
set()。并集(*d)
使用空集
d
,但这是一个更重要的因素
>>> timeit.timeit("[list(set(reduce ( list.__add__, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
16.051744650801993
>>> timeit.timeit("[list(set(reduce ( itertools.chain, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
54.721315866467194
>>> timeit.timeit("list(set(itertools.chain.from_iterable(big)))", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
0.040056066849501804
>>> from itertools import chain
>>> list(set(chain.from_iterable(d)))
[1, '41', '42', '43', '40', '34', '30', '44']
>>> list(set().union(*d))
[1, '41', '42', '43', '40', '34', '30', '44']
# PYTHON 2 ONLY!
somelists = [[1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']] # your original lists
somesets = map(set,somelists) #your lists as sets

def condition(s1,s2): # condition to apply recursively to the sets
    if s1.intersection(s2):
        return s1.union(s2)
reduce( condition,somesets)
#{1, '30', '34', '40', '41', '42', '43', '44'}
from functools import reduce

out = list(reduce(set.union, iterable))
out = list(reduce(set.union, iterable[1:], set(iterable[0])))