如何在其他列表中进行比较和插入的高效循环

时间:2013-05-24 14:09:49

标签: python django list loops

我正在处理一个处理大型数据库的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)

3 个答案:

答案 0 :(得分:1)

我建议将tids放入O(1)摊销查询速度的集合中(而不是列表的O(N)):

set_tids = set(tids)

for循环之前,然后

if e.tid in set_tids

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

答案 1 :(得分:0)

您还可以只访问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版本的列表。

答案 2 :(得分:0)

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

我假设您正在检查(e.yes_count > 0 or e.no_count > 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)