我正在尝试优化嵌套的for循环,将数组中的元素与数组中的其余元素进行比较。
有两个部分,第一部分是例如,一个数组有3个元素,每个元素都是一个字典:
[{"someKey_1":"a"}, {"someKey_1":"b"}, {"somekey_1":"a"}]
第一次迭代(第一个元素与第二个元素比较):
测试两个元素的“someKey”键,因为a!= b,然后我们什么都不做
第2次迭代(第1个元素与第3个元素比较):
测试两个元素的“someKey”键,因为a == a,我们做一些逻辑
代码:
for idx, val in enumerate(set_of_pk_values):
for idx_2, val_2 in enumerate(set_of_pk_values):
if (val['someKey'] == val_2['someKey'] and idx != idx_2):
#Some Logic
第二部分与上一个例子(列表中的3个项目)非常相似,在同一个字典中,我们有一个与一个键相关联的数组(现在有一个字典,在数组的每个元素中都有两个键) ,让我们说:
[{"someKey_1":[b,f]}{"someKey_2":a},
{"someKey_1":[e,f]}{"someKey_2":b},
{"somekey_1":[h,k]}{"someKey_2":c}]
第一次迭代(第一个元素与第二个元素比较):
使用键遍历数组:someKey_1
b==b
(第二个元素的someKey_2),然后做一些逻辑
f!=b
(第二个元素的someKey_2),没有逻辑完成
第二次迭代(第一个元素与第三个元素比较):
使用键遍历数组:someKey_1
b==c
(第三个元素的someKey_2),然后做一些逻辑
f!=c
(第3个元素的someKey_2),没有逻辑完成
代码:
for idx, val in enumerate(set_of_pk_values):
for idx_2, val_2 in enumerate(set_of_pk_values):
for pred in val['someKey_1']:
if(val_2['someKey_2'] == pred):
#Some Logic
目前第一个嵌套循环的运行时间:21秒,第二个嵌套循环约为19秒。与其他流程(1-2秒)相比,这部分显然是一个瓶颈。
有人能指出我如何优化这段简单但非常耗时的代码吗?
答案 0 :(得分:5)
首先,我相信这应该发布在CodeReview上,而不是StackOverflow。
StackOverflow用于获取无法正常工作的代码的帮助 CodeReview用于获取有效的代码的帮助,但是你想要做得更好。
其次,这里有一些关于优化它的建议:
enumerate()
。以下是我将如何重写您的第一个场景:
# Check if two dictionary with the key 'predecessor' are the same,
# and they are NOT the same index (otherwise it would be comparing themselves)
# set_of_pk_values is an array of dictionaries.
for idx, val in enumerate(set_of_pk_values):
for val_2 in set_of_pk_values[idx+1:]: # Note the slice and lack of enumerate
if (val['predecessor'] == val_2['predecessor']): # Don't waste time checking indexes
# Do Something Here, also we don't want to compare itself, because it will be true
而不是for\if
,请使用if in
:
for idx, val in enumerate(set_of_pk_values):
if val in set_of_pk_values[idx+1:]:
# Do Something Here, also we don't want to compare itself, because it will be true.
如果你真的想要枚举,因为你只需要多次相同的枚举,我只需要在循环外执行一次并将其存储在变量中,然后循环它。这就是我的意思:
我不对,下面的内容不起作用,请参阅评论。
# Doesn't work, see comments.
# from itertools import islice
# whatIEnumerate = enumerate(set_of_pk_values)
# for idx, val in whatIEnumerate:
# for idx_2, val_2 in islice(whatIEnumerate, idx+1, None):
# ...
答案 1 :(得分:4)
伙计,这很糟糕:
for idx, val in enumerate(set_of_pk_values):
for idx_2, val_2 in enumerate(set_of_pk_values):
if (val['someKey'] == val_2['someKey'] and idx != idx_2):
do_stuff()
只需跳过您已经尝试过的元素的索引(==
是可交换的):
for idx, val in enumerate(set_of_pk_values[:-1]):
for val_2 in set_of_pk_values[idx+1:]
if (val['someKey'] == val_2['someKey']):
do_stuff()
重命名。它很丑。
for idx, first_dic in enumerate(set_of_pk_values[:-1]):
for second_dic in set_of_pk_values[idx+1:]
if (first_dic['someKey'] == second_dic['someKey']):
do_stuff()
现在,每次循环迭代中的if
都很麻烦。通过过滤缩小列表来替换它:
hits = []
for idx, first_dic in enumerate(set_of_pk_values[:-1]):
hits += (first_dic['someKey'], filter(lambda dic: dic['someKey'] == first_dic['someKey'], set_of_pk_values[idx:1]) )
hits
现在包含一个匹配元组列表:hits[i] = (
mathing first id ,
匹配列表,其中包含idx>第一个元素)
。
字典查找很昂贵。使用operator.itemgetter
替换它们:
from operator import itemgetter
getter = itemgetter("someKey")
hits = []
for idx, first_dic in enumerate(set_of_pk_values[:-1]):
hits += (getter(first_dic), filter(lambda dic: getter(dic) == getter(first_dic), set_of_pk_values[idx:1]) )
坐下来看看。 for循环的迭代并不真正依赖于最后一次迭代的状态。列表理解的时间。
from operator import itemgetter
getter = itemgetter("someKey")
hits = [ ( getter(first_dic), filter(lambda dic: getter(dic) == getter(first_dic), set_of_pk_values[idx:-1]) ) for idx, first_dic in enumerate(set_of_pk_values[:-1])]
答案 2 :(得分:3)
Python中的迭代比C中的迭代要慢。最好使用Python库在C中进行迭代。有趣的是,这里没有人提到itertools
......
itertools.combinations
在C中创建唯一组合,然后返回组合的生成器:
import itertools
import operator
getter = operator.itemgetter('someKey_1')
for a, b in itertools.combinations(set_of_pk_values, 2):
if getter(a) == getter(b):
# logic?