Пітонічний спосіб знайти максимальне значення та його індекс у списку?


151

Якщо я хочу максимальне значення у списку, я можу просто написати max(List), але що робити, якщо мені також потрібен індекс максимального значення?

Я можу написати щось подібне:

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

Але мені це виглядає нудно.

І якщо я напишу:

List.index(max(List))

Тоді він повторюватиме список двічі.

Чи є кращий спосіб?


Що ви маєте на увазі під "вона перейде до списку два рази"? List.index (max (Список)) працює для мене.
mwcz

14
@mwc: Він повторить список один раз для визначення максимального значення, а потім повторить його вдруге, щоб знайти індекс цього значення.

10
Чи не list.index () буде проблематичним, якщо є дублюються максимальні значення?
Логан Ян

@LoganYang так, може бути два елементи з однаковим значенням.
Флоріан

Якщо замовлення не важливе, ви можете зробити щось на кшталт List.sort () [- 1]
Флоріан,

Відповіді:


186

Існує багато варіантів, наприклад:

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

2
Ах, я бачив це в інших місцях, але думав, що це поверне лише одне значення, а не кортеж.
Сонячний88

1
@ Sunny88: keyФункція використовується лише для визначення, який елемент є максимальним. Елементи не змінені.
Свен Марнах

6
@SvenMarnach Чому б не key=lambda e: e[1]замість цього і тим самим уникати імпорту?
життєвий баланс

8
@lifebalance Використання itemgetter()швидше, а уникати імпорту - не мета, яку варто досягти. Уникнення зовнішніх залежностей може бути доцільним у деяких випадках, але імпорт із стандартної бібліотеки - це не проблема.
Свен Марнах

324

Я вважаю, що прийнята відповідь є чудовою, але чому б ти не зробиш її прямо? Я думаю, що більше людей зрозуміє ваш код, і це узгоджується з PEP 8:

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

Цей спосіб також приблизно втричі швидший за прийняту відповідь:

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)

Результати, коли вони працюють на моєму комп’ютері:

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

Інший набір:

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

3
Не очікував, що це буде швидше. Це швидше, навіть коли я заміню l на "l = [random.random () для _ in xrange (10000000)] + [2]", що гарантує, що останній елемент є найбільшим.
Sunny88

14
@ Sunny88: для простого списку чисел простий підхід швидший. Якщо ви після цього працюєте, я б запропонував використовувати numpy.argmax(), що на моїй машині ще в 30 разів швидше. Якщо список містить більш складні об'єкти, ніж прості числа, підхід у моїй відповіді може стати швидшим. Ще одна перевага цього підходу полягає в тому, що його можна використовувати для довільних ітераторів, а не лише для списків.
Свен Марнах

@ Sven-Marnach Чи буде numpy швидше, якби мені спочатку довелося перетворити свій список в nummy масив? Чи було б швидше на простий приклад [0,1,0]?
tommy.carstensen

1
@ Sven-Marnach я щойно перевірив. numpy.argmax - це самий повільний метод, і він дає неправильну відповідь, якщо масив містить рядки замість плаваючих чи цілих чисел.
tommy.carstensen

9
Чи не list.index () буде проблематичним, якщо є дублюються максимальні значення?
Логан Ян

20

Ця відповідь в 33 рази швидше, ніж @Escualo, якщо припустити, що список дуже великий, і якщо припустити, що це вже np.array (). Мені довелося зменшити кількість проб, оскільки тест переглядає 10000000 елементів, а не 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 )

Результати на моєму комп’ютері:

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

Просто для уточнення: прискорення якраз пов’язане з нумерованою реалізацією C проти чистого пітона? Або є спосіб покращити відповідь @ Escualo за допомогою чистого пітона?
макс

Якщо ви хочете використовувати python 3.6, можна зробити щось на кшталт: "l = np.array ([random.random () для _ в діапазоні (10000000)])") print (f "Npmax: {(1000 * t. timeit (число = 10) / 10): 5,2f} мсек / пропуск ")
Piotr Siejda

Це було 2.7
portforwardpodcast

1
Ну, швидкість numpy.argmaxвиглядає дивовижною, поки ви не дозволите йому обробляти стандартний список пітонів. Тоді швидкість лежить між явною та неявною версією. Я думаю, np.arrayщо не просто створюється список, але він зберігає додаткову інформацію в ньому - як, наприклад, значення min та max (лише гіпотеза).
Мирослав Опока

18

З вбудованою бібліотекою Python це досить просто:

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

Це говорить , maxщоб знайти найбільше число у списку [0, 1, 2, ..., len(a)], використовуючи призначену для користувача функцію lambda x: a[x], яка говорить , що 0насправді 2, 1насправді 9, і т.д.


У Python 3 немає xrange, якщо ви хочете написати код, який буде працювати як для Python 2, так і для Python 3, слід використовувати range ().
Чонде Хуан

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

Це краще, тому що ви можете адаптувати його до використання з чимось іншим, ніж кортеж.
wieczorek1990

Як саме це працює? Чи можете ви зламати процес?
clabe45

Привіт @ clabe45, він перетворює my_list у список кортежів (v, i), де v - кожен елемент мого списку, а я - відповідний індекс, тоді він отримує кортеж зі значенням maximun (і з відповідним індексом також)
Луїс Собрекева

4
Спасибі, чи можете ви розмістити це у відповіді? І як maxзнати просто враховувати перший пункт кожного кортежу ( v) під час обчислення максимального значення?
clabe45

1
@ clabe45 Можливо, ця відповідь надто пізно, але для інших (як я), які зараз натрапили на цю тему, тут: stackoverflow.com/questions/18296755/… є поясненням. Не цей рядок: "За замовчуванням max порівнюватиме елементи за першим індексом, якщо перший індекс той самий, то він порівняє другий індекс." Тому я спробував це зі списком: l = [1,1,1], а потім max ([(v, i) для i, v в перерахунку (l)]), і він дає мені не перший 1, а останній один: (1,2) як результат. Сподіваюся, це пояснює :)
Анупам Джайн

10

Я б запропонував дуже простий спосіб:

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

Сподіваюся, це допомагає.


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

Якщо максимальне значення в даний час більше, ніж один раз, і ви хочете отримати всі індекси,

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

1
Так. Я майже опублікував відповідь, але потім побачив, що у вас було одне й те саме рішення з тією ж логікою у вашому розумінні списку.
WalyKu

2

Можливо, вам все-таки потрібен відсортований список?

Спробуйте це:

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. Сортування має більш високу часову складність. 2. sorted_listне має індексів, а значень, тому це не працює.

1

вибачте за те, що відродив цю нитку, але подумав, що мій метод варто додати.

Назва списку в цьому прикладі "список"

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

Це дозволить легко надрукувати найвище значення у списку!

list.sort()сортує список за значенням елемента в таблиці ASCII , тому ефективно сортує список від найнижчого до найвищого. Потім я просто друкую останнє значення у списку (яке буде найбільшою кількістю), використовуючи print(list[-1]).

Сподіваюся, це допомагає!


5
ми не можемо отримати індекс таким чином
toing_toing

-1

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