Python的all()/ any()之类的方法用于列表的一部分/一部分?

时间:2019-07-09 15:55:48

标签: python loops for-in-loop

最优雅/ pythonic的实现方式是:“如果列表中总值的x%大于y,则返回true”。我目前已实现一个功能:

def check(listItems, val):
   '''A method to check all elements of a list against a given value.
   Returns true if all items of list are greater than value.'''
   return all(x>val for x in listItems)

但是对于我的用例来说,等待这个特殊条件是非常昂贵的,而且没有用。如果列表中〜80%的项目大于给定值,我想继续。 我想到的一种方法是按降序对列表进行排序,创建另一个列表,并将列表中80%的元素复制到新列表中,然后为该新列表运行函数。但是,我希望必须有一种更优雅的方法。有什么建议吗?

5 个答案:

答案 0 :(得分:7)

听起来您正在处理很长的列表,这就是为什么这样做很昂贵的原因。如果可以,请您在条件满足后尽快退出,如果这样很好。 any()可以做到这一点,但是您要避免在将整个列表传递给any()之前先阅读整个列表。一种选择是使用itertools.accumulate来保持运行的True值的总数,并将其传递给任何值。像这样:

from itertools import accumulate

a = [1, 2, 2, 3, 4, 2, 4, 1, 1, 1]

# true if 50% are greater than 1
goal = .5 * len(a) # at least 5 out of 10   
any( x > goal for x in accumulate(n > 1 for n in a))

accumulate不需要读取整个列表-它将开始传递到目前为止所看到的True值的数量。 any应该在找到真值(在上述情况下为索引5)时立即短路。

答案 1 :(得分:2)

那呢:

def check(listItems, val, threshold=0.8):
    return sum(x > val for x in listItems) > len(listItems) * threshold

它指出:如果check中大于True的元素中占{{1}%(默认为0.80)%,则thresholdlistItems。 / p>

答案 2 :(得分:2)

您可以为此使用filter。到目前为止,这是最快的方法。请参考我的其他答案,因为它比该方法更快。

def check(listItems, val, goal=0.8):
    return len((*filter(val.__lt__, listItems),)) >= len(listItems) * goal

与此我的另一个问题中的方法一起测试的结果时间是:

1.684135717988247

答案 3 :(得分:1)

按顺序检查每个项目。

  • 如果您感到满意,请尽早返回True。

  • 如果您到达了永远无法满足的地步,即使以后的每一项都通过了测试,那么请尽早返回False。

  • 否则,请继续进行下去(以防后面的元素帮助您满足要求)。

在上面的评论中,这与FatihAkici的想法相同,但是有进一步的优化。

def check(list_items, ratio, val):
    passing = 0
    satisfied = ratio * len(list_items)
    for index, item in enumerate(list_items):
        if item > val:
            passing += 1
        if passing >= satisfied:
            return True
        remaining_items = len(list_items) - index - 1
        if passing + remaining_items < satisfied:
            return False

答案 4 :(得分:0)

我不想为Mark Meyer的回答而赞扬,因为他提出了使用accumulate的概念,以及它们的更多Python /可读性,但是如果您正在寻找“最快”的方法,那么通过使用map来修改他的方法比使用理解来更快。

any(map(goal.__le__, accumulate(map(val.__lt__, listItems))))

只需测试:

from timeit import timeit
from itertools import accumulate

def check1(listItems, val):
    goal = len(listItems)*0.8
    return any(x > goal for x in accumulate(n > val for n in listItems))

def check2(listItems, val):
    goal = len(listItems)*0.8
    return any(map(goal.__le__, accumulate(map(val.__lt__, listItems))))

items = [1, 2, 2, 3, 4, 2, 4, 1, 1, 1]

for t in (check1, check2):
    print(timeit(lambda: t(items, 1)))

结果是:

3.2596251670038328
2.0594907909980975