如何在强制限制列表项的情况下以快速的方式消除列表中的列表项?

4

这是我的第一篇文章和提问...

所以,假设 list_a 是一个列表的列表:

list_a = [[2,7,8], [3,4,2], [5,10], [4], [2,3,5]...]

假设有另一个整数列表 list_b = [5,7]

我需要排除在list_a中所有包含至少一个来自list_b的元素的列表。上面示例的结果应该类似于list_c = [[3,4,2], [4]...]

如果list_b不是一个列表,而是一个单独的数字b,那么可以通过以下一行代码定义list_c

list_c = [x for x in list_a if not b in x]

我在想,是否有可能为列表list_b编写一个优雅的一行代码,其中包含多个值。当然,我可以遍历所有list_b的值,但可能存在更快的选项?


我看到你在底部有各种答案。但作为一名开发人员,我想指出一些事情,你应该尽量避免写单行代码。一开始似乎很棒,但在故障排除时,要找出并维护它会更加困难。 - Omrum Cetin
我同意你的观点,但在这种特殊情况下,我不想为了一行代码而写一行代码。我必须对包含多达100,000个列表项的列表执行数百万次此操作,因此每个微小的执行时间缩短都对我有所帮助。 - DimaWest
3个回答

4
您可以使用列表推导式编写逻辑以筛选出A中所有子列表,其中子列表不包含B中的任何元素,例如:
A = [[2,7,8], [3,4,2], [5,10], [4], [2,3,5]]

B = [5,7]

[l for l in A if not any(n in l for n in B)]
# [[3, 4, 2], [4]]

条件any(n in l for n in B)将为真,如果B中的任何元素n在来自A的子列表l中。使用not可以取反该条件。

太棒了!这正是我在寻找的!非常感谢你! - DimaWest

4
首先,考虑检查list_a中一个单独元素的任务,例如[2,7,8],因为无论如何,我们都需要一种方法来做到这一点,然后我们将应用这种方法到具有列表推导式的列表中。我将使用a作为此类列表的名称,b作为list_b的元素。
编写此部分代码的简单方法是使用内置的any,它与生成器表达式组合起来非常优雅:any(b in a for b in list_b)
逻辑很简单:我们创建一个生成器表达式(类似于惰性求值的列表推导),表示将b in a检查应用于每个b in list_b的结果。我们通过将[]替换为()来创建它们;但由于特殊的语法规则,当将其用作函数的唯一参数时,我们可以省略它们。然后,any正好可以实现听起来的功能:它检查(并提前退出)可迭代对象(其中包括生成器表达式)中的任何元素是否是真值
然而,利用集合交集可能是更好的解决方法。关键见解是我们正在尝试执行的测试是对称的;考虑alist_b之间的测试(并为a的元素提供另一个名称),我们同样可以编写any(x in list_b for x in a)的代码,但这更难理解。
现在,从a创建一个set无济于事,因为我们必须无论如何迭代a才能这样做。 (生成器表达式隐含地执行了这一点;在列表成员身份验证所使用的in中需要迭代。)但是,如果我们从list_b创建一个set,那么我们可以提前一次完成这个操作,然后只需使用any(x in set_b for x in a)
但是,这样做有以下问题:a)如上所述,它很难理解;b)它忽略了set内置机制。通常用于集合交集的运算符&要求两侧都是set,但是命名方法.intersection则不需要。因此,set_b.intersection(a)就可以胜任。
将它们全部组合起来,我们可以得到:
set_b = set(list_b)
list_c = [a for a in list_a if not set_b.intersection(a)]

点赞,因为你选择使用集合交集 + 让我意识到我可以将列表传递给 set.intersection 而不是像我之前一直认为的传递一个集合;-) - DevLounge
1
谢谢您的解释和解决方案。我已经检查了关于运行时间的三个选项:1)“任何”- Mark M的解决方案:[l for l in A if not any(n in l for n in B)] 2)连续循环遍历list_B的值 3)Karl Knechtel的Set_b.Intersection-Solution结果发现,选项1)和2)的运行时间相当,而第三个选项是最快的。在我的测试示例中,Set_B_intersection方法大约快了5倍! - DimaWest

2

马克的回答很好,但难以阅读。

顺便提一下,您还可以利用集合:

>>> set_b = set(list_b)
>>> [l for l in list_a if not set_b.intersection(l)]
[[3, 4, 2], [4]]

谢谢!这似乎是解决问题的最快方法! - DimaWest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接