Python:从列表中删除大量项目

时间:2009-08-12 16:28:47

标签: python

我正处于我一直致力于的项目的最后阶段。一切都运行顺利,但我有一个瓶颈,我无法解决。

我有一个元组列表。该列表的长度范围为40,000-1,000,000条记录。现在我有一个字典,其中每个(值,键)都是列表中的元组。

所以,我可能有

myList = [(20000, 11), (16000, 4), (14000, 9)...]
myDict = {11:20000, 9:14000, ...}

我想从列表中删除每个(v,k)元组。

目前我在做:

for k, v in myDict.iteritems():
    myList.remove((v, k))

从包含20,000个元组的列表中删除838个元组需要3到4秒。我很可能会从1,000,000的列表中删除更多像10,000个元组,所以我需要更快。

有更好的方法吗?

我可以提供用于测试的代码,如果需要,还可以提供来自实际应用程序的pickled数据。

8 个答案:

答案 0 :(得分:19)

你必须衡量,但我可以想象这会更有效:

myList = filter(lambda x: myDict.get(x[1], None) != x[0], myList)

因为查找发生在dict中,这更适合这种事情。但请注意,这将在删除旧列表之前创建一个新列表;所以有一个记忆权衡。如果这是一个问题,重新考虑您的容器类型为jkp建议可能是有序的。

修改:但是,请注意,如果None实际上在您的列表中 - 您必须使用其他“占位符”。

答案 1 :(得分:9)

要从大约1,000,000的列表中删除大约10,000个元组,如果值是可清除的,则最快的方法应该是:

totoss = set((v,k) for (k,v) in myDict.iteritems())
myList[:] = [x for x in myList if x not in totoss]

该套装的准备是一次性成本很小,很多时候都会节省进行元组解包和重新打包或元组索引的操作。分配给myList[:]而不是分配给myList在语义上也是重要的(如果有myList周围的任何其他引用,仅仅重新绑定名称是不够的 - 你真的想要重新绑定内容! - )。

我自己没有自己的测试数据进行时间测量,唉!,但是,让我知道它如何在测试数据上发挥作用!

如果值不可清除(例如,它们是子列表),则最快可能是:

sentinel = object()
myList[:] = [x for x in myList if myDict.get(x[0], sentinel) != x[1]]

或者也许(不应该在任何方面产生很大的不同,但我怀疑前一个更好 - 索引比解包和重新打包更便宜):

sentinel = object()
myList[:] = [(a,b) for (a,b) in myList if myDict.get(a, sentinel) != b]

在这两个变体中,哨兵成语用于抵御None的值(对于首选的基于集合的方法,这不是问题 - 如果值是可以清除的!),因为它会更便宜比if a not in myDict or myDict[a] != b(需要两个索引进入myDict)。

答案 2 :(得分:5)

每次调用myList.remove时,Python都必须扫描整个列表以搜索该项并将其删除。在最糟糕的情况下,您查找的每个项目每次都会在列表的末尾。

您是否尝试过执行“反向”操作:

newMyList = [(v,k) for (v,k) in myList if not k in myDict]

但是我真的不确定这种扩展程度如何,因为你要复制原始列表 - 可能会占用很多内存。

这里最好的替代方案可能是等待Alex Martelli发布一些令人兴奋的直观,简单和有效的方法。

答案 3 :(得分:2)

问题在于我认为您使用list作为要删除的容器,这是一种完全无序的类型。因此,要查找列表中的每个项目是线性操作(O(n)),它必须迭代整个列表,直到找到匹配项。

如果您可以将list替换为使用每个项目的set订购的其他容器(hash()?),则可以更快地执行每个匹配。< / p>

以下代码展示了如何使用我和Nick在这个帖子中提供的各种想法来实现这一目标:

list_set = set(original_list)
dict_set = set(zip(original_dict.values(), original_dict.keys()))
difference_set = list(list_set - dict_set)
final_list = []
for item in original_list:
    if item in difference_set:
        final_list.append(item)

答案 4 :(得分:2)

[(i, j) for i, j in myList if myDict.get(j) != i]

答案 5 :(得分:2)

尝试这样的事情:

myListSet = set(myList)
myDictSet = set(zip(myDict.values(), myDict.keys()))
myList = list(myListSet - myDictSet)

这会将myList转换为一个集合,交换myDict中的键/值并将它们放入一个集合中,然后找到差异,将其转回列表,然后分配它回到myList。 :)

答案 6 :(得分:0)

[i for i in myList if i not in list(zip(myDict.values(), myDict.keys()))]

答案 7 :(得分:0)

在大多数运行Python的计算机上,包含一百万个2元组的列表并不大。但是,如果您必须在原地进行移除,这是一种干净的方法:

def filter_by_dict(my_list, my_dict):
    sentinel = object()
    for i in xrange(len(my_list) - 1, -1, -1):
        key = my_list[i][1]
        if my_dict.get(key, sentinel) is not sentinel:
            del my_list[i]

更新实际上每个del花费O(n)使用C的memmove()将列表指针向下移动,所以如果有d dels,那么它是O(n*d)而不是O(n**2)。注意(1)OP表明d约== 0.01 * n和(2)O(n*d)努力将一个指针复制到内存中的其他位置......所以这种方法实际上可能会更快一些而不是一瞥就能表明。基准,有人吗?

你删除了dict中的项目之后你要对列表做什么?是否有可能将dict-filtering捎带到下一步?