Obtenir l'index de l'élément max ou min retourné en utilisant max () / min () sur une liste

467

J'utilise Python maxet les minfonctions sur les listes pour un algorithme minimax, et j'ai besoin de l'index de la valeur retournée par max()ou min(). En d'autres termes, j'ai besoin de savoir quel mouvement a produit la valeur max (au tour d'un premier joueur) ou min (deuxième joueur).

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)

J'ai besoin de pouvoir renvoyer l'index réel de la valeur min ou max, pas seulement la valeur.

Kevin Griffin
la source
32
Le builtin divmodexiste pour éviter d'avoir à en dire [i / 3, i % 3]beaucoup.
Mike Graham

Réponses:

417
si isMinLevel:
    return values.index (min (valeurs))
autre:
    return values.index (max (valeurs))
trop de php
la source
38
@KevinGriffin, notez que cela ne vous donne qu'une seule des éventuelles occurrences du minimum / maximum. Ce n'est peut-être pas ce que vous voulez, par exemple s'il est possible d'augmenter votre gain de la même manière, mais l'un d'eux blesse davantage l'autre joueur. Je ne sais pas si c'est un cas que vous devez considérer.
Mike Graham
89
@Kashyap C'est en fait O (N), pas O (N ^ 2). Dans le cas min, le premier min (valeurs) est évalué, ce qui est O (N), puis values.index () est appelé, qui est également O (N). O (N) + O (N) = O (N). L'argument à indexer n'est évalué qu'une seule fois. C'est équivalent à:tmp = min(values); return values.index(tmp)
Tom Karzes
@ trop de php que faire quand il y a répétition d'éléments.?
Shashi Tunga
@ShashiTunga [list] .index () renvoie uniquement la première occurrence de quelque chose, il n'est pas garanti qu'il soit exclusif, la valeur minimale peut ne pas être unique dans la liste
Scott Anderson
473

Disons que vous avez une liste values = [3,6,1,5]et que vous avez besoin de l'index du plus petit élément, c'est- index_min = 2à- dire dans ce cas.

Évitez la solution itemgetter()présentée dans les autres réponses et utilisez plutôt

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

car il ne demande import operatorni à utiliser enumerate, et il est toujours plus rapide (benchmark ci-dessous) qu'une solution utilisant itemgetter().

Si vous avez affaire à des tableaux numpy ou si vous pouvez vous permettre numpyune dépendance, envisagez également d'utiliser

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

Ce sera plus rapide que la première solution même si vous l'appliquez à une liste Python pure si:

  • il est plus grand que quelques éléments (environ 2 ** 4 éléments sur ma machine)
  • vous pouvez vous permettre la copie de la mémoire d'une liste pure vers un numpytableau

comme l'indique cette référence: entrez la description de l'image ici

J'ai exécuté le benchmark sur ma machine avec python 2.7 pour les deux solutions ci-dessus (bleu: python pur, première solution) (rouge, solution numpy) et pour la solution standard basée sur itemgetter()(noir, solution de référence). Le même benchmark avec python 3.5 a montré que les méthodes comparent exactement la même chose que le cas python 2.7 présenté ci-dessus

gg349
la source
Un +1 très fort. J'adore l'analyse comparative des solutions proposées et les règles générales que vous avez résumées. Comme je l'ai suggéré dans une autre réponse ci-dessous, pourriez-vous fournir (ou créer un lien vers) votre code de test afin que d'autres puissent reproduire vos résultats? Les machines et les bibliothèques évoluent avec le temps, ce qui permettrait de comparer avec d'autres solutions.
Rakurai
3
Je pense qu'il peut y avoir une faute de frappe: xrange. Ne devrait-elle pas être portée?
Lindsay Fowler
6
@LindsayFowler xrange()est désormais obsolète, vous pouvez utiliserrange()
davide
np.argmin ne fonctionne pas pour les flottants. seule la première suggestion fonctionne sur les pouces et les flotteurs.
jimh
Je pense que vous vous trompez, essayez import numpy as np; x = [2.3, -1.4]; np.argmin(x). Vous verrez que cela argminfonctionne aussi sur les flotteurs
gg349
332

Vous pouvez trouver l'index et la valeur min / max en même temps si vous énumérez les éléments de la liste, mais effectuez min / max sur les valeurs d'origine de la liste. Ainsi:

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 cette façon, la liste ne sera parcourue qu'une seule fois pendant min (ou max).

Matt Anderson
la source
110
Ou utilisez un lambda:key=lambda p: p[1]
scry
116

Si vous voulez trouver l'index de max dans une liste de nombres (ce qui semble être votre cas), je vous suggère d'utiliser numpy:

import numpy as np
ind = np.argmax(mylist)
dr.haz
la source
En cas d'occurrences multiples des valeurs maximales, les indices correspondant à la première occurrence sont retournés.
Cohensius
41

Une solution plus simple serait peut-être de transformer le tableau de valeurs en un tableau de valeurs, des paires d'index, et d'en prendre le maximum / min. Cela donnerait l'indice le plus grand / le plus petit qui a le max / min (c'est-à-dire que les paires sont comparées en comparant d'abord le premier élément, puis en comparant le deuxième élément si les premiers sont les mêmes). Notez qu'il n'est pas nécessaire de créer réellement le tableau, car min / max autorisent les générateurs en entrée.

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

Vous donnera le premier indice du minimum.

Andy
la source
18

Je pense que la meilleure chose à faire est de convertir la liste en a numpy arrayet d'utiliser cette fonction:

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

J'étais également intéressé par cela et j'ai comparé certaines des solutions suggérées en utilisant perfplot (un de mes projets ).

Il s'avère que l' argmin de ce numpy ,

numpy.argmin(x)

est la méthode la plus rapide pour des listes suffisamment grandes, même avec la conversion implicite de l'entrée listen a numpy.array.

entrez la description de l'image ici


Code de génération du tracé:

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
la source
Notez que la même conclusion est déjà affichée ci-dessus dans ma réponse, il y a plus de 2 ans, avec plus d'informations sur quand et pourquoi argmin peut être utilisé ou non. Pensez à supprimer la réponse, qui ne donne pas non plus de mérite à ce qui a déjà été proposé sur cette même page. Pensez également à revoir vos autres réponses sur SO pour un comportement similaire: vous semblez ne pas citer la réponse réelle fournissant la meilleure solution dans vos analyses de performances. C'est plutôt mauvais, surtout pour quelqu'un avec> 10K rep qui existe depuis assez longtemps pour mieux savoir.
gg349
@ gg349, de très bons points, mais il fournit le code source pour générer les résultats, ce qui le rend facilement reproductible et adaptable à la comparaison d'autres solutions. Je suis d'accord qu'il pourrait envisager de supprimer cette réponse en double, mais peut-être pourriez-vous ajouter de la valeur à votre réponse en incluant ou en créant un lien vers le code que vous avez utilisé?
Rakurai
8

Utilisez un tableau numpy et la fonction argmax ()

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

Après avoir obtenu les valeurs maximales, essayez ceci:

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

Beaucoup plus simple que beaucoup d'options.

alpha_989
la source
6

Je pense que la réponse ci-dessus résout votre problème, mais je pensais partager une méthode qui vous donne le minimum et tous les indices dans lesquels le minimum apparaît.

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

Cela passe la liste deux fois mais est encore assez rapide. Il est cependant légèrement plus lent que de trouver l'indice de la première rencontre du minimum. Donc, si vous avez besoin d'un seul des minima, utilisez la solution de Matt Anderson , si vous en avez tous besoin, utilisez-la.

Burak Bağdatlı
la source
1
J'aime ça parce qu'il utilise Python de base, et je trouve la compréhension de liste plus facile à comprendre que itemgetter, lambda etc. (et suffisamment flexible pour résoudre une variété de tâches, comme celle-ci ....)
James
brut. Je préfère ça.
Dev_Man
6

Utilisez la fonction numpy du module numpy.where

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

Pour l'indice de valeur minimale:

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

Pour l'indice de valeur maximale:

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

En fait, cette fonction est beaucoup plus puissante. Vous pouvez poser toutes sortes d'opérations booléennes Pour un indice de valeur entre 3 et 60:

idx = n.where((x>3)&(x<60))[0]
idx
array([2, 3, 4, 5])
x[idx]
array([ 4,  7,  4, 56])
Ishan Tomar
la source
index en python commence à 0. l'index retourné doit être 6 (pour 65), tandis que votre code retourne 7 (la question de OP était "Obtenir l'index ...")
tagoma
Dans la commande, j'ai demandé un indice de valeur minimale (ici: 1) dont l'indice EST 7. 65 est la valeur maximale des éléments du tableau. Si vous tapez: n. Où (x == x.max ()) [0] vous obtiendrez un index de max. valeur qui est de 65 ici. Son indice sortira à 6
Ishan Tomar
utilisation de numpy: probablement interdite dans cette application. Mais si vous allez utiliser numpy, vous êtes bien mieux d'utiliser simplement argmin()au lieu de ce que vous avez fait ici.
RBF06
Merci @ RBF06 je vais le vérifier.
Ishan Tomar
5

Ceci est tout simplement possible en utilisant le haut- enumerate()et la max()fonction et l'option keyargument de la max()fonction et une simple expression lambda:

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

Dans les documents car max()il dit que l' keyargument attend une fonction comme dans la list.sort()fonction. Voir également le guide de tri .

Il en va de même pour min(). Btw, il renvoie la première valeur max / min.

Simon Hänisch
la source
Tard mais meilleure réponse (si vous n'avez pas besoin de vitesse).
mmj
5

Supposons que vous ayez une liste telle que:

a = [9,8,7]

Les deux méthodes suivantes sont des moyens assez compacts pour obtenir un tuple avec l'élément minimum et son index. Les deux prennent un temps similaire à traiter. Je préfère la méthode zip, mais c'est mon goût.

méthode 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)

énumérer la méthode

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
la source
4

Tant que vous savez utiliser lambda et l'argument "clé", une solution simple est:

max_index = max( range( len(my_list) ), key = lambda index : my_list[ index ] )
Veiga
la source
Très propre! Et contrairement à la réponse acceptée, c'est vrai O (n), non? Je sais que O (2n) est considéré comme O (n), mais pour les très grands, nil peut être sensiblement plus lent.
kevlarr
4

Aussi simple que cela :

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

index = stuff.index(max(stuff))
dctremblay
la source
3

Pourquoi s'embêter à ajouter des indices d'abord puis à les inverser? La fonction Enumerate () n'est qu'un cas particulier d'utilisation de la fonction zip (). Utilisons-le de manière appropriée:

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)
sophiste
la source
2

Juste un ajout mineur à ce qui a déjà été dit. values.index(min(values))semble renvoyer le plus petit indice de min. Ce qui suit obtient le plus grand indice:

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

La dernière ligne peut être laissée de côté si l'effet secondaire de l'inversion en place n'a pas d'importance.

Pour parcourir toutes les occurrences

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

Pour des raisons de brièveté. Il est probablement préférable de mettre en cache en min(values), values.count(min)dehors de la boucle.

hyperbolique
la source
2
reversed(…)au lieu de ….reverse()est probablement préférable car il ne mute pas et renvoie de toute façon un générateur. Et toutes les occurrences pourraient également êtreminv = min(values); indices = [i for i, v in enumerate(values) if v == minv]
HoverHell
2

Un moyen simple de trouver les index avec une valeur minimale dans une liste si vous ne voulez pas importer de modules supplémentaires:

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

Choisissez ensuite par exemple le premier:

choosen = indexes_with_min_value[0]
antoninstuppa
la source
1

N'ont pas assez de représentants pour commenter la réponse existante.

Mais pour https://stackoverflow.com/a/11825864/3920439 réponse

Cela fonctionne pour les entiers, mais ne fonctionne pas pour les tableaux de flottants (au moins en python 3.6) TypeError: list indices must be integers or slices, not float

ThePianoDentist
la source
0

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

Si plusieurs éléments sont maximaux, la fonction renvoie le premier rencontré. Ceci est cohérent avec d'autres outils préservant la stabilité du tri tels quesorted(iterable, key=keyfunc, reverse=True)[0]

Pour obtenir plus que la première, utilisez la méthode de tri.

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)]
Le Demz
la source
0

Et ça:

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

Il crée un dictionnaire à partir des éléments en atant que clés et de leurs index en tant que valeurs, dict(zip(a,range(len(a))))[max(a)]renvoie ainsi la valeur qui correspond à la clé max(a)qui est l'index du maximum dans a. Je suis un débutant en python donc je ne connais pas la complexité de calcul de cette solution.

Dr.Simplisist
la source