2012-08-10 40 views
12

我有一本字典d1和列表l1Python字典的理解很慢

字典键是字符串,值是我定义自己的对象。如果有帮助,我可以描述对象更详细,但就目前而言,对象有一个列表属性names,以及一些name元素可能会或可能不会出现在l1

我想做的是丢掉字典d1中的任何元素,其中该元素中的对象的name属性不包含任何出现在l1中的元素。

作为一个简单的例子:

l1 = ['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly'] 

d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 

因此所得到的字典将或多或少相同,但每个列表中的元素将是从1键值对到4不包括水果和蔬菜,并且将不包含5键值面值为无家具值出现在l1

为此,我使用了嵌套列表/字典理解这是这样的:

d2 = {k: [a for a in l1 if a in d1[k]] for k in d1.keys()} 
print(d2) 

>>>>{'1': ['dog', 'mouse', 'horse'], 
    '3': ['cat', 'dog', 'mouse'], 
    '2': ['cat', 'mouse', 'horse'], 
    '5': [], 
    '4': ['cat', 'dog', 'horse']} 

d2 = {k: v for k,v in d2.iteritems() if len(v)>0} 
print(d2) 

>>>>{'1': ['dog', 'mouse', 'horse'], 
    '3': ['cat', 'dog', 'mouse'], 
    '2': ['cat', 'mouse', 'horse'], 
    '4': ['cat', 'dog', 'horse'],} 

这似乎是工作,但对于大辞典,7000名+的项目,它需要大约20秒的工作,通过。本身并不可怕,但我需要在循环内进行10,000次迭代,因此目前不可行。有关如何快速做到这一点的任何建议?

+1

注意给大家:他是用Python 2.7版不是3,由于使用'itertitems',不要让打印' ()'骗你 – jamylak 2012-08-10 14:08:16

+0

python 2.7有词典理解吗? – Claudiu 2012-08-10 14:12:47

+0

@Claudiu是的,他们是backported – jamylak 2012-08-10 14:13:49

回答

13

您有效地计算每个列表发生的文化在字典中的值的交集与列表l1。由于涉及到线性搜索,使用列表设置交叉点效率相当低。你应该把l1成一组,并使用set.intersection()或集合成员资格测试,而不是(这取决于它是否是可以接受的结果是一组再次)。

完整的代码看起来是这样的:

l1 = set(l1) 
d2 = {k: [s for s in v if s in l1] for k, v in d1.iteritems()} 
d2 = {k: v for k, v in d2.iteritems() if v} 

代替两个字典推导的,它也可能是最好使用单一for循环这里:

l1 = set(l1) 
d2 = {} 
for k, v in d1.iteritems(): 
    v = [s for s in v if s in l1] 
    if v: 
     d2[k] = v 
+0

为了提高效率,我会把你的第一个代码改成>>> >>> d2 =((k,[s代表v中的s,如果s代入l1])代替k,v代替d1.iteritems()) >>> d2 = {k:v for k,v in d2 if v}'。 – jamylak 2012-08-10 14:33:41

+0

@jamylak:你认为这会比'for'循环更快吗?我认为这至少是非常丑陋的。 :) – 2012-08-10 14:36:22

+0

那么它会比现在的代码更有效率,它将再次通过d2运行。不确定第二个,将不得不''timeit' – jamylak 2012-08-10 14:39:43

4

问题不字典的理解,但嵌套的列表理解。您每次都在迭代相同的密钥。这种事情最好用集合来完成。

s1 = set(l1) 
d2 = {k: list(s1.intersection(v)) for k, v in d1.items()} 
+2

为了更有效地使用'iteritems' – jamylak 2012-08-10 14:15:00

+1

如果允许将'd1'和'd2'中的值设为集合,它也会更高效。 – 2012-08-10 14:23:43

0

使用set

>>> l1 = ['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly'] 
>>> d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 
>>> l1_set = set(l1) 
>>> d2 = dict((k, set(d1[k]) & l1_set) for k in d1.keys()) 
>>> d2 
{'1': set(['horse', 'mouse', 'dog']), '3': set(['mouse', 'dog', 'cat']), '2': set(['horse', 'mouse', 'cat']), '5': set([]), '4': set(['horse', 'dog', 'cat'])} 
>>> d2 = dict((k, v) for k,v in d2.iteritems() if v) 
>>> d2 
{'1': set(['horse', 'mouse', 'dog']), '3': set(['mouse', 'dog', 'cat']), '2': set(['horse', 'mouse', 'cat']), '4': set(['horse', 'dog', 'cat'])} 
0

如果转换l1set和稍微修改一下字典理解,你可以得到更快的这方面的工作大致三次:

l1 = set(['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly']) 

d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 

d2 = {k: [a for a in d1[k] if a in l1] for k in d1.keys()} 
print(d2) 

这里是如何你可以基准性能:

import timeit 

t = timeit.Timer(
    "d2 = {k: [a for a in l1 if a in d1[k]] for k in d1.keys()}", 
    "from __main__ import (d1, l1)", 
    ) 
print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000) 

t = timeit.Timer(
    'd2 = {k: [a for a in d1[k] if a in l1] for k in d1.keys()}', 
    "from __main__ import (d1, l1)", 
    ) 
print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000) 

我在这里假设您无法控制d1,并且将所有d1的值转换为过滤之前的集合太慢。

1
l1 = ['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly'] 

d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 

def gen_items(valid_name_set, d): 
    for k, v in d.iteritems(): 
     intersection = valid_name_set.intersection(v) 
     if intersection: # not empty 
      yield (k, intersection) 

print dict(gen_items(set(l1), d1)) 

输出:

{'1': set(['dog', 'horse', 'mouse']), 
'2': set(['cat', 'horse', 'mouse']), 
'3': set(['cat', 'dog', 'mouse']), 
'4': set(['cat', 'dog', 'horse'])} 

或者:

from itertools import ifilter 
from operator import itemgetter 
set_l1 = set(l1) 
d2 = dict(ifilter(itemgetter(1), 
        ((k, set_l1.intersection(v)) for k, v in d1.iteritems())))