Modo Pythonic per trovare il massimo valore e il suo indice in un elenco?


151

Se voglio il valore massimo in un elenco, posso solo scrivere max(List), e se avessi bisogno anche dell'indice del valore massimo?

Posso scrivere qualcosa del genere:

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

Ma mi sembra noioso.

E se scrivo:

List.index(max(List))

Quindi ripeterà l'elenco due volte.

C'è un modo migliore?


Cosa intendi con "passerà l'elenco due volte"? List.index (max (List)) funziona per me.
mwcz,

14
@mwc: ripeterà l'elenco una volta per determinare il valore massimo, quindi ripeterà una seconda volta per trovare l'indice di quel valore.

10
List.index () non sarebbe problematico se ci fossero valori massimi duplicati?
Logan Yang,

@LoganYang sì, potrebbero esserci due elementi con lo stesso valore.
Florian,

Se l'ordine non è importante, potresti fare qualcosa come List.sort () [- 1]
Florian,

Risposte:


186

Esistono molte opzioni, ad esempio:

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

2
Ah, l'ho visto in altri posti, ma ho pensato che avrebbe restituito solo un valore, non una tupla.
Sunny88

1
@ Sunny88: la keyfunzione viene utilizzata solo per decidere quale elemento è massimo. Gli elementi non sono cambiati.
Sven Marnach

6
@SvenMarnach Perché non key=lambda e: e[1]invece e quindi evitare l'importazione?
equilibrio vitale

8
@lifebalance L'uso itemgetter()è più veloce ed evitare un'importazione non è un obiettivo che vale la pena perseguire. Evitare le dipendenze esterne può essere utile in alcuni casi, ma un'importazione dalla libreria standard non è un problema.
Sven Marnach,

324

Penso che la risposta accettata sia ottima, ma perché non la fai esplicitamente? Sento che più persone capiranno il tuo codice, e questo è in accordo con PEP 8:

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

Questo metodo è anche circa tre volte più veloce della risposta accettata:

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)

Risultati mentre vengono eseguiti sul mio computer:

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

Altro set:

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

3
Non mi aspettavo che fosse più veloce. È più veloce anche quando sostituisco l con "l = [random.random () per _ in xrange (10000000)] + [2]", che garantisce che l'ultimo elemento sia il più grande.
Sunny88,

14
@ Sunny88: per un semplice elenco di numeri, l'approccio semplice è più veloce. Se stai cercando prestazioni in questo caso, ti suggerisco di usare numpy.argmax(), che è un'altra 30 volte più veloce sulla mia macchina. Se l'elenco contiene oggetti più complicati dei semplici numeri, l'approccio nella mia risposta può diventare più veloce. Un altro vantaggio di questo approccio è che può essere utilizzato per iteratori arbitrari, non solo per elenchi.
Sven Marnach,

@ Sven-Marnach Sarebbe numpy più veloce, se prima dovessi convertire la mia lista in un array numpy? Sarebbe più veloce per il semplice esempio [0,1,0]?
tommy.carstensen,

1
@ Sven-Marnach Ho appena controllato. numpy.argmax è di gran lunga il metodo più lento e dà la risposta sbagliata, se l'array contiene stringhe invece di float o numeri interi.
tommy.carstensen,

9
List.index () non sarebbe problematico se ci fossero valori massimi duplicati?
Logan Yang,

20

Questa risposta è 33 volte più veloce di @Escualo supponendo che l'elenco sia molto grande e supponendo che sia già un np.array (). Ho dovuto ridurre il numero di esecuzioni di test perché il test esamina 10000000 elementi e non 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 )

Risultati sul mio computer:

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

Giusto per chiarire: l'accelerazione è dovuta solo all'implementazione insensibile di C rispetto a Python puro? O c'è un modo per ottenere qualche miglioramento alla risposta di @ Escualo usando il puro pitone?
massimo

Se si desidera utilizzare Python 3.6, si può fare qualcosa del tipo: "l = np.array ([random.random () per _ in range (10000000)])") print (f "Npmax: {(1000 * t. timeit (numero = 10) / 10): 5.2f} msec / pass ")
Piotr Siejda

Questo era il 2.7
portforwardpodcast il

1
Bene, la velocità di numpy.argmaxsembra incredibile fino a quando non lasci che elabori un elenco di pitone standard . Quindi la velocità sta tra la versione esplicita e implicita. Immagino np.arrayche non crei solo un elenco, ma salvi alcune informazioni extra, come ad esempio i valori minimo e massimo (solo un'ipotesi).
Miroslaw Opoka,

18

Con la libreria integrata di Python, è abbastanza facile:

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

Questo dice maxdi trovare il numero più grande nell'elenco [0, 1, 2, ..., len(a)], usando la funzione personalizzata lambda x: a[x], che dice che 0è effettivamente 2, 1è effettivamente 9, ecc.


In Python 3, non esiste xrange, se si desidera scrivere codice che verrà eseguito sia per Python 2 che per Python 3, è necessario utilizzare range ().
Chunde Huang,

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

Questo è meglio perché puoi adattarlo per usarlo con qualcosa di diverso dalla tupla.
wieczorek1990,

Come funziona esattamente? Puoi interrompere il processo?
clabe45,

Ciao @ clabe45, converte my_list in un elenco di tuple (v, i) dove v è ogni elemento del mio elenco e i è l'indice corrispondente, quindi ottiene la tupla con il valore massimo (e anche con l'indice associato)
Luis Sobrecueva,

4
Grazie, puoi pubblicarlo nella risposta possibilmente? E comemax a prendere in considerazione il primo elemento di ogni tupla ( v) quando si calcola il valore massimo?
clabe45,

1
@ clabe45 Forse questa risposta arriva troppo tardi, ma per gli altri (come me) che si sono imbattuti in questo thread ora, qui: stackoverflow.com/questions/18296755/… è una spiegazione. Non questa riga: "Per impostazione predefinita, max confronterà gli articoli con il primo indice, se il primo indice è lo stesso, confronterebbe il secondo indice." Quindi l'ho provato con list: l = [1,1,1] e poi max ([(v, i) per i, v in enumerate (l)]) e non mi dà il primo 1 ma l'ultimo uno: (1,2) come risultato. Spero che spieghi :)
Anupam Jain

10

Vorrei suggerire un modo molto semplice:

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

Spero che sia d'aiuto.


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

Se il valore massimo è presente più di una volta e si desidera ottenere tutti gli indici,

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

1
Sì. Ho quasi pubblicato una risposta, ma poi ho visto che avevi la stessa soluzione già con la stessa logica nella tua lista di comprensione one-liner.
WalyKu,

2

Forse hai bisogno di un elenco ordinato comunque?

Prova questo:

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. L'ordinamento ha una maggiore complessità temporale. 2. sorted_listnon ha indici ma valori, quindi non funzionerebbe.

1

mi dispiace per aver rilanciato questo thread, ma ho pensato che valesse la pena aggiungere il mio metodo.

Il nome dell'elenco in questo 'elenco' di esempio

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

Che stamperà il valore più alto nell'elenco facile come!

list.sort()ordina l'elenco in base al valore dell'elemento nella tabella ASCII , quindi ordina in modo efficace l'elenco dal più basso al più alto. Quindi stampo l'ultimo valore nell'elenco (che sarà il numero più grande) usando print(list[-1]).

Spero che questo ti aiuti!


5
non possiamo ottenere l'indice in questo modo
toing_toing

-1

Ecco una soluzione completa alla tua domanda utilizzando le funzioni integrate di 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)
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.