给定一个大字典,(实际上是一个defaultdict
),有数千个键值对(字符串:整数)。
我希望根据值上的简单条件(例如value > 20
)删除大约一半的键/值对。
最快的方法是什么?
答案 0 :(得分:3)
我认为基于迭代器的dict再生是一种很好的方法:
newdict = dict((k,v) for k,v in d.iteritems() if v > 20)
或
newdict = {k: v for k,v in d.iteritems() if v > 20}
在Python 2.7中。
请注意,您必须小心d = {k: v for k,v in d.iteritems() if v > 20}
。相反,你应该调用
d.clear()
d.update({k: v for k,v in d.iteritems() if v > 20})
这样,d
中对数据的旧引用也将引用过滤后的数据。
修改强>
让我们通过基准比较这个线程中讨论的三种方法:
结果显然取决于要删除的字典的比例(这可能是不可预测的,但只有线程开启者知道)。它也可能高度依赖于垃圾收集的活动,在timeit
期间默认为switched off。它被关闭以减少测量中的噪声。但是,这可以完全改变方法的排名。我们来看看:
预先基准代码:
from timeit import timeit
n = 2
N = "10**7"
mod = "9999999"
gc = "False"
print "N: %s; mod: %s; garbage collection: %s" % (N, mod, gc)
setup ="""
N = %s
mod = %s
d = {x:1 for x in xrange(N)}
if %s:
gc.enable()""" % (N, mod, gc)
t = timeit(
'd = {k:v for k, v in d.iteritems() if not k % mod}',
setup=setup,
number=n)
print "%s times method 1 (dict comp): %.3f s" % (n, t)
t = timeit(
'''
for k, v in d.items():
if k % mod:
del d[k]
''',
setup=setup,
number=n)
print "%s times method 2 (key deletion within for loop over d.items()): %.3f s" % (n, t)
t = timeit('''
removekeys = [k for k, v in d.iteritems() if k % mod]
for k in removekeys:
del d[k]
''',
setup=setup,
number=n)
print "%s times method 3 (key deletion after list comp): %.3f s" %(n, t)
案例1(没有过滤掉字典中的任何项目):
启用垃圾收集:
N: 10**7; mod: 1; garbage collection: True
2 times method 1 (dict comp): 4.701
2 times method 2 (key deletion within for loop over d.items()): 15.782
2 times method 3 (key deletion after list comp): 2.024
禁用垃圾收集:
N: 10**7; mod: 1; garbage collection: False
2 times method 1 (dict comp): 4.701
2 times method 2 (key deletion within for loop over d.items()): 4.268
2 times method 3 (key deletion after list comp): 2.027
案例2(过滤掉字典的一半项目):
启用垃圾收集:
N: 10**7; mod: 2; garbage collection: True
2 times method 1 (dict comp): 3.449 s
2 times method 2 (key deletion within for loop over d.items()): 12.862 s
2 times method 3 (key deletion after list comp): 2.765 s
禁用垃圾收集:
N: 10**7; mod: 2; garbage collection: False
2 times method 1 (dict comp): 3.395 s
2 times method 2 (key deletion within for loop over d.items()): 4.175 s
2 times method 3 (key deletion after list comp): 2.893 s
案例3(几乎所有字典中的项目都被过滤掉了):
启用垃圾收集:
N: 10**7; mod: 9999999; garbage collection: True
2 times method 1 (dict comp): 1.217 s
2 times method 2 (key deletion within for loop over d.items()): 9.298 s
2 times method 3 (key deletion after list comp): 2.141 s
禁用垃圾收集:
N: 10**7; mod: 9999999; garbage collection: False
2 times method 1 (dict comp): 1.213 s
2 times method 2 (key deletion within for loop over d.items()): 3.168 s
2 times method 3 (key deletion after list comp): 2.141 s
在具有24 GB内存的Xeon E5630上,在Linux 2.6.32-34-generic上的64位Python 2.7.3上测量。峰值内存使用率低于10%(通过顶部监控)。
<强>结论强>
在任何情况下我都会选择方法1,因为它比方法3更清晰,并且性能差异不大。但这完全取决于你。
答案 1 :(得分:2)
dict((k,v) for k,v in original_dict.iteritems() if condition)
这会根据您的条件创建一个新的字典,在一个内存友好的(由于iteritems
和生成器)和有效的方式(不是很多函数/方法调用)。
答案 2 :(得分:2)
供参考:
>>> from timeit import timeit as t
# Create a new dict with a dict comprehension
>>> t('x={k:v for k, v in x.iteritems() if v % 2}', 'x={x:x for x in xrange(10**7)}', number=30)
100.02150511741638
# Delete the unneeded entries in-place
>>> t('''for k, v in x.items():
... if v % 2 != 0:
... del x[k]''', 'x={x:x for x in xrange(10**7)}', number=30)
89.83732604980469
(我假设模数== 0比较的速度与&lt; 20的顺序相同,但这与这些测试并不相关。)对于一个非常大的字典,它们大约相同的数量级,但我觉得就地速度要快一点。
答案 3 :(得分:1)
如果您可以制作新的dict
:
dict((k, v) for k,v in D.iteritems() if k != "foo")
如果你真的想修改原文:
removekeys = [k for k, v in D.iteritems() if k == "foo"]
for k in removekeys: del D[k]
我不确定这些是快速 est ,但它们应该很快。