我正处于我一直致力于的项目的最后阶段。一切都运行顺利,但我有一个瓶颈,我无法解决。
我有一个元组列表。该列表的长度范围为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数据。
答案 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捎带到下一步?