【问题标题】:Data filtering performance with dict comprehension具有字典理解的数据过滤性能
【发布时间】:2021-06-14 16:37:35
【问题描述】:

在试图找到过滤字典的最有效方法时,我偶然发现了一种奇怪的行为。

我做了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 对每个 dict 值都应用两个 lambda,而在 TEST2 中,条件 if x['id'] >= 3 and x['name'].find('a') != -1 被延迟评估,x['name'].find('a') 不评估 ids

标签: python performance dictionary filter dictionary-comprehension


【解决方案1】:

因为您在括号中评估您的谓词,所以会创建一个列表并检查所有项目的所有条件:all([f(x) for f in predicates])。这意味着与其他方法相比,您正在测试第二个谓词的更多项目,并最终将苹果与橙子进行比较。

all() 函数可以使用生成器,在这种情况下,一旦条件之一为 False,它将立即停止,从而使评估短路。删除括号:all(f(x) for f in predicates),您将比较橘子和橘子。

请注意,x['name'].find('a') != -1 可以替换为 'a' in x['name'],恕我直言,这样会更清楚

【讨论】:

    【解决方案2】:

    all 评估这两个条件,而 TEST_2 中的 and 是惰性评估的,这意味着仅针对 id >= 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
    

    【讨论】:

      【解决方案3】:

      我认为这是由于使用了这个问题Python: any() unexpected performance中回答的生成器表达式

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-22
        • 2018-04-07
        • 1970-01-01
        • 2018-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多