Первый индекс списка Python больше x?


81

Каким будет самый питонический способ найти первый индекс в списке, который больше x?

Например, с

list = [0.5, 0.3, 0.9, 0.8]

Функция

f(list, 0.7)

вернется

2.

57
не используйте 'list' в качестве имени переменной ...
— mshsayem

11
Вы имеете в виду «питонический». Согласно urbandictionary.com/define.php?term=Pythonesque , «pythonesque» означает «сюрреалистичный, абсурдный», и я не думаю, что это то, что вы ищете: P
— Роберто Бонвале

1
Вопрос неоднозначный. Ответ: 2потому что 0.9 > 0.7или потому что 0.8 > 0.7? Другими словами, вы ищите последовательно или в порядке возрастания значений?
— Сергей Оршанский


Я проголосовал за то, чтобы закрыть этот вопрос как дубликат, а не наоборот, потому что новый вопрос более общий.
— Cristian Ciupitu

Ответы:


118
next(x[0] for x in enumerate(L) if x[1] > 0.7)

29
+1: Хотя я бы предпочел избегать магических чисел: next (idx для idx, значение в enumerate (L), если значение> 0,7)
— truppo

38
+1 для простоты и next(), может быть, это для удобочитаемости:next(i for i,v in enumerate(L) if v > 0.7)
— Уилл Харди

14
Несмотря на то, что это выглядит красиво, случай, когда нет результата, вызовет сбивающую с толку StopIteration.
— Вирджил Дюпрас,

3
@Wim: Но потом вы снова возвращаетесь к оценке всей последовательности. Используйте itertools.chain()вместо добавления таких списков.
— Игнасио Васкес-Абрамс,

3
@Wim, цепочка () здесь не нужна. next () принимает второй аргумент:next((i for i, x in enumerate(L) if x > value), -1)
— jfs

35

если список отсортирован, то bisect.bisect_left(alist, value)для большого списка быстрее, чем next(i for i, x in enumerate(alist) if x >= value).


Хороший ответ - это было определенно в 5-6 раз быстрее для меня, используя небольшой отсортированный список из 4 элементов, но (не уверен, делаю ли я ошибку), но когда я использую timeit с длинным списком из 10000 элементов numpy array Я считаю, что это примерно в два раза медленнее, чем приведенный выше ответ на понимание списка, чему я был удивлен.
— Адриан Томпкинс,

1
@AdrianTompkins: что-то не так с вашим тестом. bisect_leftравно O (log n), а listcomp - O (n), т. е. чем больше n, тем больше преимуществ на bisect_left()стороне. Я пытался найти индекс 500_000в range(10**6)использовании bisect_left()-> 3,75 микросекунды и используя genexpr с next()-> 51,0 миллисекунды [ 10_000раз] медленнее , как ожидалось.
— jfs

16
filter(lambda x: x>.7, seq)[0]

4
-1: Хотя технически правильно, не используйте фильтр, где понимание списка более читабельно и более производительно
— truppo

filter (lambda x: x [1]> .7, enumerate (seq)) [0] [0] - простой линейный поиск
— lowtech

4
Фильтр @truppo в python 3 возвращает генератор, поэтому он должен быть не хуже, чем понимание списка? Также я считаю этот способ более читаемым, чем решение enumerate.
— BubuIIC

Одна вещь, которая не нравится в этом, заключается в том, что вы попадаете в обработку исключений, если в seq нет элемента больше 0,7.
— Брайан С.

Решение технически неверное. Автор вопроса задал вопрос, как найти индекс элемента в списке. Но это решение вместо этого возвращает запись. Eventmore в Python 3.8 он медленнее bisect_left()(самый быстрый) и enumerate().
— Сергей Невмержицкий

16
>>> alist= [0.5, 0.3, 0.9, 0.8]
>>> [ n for n,i in enumerate(alist) if i>0.7 ][0]
2

2
он потерпит неудачу, если 'x' больше любого другого значения в списке
— mshsayem

2
@mshsayem: В данном случае проблема не определена. Неудача может быть правильным поступком.
— S.Lott

@ S.Loot: Хорошее замечание. В противном случае , если не в результатах списка в понятной ошибки при назначении этой переменной: IndexError: list index out of range. Использование index = next[ n for n,i in enumerate(alist) if i>0.7 ]ошибки дает: NameError: name 'index' is not defined. nextнемного быстрее: разница во времени составляет 12,7 нс против 11,9 нс для 60 000 номеров.
— Лев



3

1) NUMPY ARGWHERE, общие списки

Если вам нравится использовать numpy, то в общих списках (отсортированных или несортированных) будет работать следующее:

numpy.argwhere(np.array(searchlist)>x)[0]

или если вам нужен ответ в виде списка:

numpy.argwhere(np.array(searchlist)>x).tolist()[0]

или если вам нужен ответ в виде целочисленного индекса:

numpy.argwhere(np.array(searchlist)>x).tolist()[0][0]

2) СОРТИРОВАТЬСЯ по ЧИСЛАМ, отсортированные списки (очень эффективны для поиска в списках)

Однако, если ваш список поиска отсортирован, гораздо чище и приятнее использовать функцию np.searchsorted :

numpy.searchsorted(searchlist,x)

Приятная вещь в использовании этой функции заключается в том, что помимо поиска единственного значения x, x также может быть списком, т.е. вы также можете вернуть список индексов для списка найденных значений [x1, x2, x3 .. xn ] ( и в данном случае это очень эффективно по сравнению с пониманием списка ).


2

У меня была аналогичная проблема, когда мой список был очень длинным. решения на основе понимания или фильтрации пройдут через весь список. itertools.takewhile прервет цикл, как только условие станет ложным в первый раз:

from itertools import takewhile

def f(l, b): return len([x for x in takewhile(lambda x: x[1] <= b, enumerate(l))])

l = [0.5, 0.3, 0.9, 0.8]
f(l, 0.7)

1
почему вы просто не пишете def f (l, b): return len (list (takewhile (lambda x: x [1] <= b, enumerate (l))))?
— Аво Асатрян

2

Я знаю, что ответов уже много, но иногда мне кажется, что слово pythonic переводится как «однострочный».

Когда я думаю, что лучшее определение ближе к этому ответу :

«Использование возможностей языка Python для создания ясного, лаконичного и поддерживаемого кода».

Хотя некоторые из приведенных выше ответов являются краткими, я не считаю их ясными, и программисту-новичку потребуется время, чтобы понять, что не делает их чрезвычайно удобными в обслуживании для команды, состоящей из многих уровней навыков.

l = [0.5, 0.3, 0.9, 0.8]

def f(l, x):
    for i in l:
        if i >x: break
    return l.index(i)


f(l,.7)

или же

l = [0.5, 0.3, 0.9, 0.8]

def f(l, x):
    for i in l:
        if i >x: return l.index(i)



f(l,.7)

Я думаю, что вышеизложенное легко понять новичку и все еще достаточно кратко, чтобы быть принятым любым опытным программистом на Python.

Я считаю, что написание глупого кода - это хорошо.


1
>>> f=lambda seq, m: [ii for ii in xrange(0, len(seq)) if seq[ii] > m][0]
>>> f([.5, .3, .9, .8], 0.7)
2

Это выглядит довольно гладко. Но теоретически он пройдет по всему списку, а затем вернет первый результат (больше x), верно? Есть ли способ сделать тот, который остановится сразу после получения первого результата?
— c00kiemonster

что не так с обходом всего списка? если первое значение больше 0,7 находится ближе к концу списка, это не имеет значения.
— ghostdog74

3
Правда. Но в этом конкретном случае списки, в которых я собираюсь использовать эту функцию, довольно длинные, поэтому я бы предпочел, чтобы он прекратил обход, как только совпадение будет найдено ...
— c00kiemonster

независимо от того, длинный он или нет, если первое значение является последним вторым элементом списка, вам все равно придется пройти весь список, чтобы добраться туда!
— ghostdog74

4
@ ghostdog74: Да, но это не причина, чтобы все случаи были наихудшими.
— UncleBens


-1

Попробуй это:

def Renumerate(l):
    return [(len(l) - x, y) for x,y in enumerate(l)]

пример кода:

Renumerate(range(10))

вывод:

(10, 0)
(9, 1)
(8, 2)
(7, 3)
(6, 4)
(5, 5)
(4, 6)
(3, 7)
(2, 8)
(1, 9)

1
Вопрос заключался в том, чтобы « найти первый индекс в списке, который больше x ».
— Gino Mempin
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.