最优雅/ 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%的元素复制到新列表中,然后为该新列表运行函数。但是,我希望必须有一种更优雅的方法。有什么建议吗?
答案 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)%,则threshold
为listItems
。 / 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