Python字典理解非常慢

Python字典理解非常慢,python,list,dictionary,Python,List,Dictionary,我有一本字典d1和一个列表l1 字典键是字符串,值是我自己定义的对象。如果有帮助的话,我可以更详细地描述对象,但是现在,对象有一个列表属性名称,并且名称的一些元素可能出现在l1中,也可能不出现 我想做的是扔掉字典d1中的任何元素,其中所述元素中对象的name属性不包含出现在l1中的任何元素 举个简单的例子: l1 = ['cat', 'dog', 'mouse', 'horse', 'elephant', 'zebra', 'lion', 'snake', 'fly'] d1 =

我有一本字典
d1
和一个列表
l1

字典键是字符串,值是我自己定义的对象。如果有帮助的话,我可以更详细地描述对象,但是现在,对象有一个列表属性
名称
,并且
名称
的一些元素可能出现在
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”/代码>(不包括水果和蔬菜)的键值对,并且不会包含第五个键值PAR,因为在“代码> 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'],}
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}

这似乎是可行的,但对于大字典,7000多个条目,需要大约20秒才能完成。就其本身而言,这并不可怕,但我需要在一个迭代10000次的循环中执行此操作,因此目前它不可行。有没有关于如何快速执行此操作的建议?

您正在有效地计算字典值中出现的每个列表与列表
l1
的集合交集。由于涉及到线性搜索,因此对集合交点使用列表效率很低。您应该将
l1
转换为一个集合,并改用
set.intersection()
或集合成员资格测试(取决于结果是否为集合)

完整代码可能如下所示:

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'],}
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

问题不是dict理解,而是其中的嵌套列表理解。您每次都在重复相同的键。这种事情最好用电视机来做

s1 = set(l1)
d2 = {k: list(s1.intersection(v)) for k, v in d1.items()}
使用
设置

>>> 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'])}

如果您将
l1
转换为
set
并稍微修改dict理解,您可以使其工作速度大约快三倍:

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
的所有值转换为集合的速度太慢

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())))

大家注意:他使用的是python 2.7而不是3,因为使用了
itertitems
,不要让
print()
蒙蔽了你。python 2.7有dict理解能力?@Claudiu是的,它们是后端口+1,用于提供完全可复制的示例这有几个原因导致运行缓慢,首先,对于dict,您可以像d1中的k一样迭代
。默认情况下,字典会迭代键,在Python 2.7中,dict.keys会返回一个列表。另一个原因是,您正在检查Membership的列表,您从来没有真正想要这样做,因为它在O(N)中运行。然而,检查一个集合的成员资格需要O(1)。为了更有效地使用
iteritems
,如果允许设置
d1
d2
中的值,也会更有效。为了完全有效,我会将您的第一个代码更改为
>>d2=(k[s代表v中的s,如果s代表l1中的s]),对于k,v代表d1中的v。iteritems())>>d2={k:v代表k,如果v}
@jamylak:你认为这会明显快于
for
循环吗?我个人认为它至少要丑得多。:)它会比你现在第一个代码的代码效率更高,它将再次运行d2。不确定第二个,必须
timeit