The Python Oracle

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

Become part of the top 3% of the developers by applying to Toptal https://topt.al/25cXVn

--

Music by Eric Matyas
https://www.soundimage.org
Track title: Hypnotic Puzzle4

--

Chapters
00:00 Question
01:04 Accepted answer (Score 7)
01:57 Answer 2 (Score 2)
02:22 Answer 3 (Score 2)
02:45 Answer 4 (Score 1)
03:32 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