从 Python 列表中删除列表子集的最快方法

Posted

技术标签:

【中文标题】从 Python 列表中删除列表子集的最快方法【英文标题】:Fastest way to remove subsets of lists from a list in Python 【发布时间】:2016-02-04 18:44:54 【问题描述】:

假设我有一个类似下面的列表(实际列表要长得多):

fruits = [['apple', 'pear'],
          ['apple', 'pear', 'banana'],
          ['banana', 'pear'],
          ['pear', 'pineapple'],
          ['apple', 'pear', 'banana', 'watermelon']]

在这种情况下,['banana', 'pear']['apple', 'pear']['apple', 'pear', 'banana'] 列表中的所有项目都包含在 ['apple', 'pear', 'banana', 'watermelon'] 列表中(项目的顺序无关紧要),所以我想删除 @987654327 @、['apple', 'pear']['apple', 'pear', 'banana'],因为它们是 ['apple', 'pear', 'banana', 'watermelon'] 的子集。

我目前的解决方案如下所示。我首先使用ifilterimap 为每个列表可能具有的超集创建一个生成器。然后对于那些确实有超集的情况,我使用compressimap 删除它们。

from itertools import imap, ifilter, compress

supersets = imap(lambda a: list(ifilter(lambda x: len(a) < len(x) and set(a).issubset(x), fruits)), fruits)


new_list = list(compress(fruits, imap(lambda x: 0 if x else 1, supersets)))
new_list
#[['pear', 'pineapple'], ['apple', 'pear', 'banana', 'watermelon']]

我想知道是否有更有效的方法来做到这一点?

【问题讨论】:

Python - verifying if one list is a subset of the other的可能重复 您可以从删除 imap 和 ifilter 开始使用生成器表达式/列表推导。它们以相同的方式工作,但产生可读的代码...... @BrentWashburne 这不完全是重复的。如您所见,我当前的解决方案实际上确实使用了链接帖子所建议的issubset()。我的问题更多是关于如何删除作为大列表中其他列表子集的列表。 @JBernardo:你能举个例子吗?谢谢! :) @dawg:抱歉,忘记更改代码。 foo 应该是 supersets。我更新了它 【参考方案1】:
filter(lambda f: not any(set(f) < set(g) for g in fruits), fruits)

【讨论】:

当我尝试你的代码时,我得到了[['apple', 'pear'], [['apple', 'pear', 'banana'], ['banana', 'pear'], ['pear', 'pineapple'], ['apple', 'pear', 'banana', 'watermelon']] 我的代码有错误。我认为当前版本应该可以工作。 无论哪种方式,它都对我有用 - 无论哪种方式,这都是一个 Pythonic 的答案。这让我很开心。 很奇怪。在 Canopy 编辑器界面中,我不断收到一个空列表。但是当我在命令行界面中尝试时,我得到了正确的结果!谢谢你【参考方案2】:

我不知道它是否更快,但这更容易阅读(无论如何对我来说):

sets=frozenset(e) for e in fruits  
us=set()
while sets:
    e=sets.pop()
    if any(e.issubset(s) for s in sets) or any(e.issubset(s) for s in us):
        continue
    else:
        us.add(e)   

更新

速度很快。更快的是使用for 循环。检查时间:

fruits = [['apple', 'pear'],
        ['apple', 'pear', 'banana'],
        ['banana', 'pear'],
        ['pear', 'pineapple'],
        ['apple', 'pear', 'banana', 'watermelon']]

from itertools import imap, ifilter, compress    

def f1():              
    sets=frozenset(e) for e in fruits  
    us=[]
    while sets:
        e=sets.pop()
        if any(e.issubset(s) for s in sets) or any(e.issubset(s) for s in us):
            continue
        else:
            us.append(list(e))   
    return us           

def f2():
    supersets = imap(lambda a: list(ifilter(lambda x: len(a) < len(x) and set(a).issubset(x), fruits)), fruits)
    new_list = list(compress(fruits, imap(lambda x: 0 if x else 1, supersets)))
    return new_list

def f3():
    return filter(lambda f: not any(set(f) < set(g) for g in fruits), fruits)

def f4():              
    sets=frozenset(e) for e in fruits  
    us=[]
    for e in sets:
        if any(e < s for s in sets):
            continue
        else:
            us.append(list(e))   
    return us              

if __name__=='__main__':
    import timeit     
    for f in (f1, f2, f3, f4):
        print f.__name__, timeit.timeit("f()", setup="from __main__ import f, fruits"), f()  

在我的 Python 2.7 机器上:

f1 8.09958791733 [['watermelon', 'pear', 'apple', 'banana'], ['pear', 'pineapple']]
f2 15.5085151196 [['pear', 'pineapple'], ['apple', 'pear', 'banana', 'watermelon']]
f3 11.9473619461 [['pear', 'pineapple'], ['apple', 'pear', 'banana', 'watermelon']]
f4 5.87942910194 [['watermelon', 'pear', 'apple', 'banana'], ['pear', 'pineapple']]

【讨论】:

我试过f1(),结果得到set()..mm 感谢您的实际时间安排!

以上是关于从 Python 列表中删除列表子集的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

以大于 Python 列表中的值的最小差值对大多数数字进行采样的最快方法

在 Python 中,从列表中删除重复项以使所有元素都是唯一的*同时保留顺序*的最快算法是啥? [复制]

获取可以从python中的列表形成的所有互斥对的最快方法? [复制]

Python - 通过列表中的前缀和后缀删除元组

在python中合并两个列表的最快方法是啥?

计算 Python 列表中出现次数的最快方法