Chỉ mục danh sách Python đầu tiên lớn hơn x?


81

Cách dễ hiểu nhất để tìm chỉ mục đầu tiên trong danh sách lớn hơn x là gì?

Ví dụ, với

list = [0.5, 0.3, 0.9, 0.8]

Chức năng

f(list, 0.7)

sẽ trở lại

2.

57
không sử dụng 'danh sách' làm tên biến ...
mshsayem

11
Ý bạn là "pythonic". Theo urbandictionary.com/define.php?term=Pythonesque , "pythonesque" có nghĩa là "siêu thực, vô lý", và tôi không nghĩ rằng đó là những gì bạn đang tìm kiếm: P
Roberto Bonvallet

1
Câu hỏi là mơ hồ. Câu trả lời là 2bởi vì 0.9 > 0.7hay bởi vì 0.8 > 0.7? Nói cách khác, bạn đang tìm kiếm tuần tự hay theo thứ tự giá trị tăng dần?
Sergey Orshanskiy


Tôi đã bỏ phiếu để đóng câu hỏi này dưới dạng trùng lặp thay vì làm ngược lại vì câu hỏi mới hơn chung chung hơn.
Cristian Ciupitu

Câu trả lời:


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

29
+1: Mặc dù tôi muốn tránh những con số kỳ diệu: tiếp theo (idx cho idx, giá trị trong liệt kê (L) nếu giá trị> 0,7)
truppo

38
+1 để đơn giản và next(), nhưng có thể điều này để dễ đọc:next(i for i,v in enumerate(L) if v > 0.7)
Will Hardy

14
Mặc dù điều này trông đẹp mắt, nhưng trường hợp không có kết quả sẽ gây ra một StopIteration khó hiểu.
Virgil Dupras

3
@Wim: Nhưng sau đó bạn quay lại đánh giá toàn bộ chuỗi. Sử dụng itertools.chain()thay vì thêm danh sách như thế này.
Ignacio Vazquez-Abrams

3
@Wim bạn không cần chain () ở đây. next () chấp nhận đối số thứ hai:next((i for i, x in enumerate(L) if x > value), -1)
jfs 11/01

35

nếu danh sách được sắp xếp thì bisect.bisect_left(alist, value)nhanh hơn cho một danh sách lớn hơn next(i for i, x in enumerate(alist) if x >= value).


Câu trả lời hay - Nó chắc chắn nhanh hơn 5 đến 6 lần đối với tôi khi sử dụng một danh sách nhỏ được sắp xếp gồm 4 phần tử nhưng, (không chắc liệu tôi có mắc lỗi không), nhưng khi tôi sử dụng timeit với một danh sách dài gồm 10000 phần tử mảng numpy Tuy nhiên, tôi thấy nó chậm gấp đôi so với câu trả lời đọc hiểu danh sách ở trên, điều mà tôi đã rất ngạc nhiên.
Adrian Tompkins

1
@AdrianTompkins: có gì đó không ổn với điểm chuẩn của bạn. bisect_leftlà O (log n), trong khi listcomp là O (n) tức là, càng lớn nthì càng có nhiều lợi thế bisect_left(). Tôi đã cố gắng tìm chỉ mục 500_000trong range(10**6)việc sử dụng bisect_left()-> 3,75 micro giây và sử dụng genxpr với next()-> 51,0 mili giây [ 10_000lần] chậm hơn như mong đợi.
jfs

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

4
-1: Trong khi đúng kỹ thuật, bộ lọc sử dụng dont nơi một danh sách hiểu là cả hai dễ đọc hơn và hơn performant
truppo

lọc (lambda x: x [1]> 0,7, enumerate (seq)) [0] [0] - tìm kiếm tuyến tính đơn giản
lowtech

4
Bộ lọc @truppo trong python 3 trả về một trình tạo, vì vậy nó sẽ không tệ hơn khả năng hiểu danh sách? Ngoài ra, tôi thấy cách này dễ đọc hơn so với giải pháp liệt kê.
BubuIIC

Một điều không hay ở cái này là bạn sẽ phải xử lý ngoại lệ nếu không có mục nào trong seq lớn hơn .7.
Brian C.

Giải pháp này không chính xác về mặt kỹ thuật. Tác giả câu hỏi hỏi làm thế nào để tìm chỉ mục của phần tử trong danh sách. Nhưng giải pháp này trả về một bản ghi thay thế. Eventmore trong Python 3.8 thì chậm hơn bisect_left()(nhanh nhất) và enumerate().
Sergey Nevmerzhitsky

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

2
nó sẽ thất bại nếu 'x' là lớn hơn bất kỳ giá trị khác trong danh sách
mshsayem

2
@mshsayem: Vấn đề là không xác định cho trường hợp này. Thất bại có thể là điều phải làm.
S.Lott

@ S.Loot: Điểm tốt. Không nếu không có trong kết quả danh sách trong một lỗi dễ hiểu khi giao này cho một biến: IndexError: list index out of range. Sử dụng index = next[ n for n,i in enumerate(alist) if i>0.7 ]lỗi cho: NameError: name 'index' is not defined. nextnhanh hơn một chút: Chênh lệch thời gian là 12,7 ns so với 11,9 ns cho 60 000 số.
Leo

10
for index, elem in enumerate(elements):
    if elem > reference:
        return index
raise ValueError("Nothing Found")


3

1) NUMPY ARGWHERE, danh sách chung

Nếu bạn hài lòng khi sử dụng numpy, thì những điều sau sẽ hoạt động trên các danh sách chung (được sắp xếp hoặc không được sắp xếp):

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

hoặc nếu bạn cần câu trả lời dưới dạng danh sách:

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

hoặc nếu bạn cần câu trả lời dưới dạng chỉ số số nguyên:

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

2) NUMPY SEARCHSORTED, danh sách được sắp xếp (rất hiệu quả để tìm kiếm danh sách)

Tuy nhiên, nếu danh sách tìm kiếm của bạn được sắp xếp, thì việc sử dụng hàm np.searchsorted sẽ gọn gàng và đẹp hơn nhiều :

numpy.searchsorted(searchlist,x)

Điều thú vị khi sử dụng hàm này là cũng như tìm kiếm một giá trị duy nhất x, x cũng có thể là một danh sách, tức là bạn cũng có thể trả về một danh sách các chỉ số cho danh sách các giá trị được tìm kiếm [x1, x2, x3 .. xn ], ( và nó rất hiệu quả so với khả năng hiểu danh sách trong trường hợp này ).


2

Tôi đã gặp vấn đề tương tự khi danh sách của tôi rất dài. sự hiểu biết hoặc các giải pháp dựa trên bộ lọc sẽ đi qua toàn bộ danh sách. itertools.takeworthy sẽ phá vỡ vòng lặp khi điều kiện trở thành sai lần đầu tiên:

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)

tại sao bạn chỉ không viết def f (l, b): return len (list (take gone (lambda x: x [1] <= b, enumerate (l))))?
Avo Asatryan

2

Tôi biết đã có rất nhiều câu trả lời, nhưng đôi khi tôi cảm thấy rằng từ pythonic được dịch thành 'một lớp lót'.

Khi tôi nghĩ một định nghĩa tốt hơn gần với câu trả lời này hơn :

"Khai thác các tính năng của ngôn ngữ Python để tạo ra mã rõ ràng, ngắn gọn và dễ bảo trì."

Mặc dù một số câu trả lời ở trên là ngắn gọn nhưng tôi không thấy chúng rõ ràng và một lập trình viên mới sẽ mất một thời gian để hiểu, do đó không khiến chúng trở nên cực kỳ dễ bảo trì đối với một nhóm được xây dựng với nhiều cấp độ kỹ năng.

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)

hoặc là

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)

Tôi nghĩ rằng những điều trên có thể dễ dàng hiểu bởi một người mới và vẫn đủ ngắn gọn để được chấp nhận bởi bất kỳ lập trình viên python kỳ cựu nào.

Tôi nghĩ rằng viết mã ngu ngốc là một tích cực.


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

Trông khá bóng bẩy. Nhưng về mặt lý thuyết, nó sẽ duyệt qua toàn bộ danh sách và sau đó trả về kết quả đầu tiên (lớn hơn x), phải không? Có cách nào để làm cho một điểm dừng ngay sau khi tìm thấy kết quả đầu tiên?
c00kiemonster 10/02/10

có gì sai khi duyệt qua toàn bộ danh sách? nếu giá trị đầu tiên lớn hơn 0,7 ở gần cuối danh sách, nó không tạo ra sự khác biệt.
ghostdog 74

3
Thật. Nhưng trong trường hợp đặc biệt này trong danh sách tôi có ý định sử dụng các chức năng trên là khá dài, vì vậy tôi muốn nó bỏ đi qua càng sớm càng hợp được tìm thấy ...
c00kiemonster

cho dù dài hay không, nếu giá trị đầu tiên là mục thứ hai cuối cùng của danh sách, bạn vẫn sẽ phải duyệt qua toàn bộ danh sách để đến được đó!
ghostdog 74

4
@ ghostdog74: Có, nhưng đây không phải là lý do để muốn tất cả các trường hợp đều là trường hợp xấu nhất.
UncleBens

0

Bạn cũng có thể làm điều này bằng cách sử dụng numpy:

import numpy as np

list(np.array(SearchList) > x).index(True)

-1

Hãy thử cái này:

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

mã ví dụ:

Renumerate(range(10))

đầu ra:

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

1
Câu hỏi là " tìm chỉ mục đầu tiên trong danh sách lớn hơn x ".
Gino Mempin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.