[英]Union of multiple sets in python
[[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]
I have a list of lists.我有一个列表列表。 My aim is to check whether any one sublist has anything in common with other sublists(excluding the first index object to compare).
我的目标是检查任何一个子列表是否与其他子列表有任何共同点(不包括要比较的第一个索引对象)。 If it has anything in common then unify those sublists.
如果它有任何共同点,那么统一这些子列表。
For example, for this example my final answer should be something like:例如,对于这个例子,我的最终答案应该是这样的:
[[1, '34', '44', '40' '30', '41', '42', '43']]
I can understand that I should convert the sublists to sets and then use union() and intersection() operations.我可以理解我应该将子列表转换为集合,然后使用 union() 和 intersection() 操作。 But what I am stuck with is how to compare each set/sublist.
但我坚持的是如何比较每个集合/子列表。 I can't run a loop over the list and compare each sublist one by one as the contents of the list would be modified and this would lead to an error.
我无法在列表上运行循环并一一比较每个子列表,因为列表的内容将被修改,这会导致错误。
What I want to know is there any efficient method to compare all the sublists(converted to sets) and get a union of them?我想知道是否有任何有效的方法来比较所有子列表(转换为集合)并获得它们的联合?
The itertools module makes short work of this problem: itertools模块可以解决这个问题:
>>> from itertools import chain
>>> list(set(chain.from_iterable(d)))
[1, '41', '42', '43', '40', '34', '30', '44']
Another way to do it is to unpack the list into separate arguments for union() :另一种方法是将列表解压缩为union()的单独参数:
>>> list(set().union(*d))
[1, '41', '42', '43', '40', '34', '30', '44']
The latter way eliminates all duplicates and doesn't require that the inputs first be converted to sets.后一种方式消除了所有重复项,并且不需要首先将输入转换为集合。 Also, it doesn't require an import.
此外,它不需要导入。
Using the unpacking operator *
:使用解包运算符
*
:
>> list(set().union(*a))
[1, '44', '30', '42', '43', '40', '41', '34']
(Thanks Raymond Hettinger and ShadowRanger for the comments!) (感谢 Raymond Hettinger 和 ShadowRanger 的评论!)
(Note that (注意
set.union(*tup)
will unpack to将解压到
set.union(tup[0], tup[1], ... tup[n - 1])
) )
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']
Update:更新:
Thanks for the comments感谢您的评论
You can use itertools to perform this action.您可以使用 itertools 来执行此操作。 Let us assume that your list has a variable name A
让我们假设您的列表有一个变量名 A
import itertools
single_list_with_all_values = list(itertools.chain(*A))
single_list_with_all_values.sort()
print set(single_list_with_all_values)
>>> 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'])
And if you really want a list of a list as a final result如果你真的想要一个列表作为最终结果
>>>>[list(set(reduce ( lambda l,a : l + a, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]
And if you don't like recoding a lambda function for the list addition :如果您不喜欢为列表添加重新编码 lambda 函数:
>>>>[list(set(reduce ( list.__add__, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]
EDIT : after your recommendation about using itertools.chain instead of list.__add__ I ran a timeit for both with the original variable used by the original poster.编辑:在您建议使用 itertools.chain 而不是 list.__add__ 之后,我使用原始海报使用的原始变量为两者运行了 timeit。
It seems that timeit times list.__add__ around 2.8s and itertools.chain around 3.5 seconds.似乎 timeit times list.__add__ 大约 2.8 秒和 itertools.chain 大约 3.5 秒。
I checked on this page and yes, you were right with the itertools.chain contains a from_iterable method that grants a huge performance boost.我在这个页面上检查过,是的,你是对的,itertools.chain 包含一个 from_iterable 方法,它可以极大地提升性能。 see below with list.__add__, itertools.chain and itertools.chain.from_iterable.
请参阅下面的 list.__add__、itertools.chain 和 itertools.chain.from_iterable。
>>> 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
Thank you very much for your advises :)非常感谢您的建议:)
Tested with python 2 only: I personally like the readability of reduce
, paired with a simple conditional function, something like仅用 python 2 测试:我个人喜欢
reduce
的可读性,搭配一个简单的条件函数,比如
# 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'}
Of course you can cast this result to a 2d list if you desire list([reduce(...
当然,如果您需要
list([reduce(...
I will note that this is something like 3x slower than the chain.fromiterable
answer.我会注意到这比
chain.fromiterable
答案慢 3 倍。
from functools import reduce
out = list(reduce(set.union, iterable))
as long as at least the first the element of iterable
is a set.只要至少第一个
iterable
的元素是一个集合。 Otherwise,否则,
out = list(reduce(set.union, iterable[1:], set(iterable[0])))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.