¿Forma pitónica para encontrar el valor máximo y su índice en una lista?

151

Si quiero el valor máximo en una lista, puedo escribir max(List), pero ¿y si también necesito el índice del valor máximo?

Puedo escribir algo como esto:

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

Pero me parece tedioso.

Y si escribo:

List.index(max(List))

Luego iterará la lista dos veces.

¿Hay una mejor manera?

Sunny88
fuente
¿Qué quiere decir con "pasará la lista dos veces"? List.index (max (List)) funciona para mí.
mwcz
14
@mwc: iterará la lista una vez para determinar el valor máximo, luego la iterará por segunda vez para encontrar el índice de ese valor.
10
¿List.index () no sería problemático si hay valores máximos duplicados?
Logan Yang
@LoganYang sí, podría haber dos elementos con el mismo valor.
Florian
Si el pedido no es importante, puede hacer algo como List.sort () [- 1]
Florian

Respuestas:

186

Hay muchas opciones, por ejemplo:

import operator
index, value = max(enumerate(my_list), key=operator.itemgetter(1))
Sven Marnach
fuente
2
Ah, he visto esto en otros lugares, pero pensé que devolvería solo un valor, no una tupla.
Sunny88
1
@ Sunny88: la keyfunción solo se utiliza para decidir qué elemento es máximo. Los elementos no se cambian.
Sven Marnach
66
@SvenMarnach ¿Por qué no en su key=lambda e: e[1]lugar y así evitar la importación?
Lifebalance
8
@lifebalance El uso itemgetter()es más rápido y evitar una importación no es un objetivo que valga la pena perseguir. Evitar dependencias externas puede valer la pena en algunos casos, pero una importación desde la biblioteca estándar no es un problema.
Sven Marnach
324

Creo que la respuesta aceptada es excelente, pero ¿por qué no lo haces explícitamente? Siento que más personas entenderían su código, y eso está de acuerdo con PEP 8:

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

Este método también es aproximadamente tres veces más rápido que la respuesta aceptada:

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)

Resultados a medida que se ejecutan en mi computadora:

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

Otro conjunto:

Explicit: 6.80 usec/pass
Implicit: 19.01 usec/pass
Escualo
fuente
3
No esperaba que fuera más rápido. Es más rápido incluso cuando reemplazo l con "l = [random.random () para _ en xrange (10000000)] + [2]", lo que garantiza que el último elemento es el más grande.
Sunny88
14
@ Sunny88: para una lista simple de números, el enfoque simple es más rápido. Si buscas rendimiento en este caso, te sugiero que lo uses numpy.argmax(), que es 30 veces más rápido en mi máquina. Si la lista contiene objetos más complicados que simples números, el enfoque en mi respuesta puede ser más rápido. Otra ventaja de este enfoque es que puede usarse para iteradores arbitrarios, no solo para listas.
Sven Marnach
@ Sven-Marnach ¿Numpy sería más rápido si tuviera que convertir mi lista a una matriz numpy primero? ¿Sería más rápido para el ejemplo simple [0,1,0]?
tommy.carstensen
1
@ Sven-Marnach acabo de comprobar. numpy.argmax es, con mucho, el método más lento, y da la respuesta incorrecta, si la matriz contiene cadenas en lugar de flotantes o enteros.
tommy.carstensen
9
¿List.index () no sería problemático si hay valores máximos duplicados?
Logan Yang
20

Esta respuesta es 33 veces más rápida que @Escualo suponiendo que la lista es muy grande y suponiendo que ya es un np.array (). Tuve que rechazar el número de ejecuciones de prueba porque la prueba está analizando 10000000 elementos, no solo 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 )

Resultados en mi computadora:

Npmax: 8.78 msec/pass
Explicit: 290.01 msec/pass
Implicit: 790.27 msec/pass
portforwardpodcast
fuente
Solo para aclarar: ¿la aceleración se debe solo a la implementación de C numpy versus Python puro? ¿O hay una manera de mejorar la respuesta de @ Escualo usando Python puro?
max
Si a uno le gustaría usar Python 3.6, puede hacer algo como: "l = np.array ([random.random () for _ in range (10000000)])") print (f "Npmax: {(1000 * t. timeit (número = 10) / 10): 5.2f} mseg / pass ")
Piotr Siejda
Esto fue el 2.7
portforwardpodcast
1
Bueno, la velocidad de numpy.argmaxluce increíble hasta que dejas que procese una lista estándar de Python. Entonces la velocidad se encuentra entre la versión explícita y la implícita. Supongo np.arrayque no solo crea una lista, sino que guarda información adicional en ella, como por ejemplo valores mínimos y máximos (solo una hipótesis).
Miroslaw Opoka
18

Con la biblioteca incorporada de Python, es bastante fácil:

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

Esto le indica maxque busque el número más grande en la lista [0, 1, 2, ..., len(a)], utilizando la función personalizada lambda x: a[x], que dice que 0es en realidad 2, 1es en realidad 9, etc.

Sunil Kapil
fuente
En Python 3, no hay xrange, si desea escribir código que se ejecutará tanto para Python 2 como para Python 3, debe usar range ().
Chunde Huang el
10
max([(v,i) for i,v in enumerate(my_list)])
Luis Sobrecueva
fuente
Esto es mejor porque puedes adaptarlo para usarlo con algo que no sea tupla.
wieczorek1990
¿Cómo funciona esto exactamente? ¿Puedes romper el proceso?
clabe45
Hola @ clabe45, convierte my_list en una lista de tuplas (v, i) donde v es cada elemento de mi lista e i es el índice correspondiente, luego obtiene la tupla con el valor máximo (y también con su índice asociado)
Luis Sobrecueva
44
Gracias, ¿puedes publicar eso en la respuesta posiblemente? ¿Y cómo maxsabe tener en cuenta el primer elemento de cada tupla ( v) al calcular el valor máximo?
clabe45
1
@ clabe45 Puede ser que esta respuesta llegue demasiado tarde, pero para otros (como yo) que se encontraron con este hilo ahora, aquí: stackoverflow.com/questions/18296755/… es una explicación. No esta línea: "De forma predeterminada, max comparará los elementos por el primer índice, si el primer índice es el mismo, comparará el segundo índice". Así que lo probé con la lista: l = [1,1,1] y luego max ([(v, i) para i, v en enumerate (l)]) y no me da el primer 1 sino el último uno: (1,2) como resultado. Espero que explique :)
Anupam Jain
10

Sugeriría una forma muy simple:

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

Espero eso ayude.

Igor Manzhos
fuente
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

Si el valor máximo está presente más de una vez y desea obtener todos los índices,

max_value = max(your_list)
maxIndexList = [index for index,value in enumerate(your_list) if value==max(your_list)]
Taohidul Islam
fuente
1
Sip. Casi publiqué una respuesta, pero luego vi que ya tenías la misma solución con la misma lógica en tu lista de comprensión.
WalyKu
2

¿Quizás necesita una lista ordenada de todos modos?

Prueba esto:

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])
Mattias
fuente
1. La clasificación tiene una mayor complejidad temporal. 2. sorted_listno tiene índices sino valores, por lo que no funcionaría.
1

perdón por revivir este hilo, pero pensé que valía la pena agregar mi método.

El nombre de la lista en este ejemplo 'lista'

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

Eso imprimirá el valor más alto en la lista fácil como!

list.sort()ordena la lista por el valor del elemento en la tabla ASCII , por lo que efectivamente clasifica la lista de menor a mayor. Luego simplemente imprimo el último valor en la lista (que será el mayor número) usando print(list[-1]).

¡Espero que esto ayude!

Mackey Johnstone
fuente
55
no podemos obtener el índice de esta manera
toing_toing
-1

Aquí hay una solución completa a su pregunta utilizando las funciones integradas de 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)
Samdom For Peace
fuente