Первый индекс списка 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.