【问题标题】:How to do an efficient loop with comparisons and insertions in other lists如何在其他列表中进行比较和插入的有效循环
【发布时间】:2013-05-24 14:09:48
【问题描述】:

我正在开发一个处理大型数据库的 Web 应用程序 (Python/Django),我需要优化此循环以获得更好的执行时间。

我有一个条目列表,每个条目都有一个 yes_count 属性、一个 no_count 属性和一个 tid 属性。

我需要根据比率创建两个新列表 = yes_count / (yes_count + no_count)

使用内置函数(或更快的方法)是不是更好的方法?

yes_entries = []
no_entries = []

for e in entries:
    if e.tid in tids:
        if e.yes_count > 0 or e.no_count > 0:
            ratio = e.yes_count / (e.yes_count + e.no_count)
            if ratio > 0.75:
                yes_entries.append(e.tid)
            elif ratio < 0.25:
                no_entries.append(e.tid)

【问题讨论】:

  • yes/no_count
  • 你为什么不在你的数据库中做这个,它会比任何python代码都快。
  • 是的,正如@JochenRitzel 所说,所有这些都可以在数据库中的单个查询中更有效地完成。

标签: python django list loops


【解决方案1】:

我建议将tids 设置为 O(1) 分期查找速度的集合(而不是 O(N) 的列表):

set_tids = set(tids)

for 循环之前,然后

if e.tid in set_tids

否则你给出的其余代码看起来很优化

【讨论】:

  • 好建议,但我们实际上无法查看tids 是否已经是来自 sn-p 的set
【解决方案2】:

您还可以通过只访问一次e.tide.yes_counte.no_count 并将它们存储在变量中来节省一些时间:

for e in entries:
    tid = e.tid
    if tid in tids:
        yes_count = e.yes_count
        no_count = e.no_count
        if yes_count > 0 or no_count > 0:
            ratio = yes_count / (yes_count + no_count)
            if ratio > 0.75:
                yes_entries.append(tid)
            elif ratio < 0.25:
                no_entries.append(tid)

您还可以通过缓存 no_entries.append 和 yes_entries.append 来节省时间:

yes_entries_append = yes_entries.append
no_entries_append = no_entries.append

for e in entries:
    tid = e.tid
    if tid in tids:
        yes_count = e.yes_count
        no_count = e.no_count
        if yes_count > 0 or no_count > 0:
            ratio = yes_count / (yes_count + no_count)
            if ratio > 0.75:
                yes_entries_append(tid)
            elif ratio < 0.25:
                no_entries_append(tid)

但到那时,你可能开始变得愚蠢了。

另一个可能更愚蠢的尝试是看看使用过滤器是否更快。在 python2 中, filter 返回一个列表,这意味着您要对其进行两次迭代,这不太理想。但是,我们有 itertools 来帮助我们:

def filterfunc(e):
    return (e.tid in tids) and (yes_count > 0 or no_count > 0)

for e in itertools.ifilter(filterfunc, entries):
    tid = e.tid
    yes_count = e.yes_count
    no_count = e.no_count
    ratio = yes_count / (yes_count + no_count)
    if ratio > 0.75:
        yes_entries_append(tid)
    elif ratio < 0.25:
        no_entries_append(tid)

下一个问题是我们再次访问 e 上的字段两次。让我们用一些迭代器魔法来解决这个问题:

def filterfunc(t):
    tid, yes_count, no_count = t
    return (tid in tids) and (yes_count > 0 or no_count > 0)

for tid, yes_count, no_count in itertools.ifilter(filterfunc, itertools.imap(attrgetter(["tid", "yes_count", "no_count"]), entries)):
    ratio = yes_count / (yes_count + no_count)
    if ratio > 0.75:
        yes_entries_append(tid)
    elif ratio < 0.25:
        no_entries_append(tid)

由您和您的分析器从我建议的所有选项中确定最佳方法。

另外,如果您使用的是 python3,请使用 filter 而不是 itertools.ifilter,因为它返回的是生成器而不是 python2 的版本列表。

【讨论】:

  • 这些技巧真的能提高性能吗?只是好奇。我对 CPython 有多聪明(或愚蠢?)知之甚少。
  • 是的,在一定程度上 - 但实际获得的数量在所有循环中都是微不足道的。
【解决方案3】:

注意:以下是一种更紧凑的解决方案的尝试,不一定更有效。一些分析可能是有序的。

我假设您正在检查(e.yes_count &gt; 0 or e.no_count &gt; 0),这样您就不会被零除。假设这是一个非常罕见的情况,我会简单地将比率计算包装为一个处理ZeroDivisonError 异常的函数。在这种情况下,我们为该边缘情况返回零。

def get_ratio(y, n):
    try:
        return y / (y + n)
    except ZeroDivisionError:
        return 0

接下来,我们创建一个生成器,它遍历条目并返回候选值的比率和 tid。

tidset = set(tids)  # assuming tids is not yet a set()
ratios = ((get_ratio(e.yes_count, e.no_count), e.tid) 
            for e in entries if e.tid in tidset)

最后,我们遍历生成器并将它们附加到适当的列表中:

yes_entries, no_entries = [], []
for ratio, tid in ratios:
    (yes_entries, no_entries)[ratio < 0.75].append(tid)

【讨论】:

    猜你喜欢
    • 2015-04-19
    • 2022-08-05
    • 2019-02-28
    • 1970-01-01
    • 2022-11-05
    • 2018-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多