Obtener el índice del elemento max o min devuelto usando max () / min () en una lista

467

Estoy usando Python maxy minfunciones en listas para un algoritmo minimax, y necesito el índice del valor devuelto por max()o min(). En otras palabras, necesito saber qué movimiento produjo el valor máximo (en el turno del primer jugador) o mínimo (segundo jugador).

for i in range(9):
    newBoard = currentBoard.newBoardWithMove([i / 3, i % 3], player)

    if newBoard:
        temp = minMax(newBoard, depth + 1, not isMinLevel)  
        values.append(temp)

if isMinLevel:
    return min(values)
else:
    return max(values)

Necesito poder devolver el índice real del valor mínimo o máximo, no solo el valor.

Kevin Griffin
fuente
32
El builtin divmodexiste para evitar tener que decir [i / 3, i % 3]mucho.
Mike Graham

Respuestas:

417
if isMinLevel:
    valores de retorno índice (min (valores))
más:
    return values.index (max (valores))
demasiado php
fuente
38
@KevinGriffin, tenga en cuenta que esto solo le ofrece una de las posibles ocurrencias del mínimo / máximo. Es posible que esto no sea lo que desea, por ejemplo, si es posible aumentar su ganancia de la misma manera, pero una de ellas perjudica más al otro jugador. No sé si este es un caso que debe considerar.
Mike Graham
89
@Kashyap En realidad es O (N), no O (N ^ 2). En el caso min, se evalúa el primer min (valores), que es O (N), luego se llama values.index (), que también es O (N). O (N) + O (N) = O (N). El argumento para indexar solo se evalúa una vez. Es equivalente a:tmp = min(values); return values.index(tmp)
Tom Karzes
@demasiado php qué hacer cuando hay repetición de elementos.
Shashi Tunga
@ShashiTunga [list] .index () devuelve solo la primera ocurrencia de algo, no se garantiza que sea exclusivo, el valor mínimo podría no ser único dentro de la lista
Scott Anderson
473

Digamos que tiene una lista values = [3,6,1,5]y necesita el índice del elemento más pequeño, es decir, index_min = 2en este caso.

Evite la solución itemgetter()presentada en las otras respuestas, y use en su lugar

index_min = min(range(len(values)), key=values.__getitem__)

porque no requiere import operatorni usar enumerate, y siempre es más rápido (punto de referencia a continuación) que una solución que usa itemgetter().

Si está lidiando con matrices numpy o puede pagar numpycomo una dependencia, considere también usar

import numpy as np
index_min = np.argmin(values)

Esto será más rápido que la primera solución, incluso si lo aplica a una lista pura de Python si:

  • es más grande que unos pocos elementos (aproximadamente 2 ** 4 elementos en mi máquina)
  • puede permitirse la copia de memoria de una lista pura a una numpymatriz

como señala este punto de referencia: ingrese la descripción de la imagen aquí

He ejecutado el punto de referencia en mi máquina con python 2.7 para las dos soluciones anteriores (azul: pitón puro, primera solución) (rojo, solución numpy) y para la solución estándar basada en itemgetter()(negro, solución de referencia). El mismo punto de referencia con Python 3.5 mostró que los métodos comparan exactamente lo mismo del caso de Python 2.7 presentado anteriormente

gg349
fuente
Un muy fuerte +1. Me encanta la evaluación comparativa de las soluciones propuestas y las reglas generales que ha resumido. Como sugerí en otra respuesta a continuación, ¿podría proporcionar (o vincular) su código de prueba para que otros puedan reproducir sus resultados? Las máquinas y las bibliotecas cambian con el tiempo, y permitiría compararlas con otras soluciones.
Rakurai
3
Creo que puede haber un error tipográfico: xrange. ¿No debería ser rango?
Lindsay Fowler
66
@LindsayFowler xrange()ahora está en desuso, puedes usarlorange()
davide
np.argmin no funciona para flotadores. solo la primera sugerencia funciona en ints y flotantes.
Jim
Creo que te equivocas, inténtalo import numpy as np; x = [2.3, -1.4]; np.argmin(x). Verás que también argminfunciona en carrozas
gg349
332

Puede encontrar el índice y el valor mínimo / máximo al mismo tiempo si enumera los elementos de la lista, pero realiza un mínimo / máximo en los valores originales de la lista. Al igual que:

import operator
min_index, min_value = min(enumerate(values), key=operator.itemgetter(1))
max_index, max_value = max(enumerate(values), key=operator.itemgetter(1))

De esta forma, la lista solo se recorrerá una vez durante min (o max).

Matt Anderson
fuente
110
O use una lambda:key=lambda p: p[1]
scry
116

Si desea encontrar el índice de max dentro de una lista de números (que parece ser su caso), le sugiero que use numpy:

import numpy as np
ind = np.argmax(mylist)
dr.haz
fuente
En caso de múltiples ocurrencias de los valores máximos, se devuelven los índices correspondientes a la primera aparición.
Cohensius el
41

Posiblemente una solución más simple sería convertir la matriz de valores en una matriz de valores, pares de índices, y tomar el máximo / mínimo de eso. Esto daría el índice más grande / más pequeño que tiene el máximo / mínimo (es decir, los pares se comparan primero comparando el primer elemento y luego comparando el segundo elemento si los primeros son iguales). Tenga en cuenta que no es necesario crear la matriz, ya que min / max permiten generadores como entrada.

values = [3,4,5]
(m,i) = max((v,i) for i,v in enumerate(values))
print (m,i) #(5, 2)
Antón
fuente
30
list=[1.1412, 4.3453, 5.8709, 0.1314]
list.index(min(list))

Te dará el primer índice de mínimo.

Andy
fuente
18

Creo que lo mejor es convertir la lista a ay numpy arrayusar esta función:

a = np.array(list)
idx = np.argmax(a)
Akshaya Natarajan
fuente
14

También estaba interesado en esto y comparé algunas de las soluciones sugeridas usando perfplot (un proyecto mío mío).

Resulta que el argmin de ese numpy ,

numpy.argmin(x)

es el método más rápido para listas lo suficientemente grandes, incluso con la conversión implícita de la entrada lista a numpy.array.

ingrese la descripción de la imagen aquí


Código para generar la trama:

import numpy
import operator
import perfplot


def min_enumerate(a):
    return min(enumerate(a), key=lambda x: x[1])[0]


def min_enumerate_itemgetter(a):
    min_index, min_value = min(enumerate(a), key=operator.itemgetter(1))
    return min_index


def getitem(a):
    return min(range(len(a)), key=a.__getitem__)


def np_argmin(a):
    return numpy.argmin(a)


perfplot.show(
    setup=lambda n: numpy.random.rand(n).tolist(),
    kernels=[
        min_enumerate,
        min_enumerate_itemgetter,
        getitem,
        np_argmin,
        ],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    )
Nico Schlömer
fuente
Tenga en cuenta que la misma conclusión se publicó anteriormente en mi respuesta, hace más de 2 años, con más información sobre cuándo y por qué se puede usar o no argmin. Considere eliminar la respuesta, que tampoco le da mérito a lo que ya se ha propuesto en esta misma página. Considere también revisar sus otras respuestas sobre SO para un comportamiento similar: parece que no cita la respuesta real que proporciona la mejor solución en sus análisis de rendimiento. Esto es bastante malo, especialmente para alguien con> 10K de representante que ha existido el tiempo suficiente como para saberlo mejor.
gg349
@ gg349, muy buenos puntos, pero proporciona el código fuente para generar los resultados, lo que lo hace fácilmente reproducible y adaptable a la comparación de otras soluciones. Estoy de acuerdo en que podría considerar eliminar esta respuesta como un duplicado, pero ¿tal vez podría agregar valor a su respuesta al incluir o vincular el código que utilizó?
Rakurai
8

Use una matriz numpy y la función argmax ()

 a=np.array([1,2,3])
 b=np.argmax(a)
 print(b) #2
John Misquita
fuente
8

Después de obtener los valores máximos, intente esto:

max_val = max(list)
index_max = list.index(max_val)

Mucho más simple que muchas opciones.

alpha_989
fuente
6

Creo que la respuesta anterior resuelve su problema, pero pensé en compartir un método que le dé el mínimo y todos los índices en los que aparece el mínimo.

minval = min(mylist)
ind = [i for i, v in enumerate(mylist) if v == minval]

Esto pasa la lista dos veces, pero sigue siendo bastante rápido. Sin embargo, es un poco más lento que encontrar el índice del primer encuentro del mínimo. Entonces, si necesita solo uno de los mínimos, use la solución de Matt Anderson , si los necesita a todos, use esto.

Burak Bağdatlı
fuente
1
Me gusta porque usa Python base, y encuentro que la comprensión de la lista es más fácil de entender que itemgetter, lambda, etc. (y lo suficientemente flexible como para resolver una variedad de tareas, como esta ...)
James
crudo. Prefiero esto.
Dev_Man
6

Use la función numpy del módulo numpy.where

import numpy as n
x = n.array((3,3,4,7,4,56,65,1))

Para índice de valor mínimo:

idx = n.where(x==x.min())[0]

Para el índice de valor máximo:

idx = n.where(x==x.max())[0]

De hecho, esta función es mucho más poderosa. Puede plantear todo tipo de operaciones booleanas Para un índice de valor entre 3 y 60:

idx = n.where((x>3)&(x<60))[0]
idx
array([2, 3, 4, 5])
x[idx]
array([ 4,  7,  4, 56])
Ishan Tomar
fuente
el índice en python comienza en 0. el índice devuelto será 6 (para 65), mientras que su código devuelve 7 (la pregunta de OP fue "Obteniendo el índice ...")
tagoma
En el comando, he consultado el índice de valor mínimo (aquí: 1) cuyo índice ES 7. 65 es el valor máximo de elementos en la matriz. Si escribe: n.where (x == x.max ()) [0] obtendrá un índice de máx. valor que es 65 aquí. Su índice saldrá a ser 6
Ishan Tomar
uso de numpy: probablemente prohibido en esta aplicación. Pero si va a usar numpy, es mucho mejor que solo usar en argmin()lugar de lo que hizo aquí.
RBF06
Gracias @ RBF06 Lo comprobaré.
Ishan Tomar
5

Esto es simplemente posible utilizando el incorporado enumerate()y la max()función y el keyargumento opcional de la max()función y una expresión lambda simple:

theList = [1, 5, 10]
maxIndex, maxValue = max(enumerate(theList), key=lambda v: v[1])
# => (2, 10)

En los documentos max(), dice que el keyargumento espera una función como en la list.sort()función. Consulte también la Clasificación de cómo hacerlo .

Funciona igual para min(). Por cierto, devuelve el primer valor máximo / mínimo.

Simon Hänisch
fuente
Respuesta tardía pero mejor (si no necesita velocidad).
mmj
5

Digamos que tiene una lista como:

a = [9,8,7]

Los siguientes dos métodos son formas bastante compactas de obtener una tupla con el elemento mínimo y su índice. Ambos tardan un tiempo similar en procesarse. Mejor me gusta el método zip, pero ese es mi gusto.

método zip

element, index = min(list(zip(a, range(len(a)))))

min(list(zip(a, range(len(a)))))
(7, 2)

timeit min(list(zip(a, range(len(a)))))
1.36 µs ± 107 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

método de enumeración

index, element = min(list(enumerate(a)), key=lambda x:x[1])

min(list(enumerate(a)), key=lambda x:x[1])
(2, 7)

timeit min(list(enumerate(a)), key=lambda x:x[1])
1.45 µs ± 78.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Pablo MPA
fuente
4

Mientras sepa cómo usar lambda y el argumento "clave", una solución simple es:

max_index = max( range( len(my_list) ), key = lambda index : my_list[ index ] )
Veiga
fuente
¡Muy limpio! Y a diferencia de la respuesta aceptada, esto es cierto O (n), ¿verdad? Sé que O (2n) se considera O (n), pero para muy grandes npuede ser notablemente más lento.
kevlarr
4

Simple como eso :

stuff = [2, 4, 8, 15, 11]

index = stuff.index(max(stuff))
dctremblay
fuente
3

¿Por qué molestarse en agregar índices primero y luego revertirlos? La función Enumerate () es solo un caso especial de uso de la función zip (). Vamos a usarlo de manera apropiada:

my_indexed_list = zip(my_list, range(len(my_list)))

min_value, min_index = min(my_indexed_list)
max_value, max_index = max(my_indexed_list)
sofista
fuente
2

Solo una pequeña adición a lo que ya se ha dicho. values.index(min(values))parece devolver el índice más pequeño de min. Lo siguiente obtiene el índice más grande:

    values.reverse()
    (values.index(min(values)) + len(values) - 1) % len(values)
    values.reverse()

La última línea se puede omitir si el efecto secundario de la inversión en su lugar no importa.

Para iterar a través de todas las ocurrencias

    indices = []
    i = -1
    for _ in range(values.count(min(values))):
      i = values[i + 1:].index(min(values)) + i + 1
      indices.append(i)

Por el bien de la brevedad. Probablemente sea una mejor idea almacenar en caché min(values), values.count(min)fuera del bucle.

hiperbólico
fuente
2
reversed(…)en lugar de ….reverse()es probable que sea preferible ya que no muta y devuelve un generador de todos modos. Y todos los sucesos también podrían serminv = min(values); indices = [i for i, v in enumerate(values) if v == minv]
HoverHell
2

Una forma sencilla de encontrar los índices con un valor mínimo en una lista si no desea importar módulos adicionales:

min_value = min(values)
indexes_with_min_value = [i for i in range(0,len(values)) if values[i] == min_value]

Luego elija, por ejemplo, el primero:

choosen = indexes_with_min_value[0]
antoninstuppa
fuente
1

No tenga un representante lo suficientemente alto como para comentar sobre la respuesta existente.

Pero para https://stackoverflow.com/a/11825864/3920439 responda

Esto funciona para enteros, pero no funciona para una matriz de flotadores (al menos en Python 3.6). TypeError: list indices must be integers or slices, not float

ThePianoDentist
fuente
0

https://docs.python.org/3/library/functions.html#max

Si varios elementos son máximos, la función devuelve el primero encontrado. Esto es consistente con otras herramientas de preservación de la estabilidad de clasificación, comosorted(iterable, key=keyfunc, reverse=True)[0]

Para obtener más que solo el primero, utilice el método de clasificación.

import operator

x = [2, 5, 7, 4, 8, 2, 6, 1, 7, 1, 8, 3, 4, 9, 3, 6, 5, 0, 9, 0]

min = False
max = True

min_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = min )

max_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = max )


min_val_index[0]
>(0, 17)

max_val_index[0]
>(9, 13)

import ittertools

max_val = max_val_index[0][0]

maxes = [n for n in itertools.takewhile(lambda x: x[0] == max_val, max_val_index)]
El demz
fuente
0

¿Qué hay de esto?

a=[1,55,2,36,35,34,98,0]
max_index=dict(zip(a,range(len(a))))[max(a)]

Crea un diccionario a partir de los elementos en acomo claves y sus índices como valores, por lo tanto, dict(zip(a,range(len(a))))[max(a)]devuelve el valor que corresponde a la clave, max(a)que es el índice del máximo en a. Soy un principiante en Python, así que no sé sobre la complejidad computacional de esta solución.

Dr.Simplisist
fuente