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.