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

81

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

Например, с

list = [0.5, 0.3, 0.9, 0.8]

Функция

f(list, 0.7)

вернется

2.
c00kiemonster
источник
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).

jfs
источник
Хороший ответ - это было определенно в 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
призрачная собака74
источник
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 номеров.
Лев
11
for index, elem in enumerate(elements):
    if elem > reference:
        return index
raise ValueError("Nothing Found")
Вирджил Дюпрас
источник
4

Еще один:

map(lambda x: x>.7, seq).index(True)
облет
источник
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)
lowtech
источник
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
0

Вы также можете сделать это, используя numpy:

import numpy as np

list(np.array(SearchList) > x).index(True)
Прабхакаран Тиругнанам
источник
-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