具有字典理解的数据过滤性能
Data filtering performance with dict comprehension
在寻找最有效的字典过滤方法时,我偶然发现了一个奇怪的行为。
我做了 4 个测试,第一个按顺序过滤字典。
第二种结合规则进行一次过滤(实际上是最有效的方式)。
然后,我尝试使其更通用,以便过滤器可以与任意数量的谓词一起使用,最终可以由用户定义,而不是硬编码。
而且我意识到将 predicated 与 all
相结合比一个接一个地进行两次过滤效率低得多。
有什么可以解释的?这是性能不佳的 all()
函数吗?
您是否会建议任何其他方法来以通用方式提高性能?
# TEST 1 (Took 1.717677354812622s)
y = {k:x for k,x in y.items() if x['id'] >= 3 }
y = {k:x for k,x in y.items() if x['name'].find('a') != -1 }
# TEST 2 (Took 1.411365032196045s)
y = {k:x for k,x in y.items() if x['id'] >= 3 and x['name'].find('a') != -1 }
# TEST 3 (Took 3.4738941192626953s)
predicates = [
lambda x: x['id'] >= 3,
lambda x: x['name'].find('a') != -1
]
y = {k:x for k,x in y.items() if all([f(x) for f in predicates]) }
# TEST 4 (Took 2.4156315326690674s)
predicates = [
lambda x: x['id'] >= 3,
]
y = {k:x for k,x in y.items() if predicates[0](x) }
predicates = [
lambda x: x['name'].find('a') != -1
]
y = {k:x for k,x in y.items() if predicates[0](x) }
测试台:
from var_dump import var_dump
import time
start_time = time.time()
for p in range(0,1000000):
users = {
1: {'id': 1, 'name': "toto"},
2: {'id': 2, 'name': "titi"},
3: {'id': 3, 'name': "tata"},
4: {'id': 4, 'name': "tutu"},
5: {'id': 5, 'name': "john"},
6: {'id': 6, 'name': "jane"}
}
y = users
#-> test goes here
print(y)
print("--- %s seconds ---" % (time.time() - start_time))
我认为这是由于使用了生成器表达式,正如这个问题中所回答的那样
all
评估两个条件,而 TEST_2 中的 and
被延迟评估,这意味着第二个条件仅针对 ids >= 3 进行评估。
通过简单的打印语句进行说明:
users = {
1: {'id': 1, 'name': "toto"},
2: {'id': 2, 'name': "titi"},
3: {'id': 3, 'name': "tata"},
4: {'id': 4, 'name': "tutu"},
5: {'id': 5, 'name': "john"},
6: {'id': 6, 'name': "jane"}
}
def check_1(item):
print("check_1")
return item['id'] >= 3
def check_2(item):
print("check_2")
return item['name'].find('a') != -1
# all condition
print("TEST AND")
for k, v in users.items():
check_1(v) and check_2(v)
# all condition
print("TEST ALL")
for k, v in users.items():
all([check_1(v), check_2(v)])
输出:
TEST AND
check_1
check_1
check_1
check_1
check_2
check_1
check_2
check_1
check_2
TEST ALL
check_1
check_2
check_1
check_2
check_1
check_2
check_1
check_2
check_1
check_2
check_1
check_2
因为您在括号中评估谓词,所以会创建一个列表,并且将检查所有项目的所有条件:all([f(x) for f in predicates])
。这意味着与其他方法相比,您正在为更多项目测试第二个谓词,并最终将苹果与橙子进行比较。
all() 函数可以使用生成器,在这种情况下,只要其中一个条件为假,它就会停止,从而使评估短路。删除括号:all(f(x) for f in predicates)
并且您将比较橙子和橙子。
请注意,x['name'].find('a') != -1
可以替换为 'a' in x['name']
,恕我直言,这样会更清楚
在寻找最有效的字典过滤方法时,我偶然发现了一个奇怪的行为。
我做了 4 个测试,第一个按顺序过滤字典。
第二种结合规则进行一次过滤(实际上是最有效的方式)。
然后,我尝试使其更通用,以便过滤器可以与任意数量的谓词一起使用,最终可以由用户定义,而不是硬编码。
而且我意识到将 predicated 与 all
相结合比一个接一个地进行两次过滤效率低得多。
有什么可以解释的?这是性能不佳的 all()
函数吗?
您是否会建议任何其他方法来以通用方式提高性能?
# TEST 1 (Took 1.717677354812622s)
y = {k:x for k,x in y.items() if x['id'] >= 3 }
y = {k:x for k,x in y.items() if x['name'].find('a') != -1 }
# TEST 2 (Took 1.411365032196045s)
y = {k:x for k,x in y.items() if x['id'] >= 3 and x['name'].find('a') != -1 }
# TEST 3 (Took 3.4738941192626953s)
predicates = [
lambda x: x['id'] >= 3,
lambda x: x['name'].find('a') != -1
]
y = {k:x for k,x in y.items() if all([f(x) for f in predicates]) }
# TEST 4 (Took 2.4156315326690674s)
predicates = [
lambda x: x['id'] >= 3,
]
y = {k:x for k,x in y.items() if predicates[0](x) }
predicates = [
lambda x: x['name'].find('a') != -1
]
y = {k:x for k,x in y.items() if predicates[0](x) }
测试台:
from var_dump import var_dump
import time
start_time = time.time()
for p in range(0,1000000):
users = {
1: {'id': 1, 'name': "toto"},
2: {'id': 2, 'name': "titi"},
3: {'id': 3, 'name': "tata"},
4: {'id': 4, 'name': "tutu"},
5: {'id': 5, 'name': "john"},
6: {'id': 6, 'name': "jane"}
}
y = users
#-> test goes here
print(y)
print("--- %s seconds ---" % (time.time() - start_time))
我认为这是由于使用了生成器表达式,正如这个问题中所回答的那样
all
评估两个条件,而 TEST_2 中的 and
被延迟评估,这意味着第二个条件仅针对 ids >= 3 进行评估。
通过简单的打印语句进行说明:
users = {
1: {'id': 1, 'name': "toto"},
2: {'id': 2, 'name': "titi"},
3: {'id': 3, 'name': "tata"},
4: {'id': 4, 'name': "tutu"},
5: {'id': 5, 'name': "john"},
6: {'id': 6, 'name': "jane"}
}
def check_1(item):
print("check_1")
return item['id'] >= 3
def check_2(item):
print("check_2")
return item['name'].find('a') != -1
# all condition
print("TEST AND")
for k, v in users.items():
check_1(v) and check_2(v)
# all condition
print("TEST ALL")
for k, v in users.items():
all([check_1(v), check_2(v)])
输出:
TEST AND
check_1
check_1
check_1
check_1
check_2
check_1
check_2
check_1
check_2
TEST ALL
check_1
check_2
check_1
check_2
check_1
check_2
check_1
check_2
check_1
check_2
check_1
check_2
因为您在括号中评估谓词,所以会创建一个列表,并且将检查所有项目的所有条件:all([f(x) for f in predicates])
。这意味着与其他方法相比,您正在为更多项目测试第二个谓词,并最终将苹果与橙子进行比较。
all() 函数可以使用生成器,在这种情况下,只要其中一个条件为假,它就会停止,从而使评估短路。删除括号:all(f(x) for f in predicates)
并且您将比较橙子和橙子。
请注意,x['name'].find('a') != -1
可以替换为 'a' in x['name']
,恕我直言,这样会更清楚