Comment trouver toutes les positions de la valeur maximale dans une liste?


152

J'ai une liste:

a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50,
             35, 41, 49, 37, 19, 40, 41, 31]

L'élément max est de 55 (deux éléments sur les positions 9 et 12)

J'ai besoin de trouver sur quelle (s) position (s) se situe la valeur maximale. S'il vous plaît, aidez.

Réponses:


210
>>> m = max(a)
>>> [i for i, j in enumerate(a) if j == m]
[9, 12]

4
Bonne réponse courte si cela ne vous dérange pas de faire plusieurs passes dans la liste - ce qui est probable.
martineau

Sauf que le grand 0 pour ceci est 2n, la liste est itérée sur 2x, une fois pour déterminer max, et une autre fois pour trouver les positions du max. Une boucle for qui suit le max actuel et sa position pourrait être plus efficace pour les listes très longues.
radtek

1
@radtek big O est juste n. les principaux coefficients sont ignorés dans le grand O
michaelsnowden

1
Théoriquement, O (N) et O (2N) sont identiques mais pratiquement, O (N) aura certainement un temps de fonctionnement plus court, d'autant plus que N approche de l'infini.
radtek

314
a.index(max(a))

vous indiquera l'index de la première instance du plus grand élément valorisé de la liste a.


8
Cela ne vous donnera que la première instance et il a demandé tous les index où se trouve la plus grande valeur. Vous devrez boucler sur cette tranche en utilisant pour obtenir la liste restante dans chaque cas et gérer l'exception lorsqu'elle n'est plus trouvée.
jaydel

10
J'ai mentionné que cela ne donnerait que la première instance. Si vous les voulez tous, la solution de SilentGhost est beaucoup plus jolie et moins sujette aux erreurs.
nmichaels

7
Au moins au moment où j'y suis arrivé, la question demande explicitement une liste dans le cas de plusieurs maxima ...
emmagras

2
Techniquement, vous pouvez l'utiliser pour obtenir la première instance de l'élément valorisé le plus grand, puis le définir sur un nombre négatif ridiculement grand, puis trouver le prochain élément valorisé le plus grand, mais ce serait trop complexe.
Neil Chowdhury

@nmichaels Existe-t-il un meilleur moyen d'obtenir toutes les positions de valeur maximale dans une liste que la réponse acceptée?
shaik moeed

18

La réponse choisie (et la plupart des autres) nécessite au moins deux passages dans la liste.
Voici une solution en un seul passage qui pourrait être un meilleur choix pour les listes plus longues.

Édité: Pour remédier aux deux lacunes soulignées par @John Machin. Pour (2), j'ai tenté d'optimiser les tests en fonction de la probabilité estimée d'occurrence de chaque condition et des inférences permises par les prédécesseurs. Il était un peu difficile de déterminer les valeurs d'initialisation appropriées pour max_valet max_indicesqui fonctionnaient dans tous les cas possibles, surtout si le max se trouvait être la première valeur de la liste - mais je pense que c'est le cas maintenant.

def maxelements(seq):
    ''' Return list of position(s) of largest element '''
    max_indices = []
    if seq:
        max_val = seq[0]
        for i,val in ((i,val) for i,val in enumerate(seq) if val >= max_val):
            if val == max_val:
                max_indices.append(i)
            else:
                max_val = val
                max_indices = [i]

    return max_indices

4
(1) La gestion de la liste vide nécessite une attention particulière. Doit revenir []comme annoncé ("Liste de retour"). Le code doit être simple if not seq: return []. (2) Le schéma de test en boucle est sous-optimal: en moyenne dans les listes aléatoires, la condition val < maxvalsera la plus courante mais le code ci-dessus prend 2 tests au lieu d'un.
John Machin

+1 au commentaire de @John Machin pour avoir attrapé l'incohérence avec la docstring et ne pas me laisser m'en tirer avec la publication de code sous-optimal. Pour être honnête, puisqu'une réponse était déjà acceptée, j'ai perdu un peu de motivation pour continuer à travailler sur ma réponse, car je supposais que presque personne ne la regarderait plus loin - et c'est tellement plus long que tout le monde.
martineau

1
@martineau: les réponses «acceptées» ne sont pas nécessairement «acceptables». Je lis généralement toutes les réponses. Y compris votre révision. Ce qui fait 3 tests maintenant dans le cas rare de ==au lieu de 2 - votre elifcondition sera toujours vraie.
John Machin

@John Machin: Je me suis vraiment inspiré et je l'ai encore révisé. Maintenant, il s'agit de tests supplémentaires minimaux, ainsi que de quelques autres modifications. Merci pour vos commentaires et critiques constructives. J'ai attrapé le toujours vrai elifmoi-même, FWIW. ;-)
martineau

@John Machin: Hmmm, vos résultats de timing semblent contredire les miens, donc je vais supprimer ce que j'ai dit dans ma réponse sur le timing afin que je puisse examiner ce qui se passe plus loin. Merci pour l'information. En fait, je pense qu'un test de synchronisation "réel" devrait utiliser des valeurs de liste aléatoires.
martineau

10

J'ai proposé ce qui suit et cela fonctionne comme vous pouvez le voir max, minet d'autres fonctions sur des listes comme celles-ci:

Alors, veuillez considérer la liste d'exemples suivante pour connaître la position du maximum dans la liste a:

>>> a = [3,2,1, 4,5]

Utilisation du générateur enumerate et réalisation d'un casting

>>> list(enumerate(a))
[(0, 3), (1, 2), (2, 1), (3, 4), (4, 5)]

À ce stade, nous pouvons extraire la position de max avec

>>> max(enumerate(a), key=(lambda x: x[1]))
(4, 5)

Ce qui précède nous dit que le maximum est en position 4 et sa valeur est 5.

Comme vous le voyez, dans l' keyargument, vous pouvez trouver le maximum sur n'importe quel objet itérable en définissant un lambda approprié.

J'espère que cela contribue.

PD: Comme @PaulOyster l'a noté dans un commentaire. Avec Python 3.xla minet maxpermettre à un nouveau mot - clé defaultqui permettent d' éviter l'exception de soulever ValueErrorlorsque l' argument est une liste vide.max(enumerate(list), key=(lambda x:x[1]), default = -1)


2
C'est une meilleure solution, car elle implique un seul passage. Quelques commentaires, cependant: 1. pas besoin de lister () l'énumération, 2. lambda mieux être mis entre parenthèses, 3. min () et max () ont maintenant un paramètre par défaut (qui est retourné sur une entrée vide), donc peuvent utiliser it (default = -1, par exemple) pour éviter une exception ValueError, et 4. veuillez changer pour max (), car c'était la question d'origine.
Paul Oyster

environ 3 éléments, oui, cela fonctionne uniquement avec Python 3.x. Je vais le mentionner. Et réparé tout le reste. ;)
jonaprieto

2
Cela ne trouverait la position de l'un des éléments à valeur maximale (le premier) que lorsqu'il apparaît plus d'une fois dans la liste - donc ne répond pas à la question posée.
martineau

8

Je ne peux pas reproduire la performance de @ SilentGhost citée par @martineau. Voici mon effort avec des comparaisons:

=== maxelements.py ===

a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50,
             35, 41, 49, 37, 19, 40, 41, 31]
b = range(10000)
c = range(10000 - 1, -1, -1)
d = b + c

def maxelements_s(seq): # @SilentGhost
    ''' Return list of position(s) of largest element '''
    m = max(seq)
    return [i for i, j in enumerate(seq) if j == m]

def maxelements_m(seq): # @martineau
    ''' Return list of position(s) of largest element '''
    max_indices = []
    if len(seq):
        max_val = seq[0]
        for i, val in ((i, val) for i, val in enumerate(seq) if val >= max_val):
            if val == max_val:
                max_indices.append(i)
            else:
                max_val = val
                max_indices = [i]
    return max_indices

def maxelements_j(seq): # @John Machin
    ''' Return list of position(s) of largest element '''
    if not seq: return []
    max_val = seq[0] if seq[0] >= seq[-1] else seq[-1]
    max_indices = []
    for i, val in enumerate(seq):
        if val < max_val: continue
        if val == max_val:
            max_indices.append(i)
        else:
            max_val = val
            max_indices = [i]
    return max_indices

Résultats d'un vieux portable usé exécutant Python 2.7 sur Windows XP SP3:

>\python27\python -mtimeit -s"import maxelements as me" "me.maxelements_s(me.a)"
100000 loops, best of 3: 6.88 usec per loop

>\python27\python -mtimeit -s"import maxelements as me" "me.maxelements_m(me.a)"
100000 loops, best of 3: 11.1 usec per loop

>\python27\python -mtimeit -s"import maxelements as me" "me.maxelements_j(me.a)"
100000 loops, best of 3: 8.51 usec per loop

>\python27\python -mtimeit -s"import maxelements as me;a100=me.a*100" "me.maxelements_s(a100)"
1000 loops, best of 3: 535 usec per loop

>\python27\python -mtimeit -s"import maxelements as me;a100=me.a*100" "me.maxelements_m(a100)"
1000 loops, best of 3: 558 usec per loop

>\python27\python -mtimeit -s"import maxelements as me;a100=me.a*100" "me.maxelements_j(a100)"
1000 loops, best of 3: 489 usec per loop

7
a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 
         55, 23, 31, 55, 21, 40, 18, 50,
         35, 41, 49, 37, 19, 40, 41, 31]

import pandas as pd

pd.Series(a).idxmax()

9

C'est comme ça que je le fais habituellement.


6

Vous pouvez également utiliser le package numpy:

import numpy as np
A = np.array(a)
maximum_indices = np.where(A==max(a))

Cela renverra un tableau numpy de tous les indices contenant la valeur maximale

si vous souhaitez transformer cela en une liste:

maximum_indices_list = maximum_indices.tolist()

5
>>> max(enumerate([1,2,3,32,1,5,7,9]),key=lambda x: x[1])
>>> (3, 32)

C'est faux. Essayez de mettre le nombre maximum au milieu de la liste.
goncalopp le

1
C'est faux. La question dit "trouver toutes les positions de la valeur maximale".
Kapil

5

Une solution, qui ne donne que la première apparence , peut également être obtenue en utilisant numpy:

>>> import numpy as np
>>> a_np = np.array(a)
>>> np.argmax(a_np)
9

3

@shash a répondu à cela ailleurs

Une manière pythonique de trouver l'index de l'élément de liste maximum serait

position = max(enumerate(a), key=lambda x: x[1])[0]

Ce qui fait une passe . Pourtant, il est plus lent que la solution de @Silent_Ghost et, plus encore, de @nmichaels:

for i in s m j n; do echo $i;  python -mtimeit -s"import maxelements as me" "me.maxelements_${i}(me.a)"; done
s
100000 loops, best of 3: 3.13 usec per loop
m
100000 loops, best of 3: 4.99 usec per loop
j
100000 loops, best of 3: 3.71 usec per loop
n
1000000 loops, best of 3: 1.31 usec per loop

2

Voici la valeur maximale et les index auxquels elle apparaît:

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50, 35, 41, 49, 37, 19, 40, 41, 31]
>>> for i, x in enumerate(a):
...     d[x].append(i)
... 
>>> k = max(d.keys())
>>> print k, d[k]
55 [9, 12]

Plus tard: pour la satisfaction de @SilentGhost

>>> from itertools import takewhile
>>> import heapq
>>> 
>>> def popper(heap):
...     while heap:
...         yield heapq.heappop(heap)
... 
>>> a = [32, 37, 28, 30, 37, 25, 27, 24, 35, 55, 23, 31, 55, 21, 40, 18, 50, 35, 41, 49, 37, 19, 40, 41, 31]
>>> h = [(-x, i) for i, x in enumerate(a)]
>>> heapq.heapify(h)
>>> 
>>> largest = heapq.heappop(h)
>>> indexes = [largest[1]] + [x[1] for x in takewhile(lambda large: large[0] == largest[0], popper(h))]
>>> print -largest[0], indexes
55 [9, 12]

vous réalisez à quel point c'est inefficace?
SilentGhost

1
Rationalisations: (1) "L'optimisation prématurée est le ... etc." (2) Cela n'a probablement pas d'importance. (3) C'est toujours une bonne solution. Peut-être que je vais le recoder pour l'utiliser heapq- trouver le maximum là-bas serait trivial.
hughdbrown

alors que j'aimerais voir votre heapqsolution, je doute que cela fonctionne.
SilentGhost

2

Idée similaire avec une compréhension de liste mais sans énumération

m = max(a)
[i for i in range(len(a)) if a[i] == m]

Je ne suis pas un électeur défavorable, mais notez que cela n'a pas l'air vraiment bien et ne fonctionnera pas bien: itérer dans les index au lieu de parcourir la liste est très gênant en Python, vous essayez d'éviter cela. De plus, c'est certainement plus lent que la solution avec énumérer à cause de l' a[i]appel.
yo '

1

Juste une ligne:

idx = max(range(len(a)), key = lambda i: a[i])

Bien, mais il ne renvoie pas TOUS les index, juste le premier.
iggy

1

Si vous souhaitez obtenir les indices des plus grands nnombres dans une liste appelée data, vous pouvez utiliser Pandas sort_values:

pd.Series(data).sort_values(ascending=False).index[0:n]

0
import operator

def max_positions(iterable, key=None, reverse=False):
  if key is None:
    def key(x):
      return x
  if reverse:
    better = operator.lt
  else:
    better = operator.gt

  it = enumerate(iterable)
  for pos, item in it:
    break
  else:
    raise ValueError("max_positions: empty iterable")
    # note this is the same exception type raised by max([])
  cur_max = key(item)
  cur_pos = [pos]

  for pos, item in it:
    k = key(item)
    if better(k, cur_max):
      cur_max = k
      cur_pos = [pos]
    elif k == cur_max:
      cur_pos.append(pos)

  return cur_max, cur_pos

def min_positions(iterable, key=None, reverse=False):
  return max_positions(iterable, key, not reverse)

>>> L = range(10) * 2
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> max_positions(L)
(9, [9, 19])
>>> min_positions(L)
(0, [0, 10])
>>> max_positions(L, key=lambda x: x // 2, reverse=True)
(0, [0, 1, 10, 11])

0

Ce code n'est pas aussi sophistiqué que les réponses publiées précédemment mais il fonctionnera:

m = max(a)
n = 0    # frequency of max (a)
for number in a :
    if number == m :
        n = n + 1
ilist = [None] * n  # a list containing index values of maximum number in list a.
ilistindex = 0
aindex = 0  # required index value.    
for number in a :
    if number == m :
        ilist[ilistindex] = aindex
        ilistindex = ilistindex + 1
    aindex = aindex + 1

print ilist

ilist dans le code ci-dessus contiendrait toutes les positions du nombre maximum dans la liste.


0

Vous pouvez le faire de différentes manières.

La vieille méthode conventionnelle est,

maxIndexList = list() #this list will store indices of maximum values
maximumValue = max(a) #get maximum value of the list
length = len(a)       #calculate length of the array

for i in range(length): #loop through 0 to length-1 (because, 0 based indexing)
    if a[i]==maximumValue: #if any value of list a is equal to maximum value then store its index to maxIndexList
        maxIndexList.append(i)

print(maxIndexList) #finally print the list

Une autre façon sans calculer la longueur de la liste et stocker la valeur maximale dans une variable,

maxIndexList = list()
index = 0 #variable to store index
for i in a: #iterate through the list (actually iterating through the value of list, not index )
    if i==max(a): #max(a) returns a maximum value of list.
        maxIndexList.append(index) #store the index of maximum value
index = index+1 #increment the index

print(maxIndexList)

Nous pouvons le faire de manière pythonique et intelligente! Utilisation de la compréhension de liste sur une seule ligne,

maxIndexList = [i for i,j in enumerate(a) if j==max(a)] #here,i=index and j = value of that index

Tous mes codes sont en Python 3.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.