Python all () / any () как метод для части / части списка? - PullRequest
4 голосов
/ 09 июля 2019

Что было бы наиболее элегантным / питоническим способом достижения: «если 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 ]

7 голосов
/ 09 июля 2019

Похоже, вы имеете дело с длинными списками, поэтому это дорого.Если было бы хорошо, если бы вы могли выйти рано, как только будет выполнено условие.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.

2 голосов
/ 10 июля 2019

Вы можете использовать filter для этого. Безусловно, это самый быстрый метод. Обратитесь к моему другому ответу, так как это быстрее, чем методы в этом.

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

Проверенное время результата для этого запускается вместе с методами в моем другом вопросе:

1.684135717988247
2 голосов
/ 09 июля 2019

Что по этому поводу:

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

В нем говорится: check равно True, если более threshold% (по умолчанию 0,80) элементов в listItems больше val.

1 голос
/ 09 июля 2019

Проверьте каждый товар по порядку.

  • Если вы достигнете точки, в которой вы удовлетворены, тогда верните Истину рано.

  • Если вы достигнете точки, в которой вы никогда не будете удовлетворены, даже если каждый будущий предмет пройдет тест, тогда верните False раньше.

  • В противном случае продолжайте (если последующие элементы помогут вам удовлетворить требование).

Это та же идея, что и у ФатихАкичи в комментариях выше, но с дальнейшей оптимизацией.

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
0 голосов
/ 09 июля 2019

Я не хочу брать на себя ответственность за ответ Марка Мейера, поскольку он придумал концепцию использования накопления, а также того, что они более питонны / читабельны, но если вы ищете «самый быстрый» подход, тогдаизменение его подхода с использованием 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
...