The Python Oracle

Python all()/any() like method for a portion/part of list?

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Quiet Intelligence

--

Chapters
00:00 Python All()/Any() Like Method For A Portion/Part Of List?
00:50 Answer 1 Score 2
01:08 Answer 2 Score 1
01:43 Accepted Answer Score 7
02:27 Answer 4 Score 0
03:02 Thank you

--

Full question
https://stackoverflow.com/questions/5695...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #loops #forinloop

#avk47



ACCEPTED ANSWER

Score 7


It sounds like you are dealing with long lists which is why this is costly. If would be nice if you could exit early as soon as a condition is met. any() will do this, but you'll want to avoid reading the whole list before passing it to any(). One options might be to use itertools.accumulate to keep a running total of True values and the pass that to any. Something like:

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 won't need to read the whole list — it will just start passing the number of True values seen up to that point. any should short-circuit as soon as it finds a true value, which in the above case is at index 5.




ANSWER 2

Score 2


What about this:

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

It states: check is True if more than threshold% (0.80 by default) of the elements in listItems are greater than val.




ANSWER 3

Score 1


Check each item in order.

  • If you reach a point where you are satisfied then return True early.

  • If you reach a point where you can never be satisfied, even if every future item passes the test, then return False early.

  • Otherwise keep going (in case the later elements help you satisfy the requirement).

This is the same idea as FatihAkici in the comments above, but with a further optimization.

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



ANSWER 4

Score 0


I don’t want to take credit for Mark Meyer’s answer as he came up with the concept of using accumulate and any as well as theirs being more pythonic/readable, but if you’re looking for the "fastest" approach then modifying his approach with using map vs using comprehensions is faster.

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

Just to test:

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)))

The results are:

3.2596251670038328
2.0594907909980975