Cách Pythonic để tìm giá trị tối đa và chỉ số của nó trong một danh sách?


151

Nếu tôi muốn giá trị tối đa trong danh sách, tôi chỉ có thể viết max(List), nhưng nếu tôi cũng cần chỉ số của giá trị tối đa thì sao?

Tôi có thể viết một cái gì đó như thế này:

maximum=0
for i,value in enumerate(List):
    if value>maximum:
        maximum=value
        index=i

Nhưng nó có vẻ tẻ nhạt với tôi.

Và nếu tôi viết:

List.index(max(List))

Sau đó, nó sẽ lặp lại danh sách hai lần.

Có cách nào tốt hơn?


Bạn có ý nghĩa gì bởi "nó sẽ vượt qua danh sách hai lần"? List.index (max (List)) hoạt động với tôi.
mwcz

14
@mwc: Nó sẽ lặp lại danh sách một lần để xác định giá trị tối đa, sau đó lặp lại lần thứ hai để tìm chỉ mục của giá trị đó.

10
Sẽ không list.index () có vấn đề nếu có các giá trị tối đa trùng lặp?
Logan Yang

@LoganYang vâng, có thể có hai mặt hàng có cùng giá trị.
Florian

Nếu thứ tự không quan trọng, bạn có thể làm một cái gì đó như List.sort () [- 1]
Florian

Câu trả lời:


186

Có nhiều lựa chọn, ví dụ:

import operator
index, value = max(enumerate(my_list), key=operator.itemgetter(1))

2
Ah, tôi đã thấy điều này ở những nơi khác, nhưng tôi nghĩ rằng nó sẽ trả về chỉ một giá trị, không phải là một tuple.
Sunny88

1
@ Sunny88: keyHàm chỉ được sử dụng để quyết định phần tử nào là tối đa. Các yếu tố không được thay đổi.
Sven Marnach

6
@SvenMarnach Tại sao không key=lambda e: e[1]thay thế và do đó tránh nhập khẩu?
cuộc sống

8
@lifebalance Sử dụng itemgetter()nhanh hơn và tránh nhập khẩu không phải là mục tiêu đáng để theo đuổi. Tránh các phụ thuộc bên ngoài có thể có giá trị trong một số trường hợp, nhưng việc nhập từ thư viện chuẩn là không thành vấn đề.
Sven Marnach

324

Tôi nghĩ rằng câu trả lời được chấp nhận là tuyệt vời, nhưng tại sao bạn không làm điều đó một cách rõ ràng? Tôi cảm thấy nhiều người sẽ hiểu mã của bạn hơn và điều đó phù hợp với PEP 8:

max_value = max(my_list)
max_index = my_list.index(max_value)

Phương pháp này cũng nhanh hơn khoảng ba lần so với câu trả lời được chấp nhận:

import random
from datetime import datetime
import operator

def explicit(l):
    max_val = max(l)
    max_idx = l.index(max_val)
    return max_idx, max_val

def implicit(l):
    max_idx, max_val = max(enumerate(l), key=operator.itemgetter(1))
    return max_idx, max_val

if __name__ == "__main__":
    from timeit import Timer
    t = Timer("explicit(l)", "from __main__ import explicit, implicit; "
          "import random; import operator;"
          "l = [random.random() for _ in xrange(100)]")
    print "Explicit: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)

    t = Timer("implicit(l)", "from __main__ import explicit, implicit; "
          "import random; import operator;"
          "l = [random.random() for _ in xrange(100)]")
    print "Implicit: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)

Kết quả khi họ chạy trong máy tính của tôi:

Explicit: 8.07 usec/pass
Implicit: 22.86 usec/pass

Bộ khác:

Explicit: 6.80 usec/pass
Implicit: 19.01 usec/pass

3
Không hy vọng nó sẽ nhanh hơn. Nó nhanh hơn ngay cả khi tôi thay thế l bằng "l = [Random.random () cho _ in xrange (10000000)] + [2]", đảm bảo rằng phần tử cuối cùng là lớn nhất.
Sunny88

14
@ Sunny88: Đối với một danh sách số đơn giản, cách tiếp cận đơn giản nhanh hơn. Nếu bạn sau khi thực hiện trong trường hợp này, tôi khuyên bạn nên sử dụng numpy.argmax(), nhanh hơn 30 lần trên máy của tôi. Nếu danh sách chứa các đối tượng phức tạp hơn các số đơn thuần, cách tiếp cận trong câu trả lời của tôi có thể trở nên nhanh hơn. Ưu điểm của phương pháp đó là nó có thể được sử dụng cho các trình vòng lặp tùy ý, không chỉ cho các danh sách.
Sven Marnach

@ Sven-Marnach Sẽ nhanh hơn, nếu tôi phải chuyển đổi danh sách của mình thành một mảng numpy trước? Nó sẽ nhanh hơn cho ví dụ đơn giản [0,1,0]?
tommy.carstensen

1
@ Sven-Marnach Tôi vừa kiểm tra. numpy.argmax là phương pháp chậm nhất và nó đưa ra câu trả lời sai, nếu mảng chứa chuỗi thay vì float hoặc số nguyên.
tommy.carstensen

9
Sẽ không list.index () có vấn đề nếu có các giá trị tối đa trùng lặp?
Logan Yang

20

Câu trả lời này nhanh hơn 33 lần so với @Escualo khi cho rằng danh sách này rất lớn và giả sử rằng đó đã là một np.array (). Tôi đã phải giảm số lần chạy thử vì thử nghiệm đang xem xét 10000000 phần tử không chỉ 100.

import random
from datetime import datetime
import operator
import numpy as np

def explicit(l):
    max_val = max(l)
    max_idx = l.index(max_val)
    return max_idx, max_val

def implicit(l):
    max_idx, max_val = max(enumerate(l), key=operator.itemgetter(1))
    return max_idx, max_val

def npmax(l):
    max_idx = np.argmax(l)
    max_val = l[max_idx]
    return (max_idx, max_val)

if __name__ == "__main__":
    from timeit import Timer

t = Timer("npmax(l)", "from __main__ import explicit, implicit, npmax; "
      "import random; import operator; import numpy as np;"
      "l = np.array([random.random() for _ in xrange(10000000)])")
print "Npmax: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

t = Timer("explicit(l)", "from __main__ import explicit, implicit; "
      "import random; import operator;"
      "l = [random.random() for _ in xrange(10000000)]")
print "Explicit: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

t = Timer("implicit(l)", "from __main__ import explicit, implicit; "
      "import random; import operator;"
      "l = [random.random() for _ in xrange(10000000)]")
print "Implicit: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

Kết quả trên máy tính của tôi:

Npmax: 8.78 msec/pass
Explicit: 290.01 msec/pass
Implicit: 790.27 msec/pass

Chỉ cần làm rõ: việc tăng tốc chỉ là do việc thực hiện C numpy so với python thuần? Hoặc có cách nào cải thiện câu trả lời của @ Escualo bằng cách sử dụng trăn thuần túy?
tối đa

Nếu muốn sử dụng python 3.6, người ta có thể làm một cái gì đó như: "l = np.array ([Random.random () cho _ trong phạm vi (10000000)])") print (f "Npmax: {(1000 * t. timeit (số = 10) / 10): 5.2f} msec / pass ")
Piotr Siejda

Đây là vào ngày 2.7
portforwardpodcast

1
Chà, tốc độ numpy.argmaxtrông tuyệt vời cho đến khi bạn cho phép nó xử lý một danh sách trăn tiêu chuẩn . Sau đó, tốc độ nằm giữa phiên bản rõ ràng và ẩn. Tôi đoán np.arraykhông chỉ tạo ra một danh sách mà nó còn lưu một số thông tin bổ sung trong đó - ví dụ như các giá trị tối thiểu và tối đa (chỉ là một giả thuyết).
Miroslaw Opoka

18

Với thư viện tích hợp của Python, thật dễ dàng:

a = [2, 9, -10, 5, 18, 9] 
max(xrange(len(a)), key = lambda x: a[x])

Điều này cho maxthấy số lượng lớn nhất trong danh sách [0, 1, 2, ..., len(a)], sử dụng chức năng tùy chỉnh lambda x: a[x], mà nói rằng 0thực sự 2, 1thực sự 9, vv


Trong Python 3, không có xrange, nếu bạn muốn viết mã sẽ chạy cho cả Python 2 và Python 3, bạn nên sử dụng phạm vi ().
Chunde Huang

10
max([(v,i) for i,v in enumerate(my_list)])

Điều này là tốt hơn bởi vì bạn có thể điều chỉnh nó để sử dụng với một cái gì đó khác hơn là tuple.
wieczorek1990

Làm thế nào chính xác để làm việc này? Bạn có thể phá vỡ quá trình?
clabe45

Xin chào @ clabe45, nó chuyển đổi my_list trong danh sách các tuples (v, i) trong đó v là mọi mục trong danh sách của tôi và tôi là chỉ số tương ứng, sau đó nó nhận được tuple với giá trị maximun (và với chỉ mục được liên kết của nó)
Luis Sobrecueva

4
Cảm ơn, bạn có thể gửi nó trong câu trả lời có thể? Và làm thế nào để maxbiết chỉ cần đưa mục đầu tiên của mỗi tuple ( v) vào khi tính giá trị tối đa?
clabe45

1
@ clabe45 Có thể câu trả lời này đến quá muộn, nhưng đối với những người khác (như tôi), người đã bắt gặp chủ đề này bây giờ, ở đây: stackoverflow.com/questions/18296755/ Lỗi là một lời giải thích. Không phải dòng này: "Theo mặc định, max sẽ so sánh các mục theo chỉ mục đầu tiên, nếu chỉ mục đầu tiên giống nhau thì nó sẽ so sánh chỉ mục thứ hai." Vì vậy, tôi đã thử nó với danh sách: l = [1,1,1] và sau đó max ([(v, i) cho i, v trong liệt kê (l)]) và nó cho tôi không phải là 1 đầu tiên mà là cuối cùng một: (1,2) là kết quả. Tôi hy vọng nó giải thích :)
Anupam Jain

10

Tôi muốn đề xuất một cách rất đơn giản:

import numpy as np
l = [10, 22, 8, 8, 11]
print(np.argmax(l))
print(np.argmin(l))

Hy vọng nó giúp.


4
max([(value,index) for index,value in enumerate(your_list)]) #if maximum value is present more than once in your list then this will return index of the last occurrence

Nếu giá trị tối đa hiện tại nhiều hơn một lần và bạn muốn nhận được tất cả các chỉ số,

max_value = max(your_list)
maxIndexList = [index for index,value in enumerate(your_list) if value==max(your_list)]

1
Vâng Tôi gần như đã đăng một câu trả lời, nhưng sau đó tôi thấy bạn đã có cùng một giải pháp với cùng một logic trong danh sách hiểu bài của bạn.
WalyKu

2

Có lẽ bạn cần một danh sách được sắp xếp nào?

Thử cái này:

your_list = [13, 352, 2553, 0.5, 89, 0.4]
sorted_list = sorted(your_list)
index_of_higher_value = your_list.index(sorted_list[-1])

1. Sắp xếp có độ phức tạp thời gian cao hơn. 2. sorted_listkhông có chỉ mục nhưng giá trị, vì vậy nó sẽ không hoạt động.

1

xin lỗi vì đã phục hồi chủ đề này, nhưng nghĩ rằng phương pháp của tôi đáng để thêm vào.

Tên danh sách trong ví dụ này 'danh sách'

list.sort()
print(list[-1])

Điều đó sẽ in giá trị cao nhất trong danh sách dễ dàng như!

list.sort()sắp xếp danh sách theo giá trị của mục trong bảng ASCII , do đó sắp xếp hiệu quả danh sách từ thấp nhất đến cao nhất. Sau đó tôi chỉ in giá trị cuối cùng trong danh sách (sẽ là số lớn nhất) bằng cách sử dụng print(list[-1]).

Hi vọng điêu nay co ich!


5
chúng ta không thể lấy chỉ mục theo cách này
toing_toing

-1

Đây là một giải pháp hoàn chỉnh cho câu hỏi của bạn bằng các hàm dựng sẵn của Python:

# Create the List
numbers = input("Enter the elements of the list. Separate each value with a comma. Do not put a comma at the end.\n").split(",") 

# Convert the elements in the list (treated as strings) to integers
numberL = [int(element) for element in numbers] 

# Loop through the list with a for-loop

for elements in numberL:
    maxEle = max(numberL)
    indexMax = numberL.index(maxEle)

print(maxEle)
print(indexMax)
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.