8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Python中多个集合的并集

Alok Rajasukumaran 1月前

154 0

[[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']]

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

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

[[1, '34', '44', '40' '30', '41', '42', '43']]

我可以理解我应该将子列表转换为集合,然后使用 union() 和 junction() 操作。但我遇到的难题是如何比较每个集合/子列表。我无法对列表进行循环并逐个比较每个子列表,因为列表的内容将被修改,这会导致错误。

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

帖子版权声明 1、本帖标题:Python中多个集合的并集
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Alok Rajasukumaran在本站《numpy》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 实际上,我忘了强调一个重要条件。抱歉,我的错误。我还提到,当且仅当子列表具有共同点时,它们才应该统一,否则它们应该保持原样。因此,首先需要检查交集(),如果不为空,则只应进行合并。@Peter Wood 不,不会有任何子列表具有单独的起始索引元素,如“2”或“3”。我的意思是在列表列表中,所有子列表都将具有相同的第一个索引元素。

  • 模块 itertools 可以轻松解决这个问题:

    >>> from itertools import chain
    >>> list(set(chain.from_iterable(d)))
    [1, '41', '42', '43', '40', '34', '30', '44']
    

    另一种方法是将列表解包为 union()

    >>> list(set().union(*d))
    [1, '41', '42', '43', '40', '34', '30', '44']
    

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

  • itertools 通常如何扩展?根据您的经验,这种操作可以处理数千万甚至数亿个项目的长列表(此处的“项目”是字符串)吗?甚至更大?

  • chain.from_iterable() 步骤是尺度不变的。在任何给定时间,其整个状态仅存储在两个指向迭代器的指针中。set() 和 list() 部分占用的内存与唯一输入的总数成比例。在我的 64 位机器上,一亿个唯一输入对于 set 对象占用 4.3 GB 的 RAM,对于 list 对象占用 0.9 GB 的 RAM。

  • 您最好将 set.union() 写成 set() 用空集初始化。在这种情况下这样做是可以的,但我花了很多时间寻找错误,因为我认为这可以推广到交集。使用 set,您可以同时进行并集和交集!

  • @RadioControlled:set().union(*d) 适用于空 d,但是,与对交叉点的操作相比,这是一个比对称性更重要的因素。

  • 遗憾的是,所有答案都回答了与所问问题不同的问题,这可能是由于问题中示例选择不当造成的。问题似乎实际上是在要求更接近超图连通分量算法的东西,而不仅仅是将所有内容都放入一个集合中。(dermen 的答案略有不同,但最终更错误。)

  • 使用 unpacking operator * :

    >> list(set().union(*a))
    [1, '44', '30', '42', '43', '40', '41', '34']
    

    (感谢 Raymond Hettinger 和 ShadowRanger 的评论!)

    (注意

    set.union(*tup)
    

    将解压到

    set.union(tup[0], tup[1], ... tup[n - 1])
    

    )

  • 此方法很有效。谢谢。您能解释一下代码中“*”的用途吗?或者提供一个链接,让我可以研究相关内容以了解更多信息。

  • 值得一提的是,元组步骤没有效果,因为星型解包适用于任何可迭代对象。您还可以用 map(set, a) 替换列表推导。结果归结为 list(set.union(*map(set, a)))。

  • 通过将 set.union(*map(set, a)) 更改为 set().union(*a),您可以大幅减少临时集的数量。需要 map(set, 的唯一原因是因为您正在调用 set.union,并且第一个参数成为调用它的 \'self\',但如果您以空集作为基础,union 会接受剩余参数的任意可迭代对象。

  • >>> 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'])
    

    如果你真的想要一份最终结果的清单

    >>>>[list(set(reduce ( lambda l,a : l + a, big)))]
    [[1, '44', '30', '42', '43', '40', '41', '34']]
    

    如果你不喜欢为列表添加重新编码 lambda 函数:

    >>>>[list(set(reduce ( list.__add__, big)))]
    [[1, '44', '30', '42', '43', '40', '41', '34']]
    

    编辑 :在您建议使用 itertools.chain 而不是 list.__add__ 之后,我使用原始海报使用的原始变量对两者运行了 timeit。

    看来 timeit 乘以 list.__add__ 大约需要 2.8 秒,而 itertools.chain 乘以大约 3.5 秒。

    我检查了这个页面,是的,你是对的,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
    

    非常感谢您的建议:)

  • 引用 14

    像这样将列表加在一起是一种低效的 O(n**2) 操作,而且几乎总是一个坏主意。请改用 itertools.chain。

  • 您可以使用 itertools 执行此操作。假设您的列表有一个变量名 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)
    
  • 这很不错。不过还是有一些改进。1) chain(*it) 应该始终更改为 chain.from_iterable(it)。2) 无需 sort(),因为在创建集合时会丢失顺序。3) 如果没有排序,则无需在创建集合之前转换为列表。有了这些更改,它就归结为 set(chain.from_iterable(d))。

  • 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']
    

    更新:

    感谢您的评论

  • 用集合理解代替列表理解可以将其归结为一个漂亮、干净的答案:list({x for _list in s for x in _list})。

  • from functools import reduce
    
    out = list(reduce(set.union, iterable))
    

    只要至少第一个元素 iterable 是一个集合。否则,

    out = list(reduce(set.union, iterable[1:], set(iterable[0])))
    
  • 仅使用 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'}
    

    当然,如果你愿意的话,你可以把这个结果转换成二维列表 list([reduce(...

    我会注意到,这比 chain.fromiterable 答案慢了 3 倍。

返回
作者最近主题: