Індекс першого списку 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? Іншими словами, ви шукаєте послідовно або в порядку збільшення значень?
Сергій Оршанський


Я проголосував, щоб закрити це питання як дублікат, а не робити навпаки, оскільки нове питання є більш загальним.
Крістіан Чьюпіту

Відповіді:


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

29
+1: Хоча я волів би уникати магічних чисел: наступний (idx для idx, значення в переліченні (L), якщо значення> 0,7)
truppo

38
+1 для простоти та next(), але, можливо, це для читабельності:next(i for i,v in enumerate(L) if v > 0.7)
Will Hardy

14
Хоча це приємно виглядає, випадок, коли результату немає, призведе до заплутаного StopIteration.
Вергілій Дупрас

3
@Wim: Але потім ви повертаєтесь до оцінки всієї послідовності. Використовуйте itertools.chain()замість додавання таких списків.
Ігнасіо Васкес-Абрамс

3
@Wim, тут вам не потрібен chain (). 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 Я вважаю, що це приблизно вдвічі повільніше, ніж відповідь на розуміння списку вище, чим я був здивований.
Адріан Томпкінс

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

фільтр (лямбда x: x [1]> .7, перерахувати (seq)) [0] [0] - простий лінійний пошук
lowtech

4
Фільтр @truppo у python 3 повертає генератор, тож він повинен бути не гіршим за розуміння списку? Крім того, я вважаю цей спосіб більш читабельним, ніж перелічене рішення.
BubuIIC

Одне, що не приємно в цьому, - це те, що ви потрапляєте на обробку винятків, якщо в наступному елементі немає елементів, більших за .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, то в загальних списках (відсортованих чи невідсортованих) буде працювати наступне:

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) NUMPY SEARCHSORTED, відсортовані списки (дуже ефективно для пошуку списків)

Однак, якщо ваш список пошуку відсортований, набагато чистіше і приємніше використовувати функцію np.searchsorted :

numpy.searchsorted(searchlist,x)

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


2

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

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 (take While (lambda x: x [1] <= b, enumerate (l))))?
Avo

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

Це виглядає досить гладко. Але теоретично він пройде весь список, а потім поверне перший результат (більший за х), так? Чи є спосіб зробити такий, який зупиняється відразу після знаходження першого результату?
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
Питання було " знайти перший індекс у списку, який перевищує х ".
Джино Мемпін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.