Ottenere l'indice dell'elemento max o min restituito utilizzando max () / min () in un elenco


466

Sto usando Python maxe le minfunzioni sugli elenchi per un algoritmo minimax e ho bisogno dell'indice del valore restituito da max()o min(). In altre parole, ho bisogno di sapere quale mossa ha prodotto il valore massimo (al turno di un primo giocatore) o minimo (secondo giocatore).

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)

Devo essere in grado di restituire l'indice effettivo del valore minimo o massimo, non solo del valore.


32
Il builtin divmodesiste per evitare di dover dire [i / 3, i % 3]molto.
Mike Graham,

Risposte:


416
se isMinLevel:
    return valori.indice (min (valori))
altro:
    restituire valori.indice (max (valori))

38
@KevinGriffin, Nota che questo ti dà solo una delle possibili occorrenze del minimo / massimo. Questo potrebbe non essere quello che vuoi, ad esempio se è possibile aumentare il tuo guadagno negli stessi due modi, ma uno di loro fa più male all'altro giocatore. Non so se questo è un caso che devi considerare.
Mike Graham,

89
@Kashyap In realtà è O (N), non O (N ^ 2). Nel caso minimo, viene valutato il primo min (valori), che è O (N), quindi viene chiamato valori.index (), che è anche O (N). O (N) + O (N) = O (N). L'argomento da indicizzare viene valutato solo una volta. È equivalente a:tmp = min(values); return values.index(tmp)
Tom Karzes,

@ troppo php cosa fare quando c'è ripetizione di elementi.?
Shashi Tunga,

@ShashiTunga [list] .index () restituisce solo la prima occorrenza di qualcosa, non è garantito che sia esclusivo, il valore minimo potrebbe non essere univoco nell'elenco
Scott Anderson

473

Supponi di avere un elenco values = [3,6,1,5]e di avere bisogno dell'indice dell'elemento più piccolo, ovvero index_min = 2in questo caso.

Evita la soluzione con itemgetter()presentata nelle altre risposte e usa invece

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

perché non richiede import operatorné da usare enumerate, ed è sempre più veloce (benchmark sotto) di una soluzione che utilizza itemgetter().

Se hai a che fare con array intorpiditi o puoi permetterti numpycome dipendenza, considera anche l'uso

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

Questo sarà più veloce della prima soluzione anche se lo applichi a un puro elenco Python se:

  • è più grande di alcuni elementi (circa 2 ** 4 elementi sulla mia macchina)
  • puoi permetterti di copiare la memoria da un puro elenco a un numpyarray

come sottolinea questo benchmark: inserisci qui la descrizione dell'immagine

Ho eseguito il benchmark sulla mia macchina con Python 2.7 per le due soluzioni sopra (blu: Python puro, prima soluzione) (soluzione rossa, intorpidita) e per la soluzione standard basata su itemgetter()(nero, soluzione di riferimento). Lo stesso benchmark con python 3.5 ha mostrato che i metodi confrontano esattamente lo stesso del caso python 2.7 presentato sopra


Un +1 molto forte. Adoro il benchmarking delle soluzioni proposte e le regole empiriche che hai riassunto. Come ho suggerito in un'altra risposta di seguito, potresti fornire (o collegare a) il tuo codice di test in modo che altri possano riprodurre i tuoi risultati? Le macchine e le librerie cambiano nel tempo e consentirebbero il confronto con altre soluzioni.
Rakurai,

3
Penso che ci possa essere un refuso: xrange. Non dovrebbe essere gamma?
Lindsay Fowler,

6
@LindsayFowler xrange()è ora obsoleto, è possibile utilizzarerange()
davide

np.argmin non funziona per i float. solo il primo suggerimento funziona su ints e float.
jimh

Penso che ti sbagli, prova import numpy as np; x = [2.3, -1.4]; np.argmin(x). Vedrai che argminfunziona anche su float
gg349,

332

È possibile trovare contemporaneamente l'indice e il valore min / max se si enumerano gli elementi nell'elenco, ma si esegue min / max sui valori originali dell'elenco. Così:

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

In questo modo l'elenco verrà attraversato una sola volta per min (o max).


110
Oppure usa una lambda:key=lambda p: p[1]
scriva il

116

Se vuoi trovare l'indice di max all'interno di un elenco di numeri (che sembra il tuo caso), allora ti suggerisco di usare numpy:

import numpy as np
ind = np.argmax(mylist)

In caso di più occorrenze dei valori massimi, vengono restituiti gli indici corrispondenti alla prima occorrenza.
Cohensius,

41

Probabilmente una soluzione più semplice sarebbe quella di trasformare l'array di valori in un array di valori, coppie di indici e prenderne il massimo / minimo. Ciò darebbe l'indice più grande / più piccolo che ha il massimo / minimo (cioè le coppie vengono confrontate confrontando prima il primo elemento, quindi confrontando il secondo elemento se i primi sono gli stessi). Si noti che non è necessario creare effettivamente l'array, poiché min / max consentono generatori come input.

values = [3,4,5]
(m,i) = max((v,i) for i,v in enumerate(values))
print (m,i) #(5, 2)

30
list=[1.1412, 4.3453, 5.8709, 0.1314]
list.index(min(list))

Ti darà il primo indice del minimo.


18

Penso che la cosa migliore da fare sia convertire la lista in una numpy arraye usare questa funzione:

a = np.array(list)
idx = np.argmax(a)

14

Ero anche interessato a questo e ho confrontato alcune delle soluzioni suggerite usando perfplot (un mio progetto per animali domestici).

Si scopre che l' argmin di quel numpy ,

numpy.argmin(x)

è il metodo più veloce per elenchi abbastanza grandi, anche con la conversione implicita dall'input listin a numpy.array.

inserisci qui la descrizione dell'immagine


Codice per la generazione della 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,
    )

Si noti che la stessa conclusione è già stata pubblicata nella mia risposta, più di 2 anni fa, con ulteriori informazioni su quando e perché argmin può essere utilizzato o meno. Prendi in considerazione l'idea di eliminare la risposta, che non sta dando merito a ciò che è già stato proposto in questa stessa pagina. Considera anche di rivedere le tue altre risposte su SO per comportamenti simili: sembra che tu non citi la risposta effettiva che fornisce la migliore soluzione nelle tue analisi delle prestazioni. Questo è piuttosto negativo, specialmente per qualcuno con un rappresentante> 10K che è stato in giro abbastanza a lungo per conoscerlo meglio.
gg349,

@ gg349, ottimi punti, ma fornisce il codice sorgente per generare i risultati, rendendolo facilmente riproducibile e adattabile al confronto con altre soluzioni. Sono d'accordo che potrebbe prendere in considerazione la rimozione di questa risposta come duplicata, ma forse potresti aggiungere valore alla tua risposta includendo o collegando al codice che hai usato?
Rakurai,

8

Utilizzare un array numpy e la funzione argmax ()

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

8

Dopo aver ottenuto i valori massimi, prova questo:

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

Molto più semplice di molte opzioni.


6

Penso che la risposta sopra risolva il tuo problema, ma ho pensato di condividere un metodo che ti dà il minimo e tutti gli indici in cui appare il minimo.

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

Questo passa l'elenco due volte ma è ancora abbastanza veloce. È tuttavia leggermente più lento di trovare l'indice del primo incontro del minimo. Quindi, se hai bisogno solo di uno dei minimi, usa la soluzione di Matt Anderson , se ne hai bisogno tutti, usa questo.


1
Mi piace perché usa Python di base e trovo che la comprensione dell'elenco sia più facile da capire rispetto a itemgetter, lambda ecc. (E abbastanza flessibile da risolvere una varietà di compiti, come questo ....)
James,

crudo. Preferisco questo.
Dev_Man

6

Utilizzare la funzione numpy del modulo numpy.where

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

Per indice di valore minimo:

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

Per indice di valore massimo:

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

In effetti, questa funzione è molto più potente. Puoi porre tutti i tipi di operazioni booleane Per indice di valore compreso tra 3 e 60:

idx = n.where((x>3)&(x<60))[0]
idx
array([2, 3, 4, 5])
x[idx]
array([ 4,  7,  4, 56])

L'indice in Python inizia da 0. L'indice restituito deve essere 6 (per 65), mentre il codice restituisce 7 (la domanda di OP era "Ottenere l'indice ...")
tagoma,

Nel comando ho richiesto l'indice di valore minimo (qui: 1) il cui indice è 7. 65 è il valore massimo degli elementi nell'array. Se digiti: n.where (x == x.max ()) [0] otterrai un indice di max. valore che è 65 qui. Il suo indice sarà 6
Ishan Tomar,

uso di numpy: probabilmente vietato in questa applicazione. Ma se hai intenzione di usare intorpidito, sei molto meglio di usare argmin()invece di quello che hai fatto qui.
RBF06

Grazie @ RBF06 Lo controllerò.
Ishan Tomar,

5

Questo è semplicemente possibile usando il built-in enumerate()e la max()funzione e l' keyargomento opzionale della max()funzione e una semplice espressione lambda:

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

Nei documenti per max()dice che l' keyargomento si aspetta una funzione come nella list.sort()funzione. Vedi anche Ordinamento Come .

Funziona allo stesso modo per min(). Tra l'altro restituisce il primo valore max / min.


Risposta in ritardo ma migliore (se non hai bisogno di velocità).
mmj

5

Supponi di avere un elenco come:

a = [9,8,7]

I due metodi seguenti sono modi abbastanza compatti per ottenere una tupla con l'elemento minimo e il suo indice. Entrambi richiedono un tempo simile per l'elaborazione. Mi piace di più il metodo zip, ma questo è il mio gusto.

metodo 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)

metodo di enumerazione

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)

4

Finché sai come usare lambda e l'argomento "chiave", una soluzione semplice è:

max_index = max( range( len(my_list) ), key = lambda index : my_list[ index ] )

Molto pulito! E a differenza della risposta accettata, questo è vero O (n), giusto? So che O (2n) è considerato O (n), ma per dimensioni molto grandi npuò essere notevolmente più lento.
kevlarr

4

Semplice come quella :

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

index = stuff.index(max(stuff))

3

Perché preoccuparsi di aggiungere prima gli indici e poi invertirli? La funzione Enumerate () è solo un caso speciale di utilizzo della funzione zip (). Usiamolo in modo appropriato:

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)

2

Solo una piccola aggiunta a ciò che è già stato detto. values.index(min(values))sembra restituire il più piccolo indice di min. Quanto segue ottiene l'indice più grande:

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

L'ultima riga può essere esclusa se l'effetto collaterale dell'inversione in posizione non ha importanza.

Per scorrere tutte le occorrenze

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

Per brevità. Probabilmente è una migliore idea memorizzare nella cache min(values), values.count(min)al di fuori del ciclo.


2
reversed(…)invece di ….reverse()è probabilmente preferibile in quanto non muta e restituisce comunque un generatore. E anche tutte le ricorrenze potrebbero essereminv = min(values); indices = [i for i, v in enumerate(values) if v == minv]
HoverHell,

2

Un modo semplice per trovare gli indici con un valore minimo in un elenco se non si desidera importare moduli aggiuntivi:

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

Quindi scegli ad esempio il primo:

choosen = indexes_with_min_value[0]


0

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

Se più elementi sono massimi, la funzione restituisce il primo rilevato. Ciò è coerente con altri strumenti di conservazione della stabilità dell'ordinamento comesorted(iterable, key=keyfunc, reverse=True)[0]

Per ottenere qualcosa di più del primo utilizza il metodo di ordinamento.

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)]

0

Che dire di questo:

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

Crea un dizionario dagli elementi acome chiavi e i loro indici come valori, quindi dict(zip(a,range(len(a))))[max(a)]restituisce il valore corrispondente alla chiave max(a)che è l'indice del massimo in a. Sono un principiante in Python, quindi non conosco la complessità computazionale di questa soluzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.