182 votes

Méthode pythonique pour trouver la valeur maximale et son indice dans une liste ?

Si je veux la valeur maximale d'une liste, je peux simplement écrire max(List) mais que faire si j'ai également besoin de l'indice de la valeur maximale ?

Je peux écrire quelque chose comme ça :

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

Mais ça me semble fastidieux.

Et si j'écris :

List.index(max(List))

Ensuite, la liste sera itérée deux fois.

Y a-t-il un meilleur moyen ?

0 votes

Que voulez-vous dire par "il passera la liste deux fois" ? List.index(max(List)) fonctionne pour moi.

14 votes

@mwc : Il va itérer la liste une fois pour déterminer la valeur maximale, puis l'itérer une seconde fois pour trouver l'indice de cette valeur.

10 votes

Est-ce que list.index() ne serait pas problématique s'il y a des valeurs max dupliquées ?

351voto

Escualo Points 12584

Je pense que la réponse acceptée est excellente, mais pourquoi ne pas le faire explicitement ? Je pense que plus de personnes comprendraient votre code, et cela est en accord avec la PEP 8 :

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

Cette méthode est également environ trois fois plus rapide que la réponse acceptée :

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)

Les résultats tels qu'ils s'exécutent dans mon ordinateur :

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

Autre ensemble :

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

3 votes

Je ne m'attendais pas à ce que ce soit plus rapide. C'est plus rapide même lorsque je remplace l par "l = [random.random() for _ in xrange(10000000)]+[2]", ce qui garantit que le dernier élément est le plus grand.

16 votes

@Sunny88 : Pour une simple liste de chiffres, l'approche simple est plus rapide. Si vous recherchez la performance dans ce cas, je vous suggère d'utiliser numpy.argmax() qui est encore 30 fois plus rapide sur ma machine. Si la liste contient des objets plus compliqués que de simples nombres, l'approche de ma réponse peut devenir plus rapide. Un autre avantage de cette approche est qu'elle peut être utilisée pour des itérateurs arbitraires, pas seulement pour des listes.

0 votes

@Sven-Marnach Est-ce que numpy serait plus rapide, si je devais d'abord convertir ma liste en un tableau numpy ? Serait-il plus rapide pour l'exemple simple [0,1,0] ?

200voto

Sven Marnach Points 133943

Il existe de nombreuses possibilités, par exemple :

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

2 votes

Ah, j'ai vu cela à d'autres endroits, mais je pensais que cela retournerait une seule valeur, et non un tuple.

1 votes

@Sunny88 : Le key n'est utilisée que pour décider quel élément est maximal. Les éléments ne sont pas modifiés.

8 votes

@SvenMarnach Pourquoi pas key=lambda e: e[1] à la place et ainsi éviter l'importation ?

21voto

portforwardpodcast Points 2044

Cette réponse est 33 fois plus rapide que @Escualo en supposant que la liste est très grande, et en supposant qu'elle est déjà un np.array(). J'ai dû réduire le nombre d'exécutions du test parce que le test porte sur 10000000 éléments et pas seulement 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 )

Résultats sur mon ordinateur :

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

21voto

Sunil Kapil Points 31

Avec la bibliothèque intégrée de Python, c'est assez facile :

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

Cela signifie que max pour trouver le plus grand nombre de la liste [0, 1, 2, ..., len(a)] en utilisant la fonction personnalisée lambda x: a[x] qui dit que 0 est en fait 2 , 1 est en fait 9 etc.

19voto

Igor Manzhos Points 51

Je vous propose un moyen très simple :

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

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X