¿Cómo encontrar todas las posiciones del valor máximo en una lista?

152

Tengo una lista:

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]

El elemento máximo es 55 (dos elementos en las posiciones 9 y 12)

Necesito encontrar en qué posición (es) se encuentra el valor máximo. Por favor ayuda.

Beto
fuente

Respuestas:

210
>>> m = max(a)
>>> [i for i, j in enumerate(a) if j == m]
[9, 12]
SilentGhost
fuente
44
Buena respuesta corta si no te importa hacer múltiples pases por la lista, lo cual es probable.
Martineau
Excepto que el 0 grande para esto es 2n, la lista se repite en 2x, una vez para determinar el máximo, y otra vez para encontrar las posiciones del máximo. Un bucle for que rastrea el máximo actual y su posición podría ser más eficiente para listas realmente largas.
radtek
1
@radtek big O es solo n. los coeficientes principales se ignoran en la gran O
michaelsnowden
1
Teóricamente, O (N) y O (2N) son iguales, pero prácticamente, O (N) definitivamente tendrá un tiempo de ejecución más corto, especialmente cuando N se acerca al infinito.
radtek
314
a.index(max(a))

le indicará el índice de la primera instancia del elemento más valioso de la lista a.

nmichaels
fuente
8
Sin embargo, esto solo le brindará la primera instancia y solicitó todos los índices donde se encuentra el valor más grande. Tendría que hacer un bucle con ese segmento para obtener la lista restante en cada caso y manejar la excepción cuando ya no se encuentre.
Jaydel
10
Mencioné que solo daría la primera instancia. Si quiere todos ellos, la solución de SilentGhost es mucho más bonita y menos propensa a errores.
nmichaels
77
Al menos cuando llegué a él, la pregunta pide explícitamente una lista en el caso de múltiples máximos ...
emmagras
2
Técnicamente, podría usar esto para obtener la primera instancia del elemento valorado más grande y luego establecerlo en un número negativo ridículamente grande, y luego encontrar el siguiente elemento valorado más grande, pero eso sería demasiado complejo.
Neil Chowdhury
@nmichaels ¿Hay alguna manera mejor de obtener todas las posiciones de valor máximo en una lista que la respuesta aceptada?
Shaik Moeed 05 de
18

La respuesta elegida (y la mayoría de las otras) requieren al menos dos pasadas por la lista.
Aquí hay una solución de un solo paso que podría ser una mejor opción para listas más largas.

Editado: para abordar las dos deficiencias señaladas por @John Machin. Para (2) intenté optimizar las pruebas basadas en la probabilidad estimada de ocurrencia de cada condición y las inferencias permitidas por los predecesores. Fue un poco complicado descubrir los valores de inicialización adecuados para max_valy max_indicesque funcionó para todos los casos posibles, especialmente si el máximo fue el primer valor en la lista, pero creo que ahora sí.

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
Martineau
fuente
44
(1) El manejo de la lista vacía necesita atención. Debe regresar []como se anuncia ("Lista de devolución"). El código debe ser simple if not seq: return []. (2) El esquema de prueba en bucle es subóptimo: en promedio en listas aleatorias, la condición val < maxvalserá la más común, pero el código anterior toma 2 pruebas en lugar de una.
John Machin
Un +1 al comentario de @John Machin por detectar la inconsistencia con la cadena de documentación y no permitirme publicar un código subóptimo. Para ser sincero, dado que una respuesta ya fue aceptada, perdí un poco de motivación para continuar trabajando en mi respuesta, ya que asumí que casi nadie más la miraría, y es mucho más larga que la de los demás.
Martineau
1
@martineau: las respuestas "aceptadas" no son necesariamente "aceptables". Generalmente leo todas las respuestas. Incluyendo su revisión. Lo que hace 3 pruebas ahora en el raro caso de en ==lugar de 2: su elifcondición siempre será verdadera.
John Machin
@ John Machin: Me inspiré mucho y lo revisé aún más. Ahora se han reducido las pruebas adicionales mínimas, además de algunos otros ajustes. Gracias por sus comentarios y críticas constructivas. elifCogí el siempre verdadero yo mismo, FWIW. ;-)
martineau
@John Machin: Hmmm, sus resultados de tiempo parecen contradecir los míos, por lo que eliminaré lo que dije en mi respuesta sobre el tiempo para poder ver qué está sucediendo más adelante. Gracias por el aviso. En realidad, creo que una prueba de tiempo "real" necesitaría usar valores de lista aleatorios.
Martineau
10

Se me ocurrió lo siguiente y funciona como puedes ver max, miny otros funcionan en listas como estas:

Por lo tanto, considere la siguiente lista de ejemplo para averiguar la posición del máximo en la lista a:

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

Usando el generador enumerate y haciendo un casting

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

En este punto, podemos extraer la posición de max con

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

Lo anterior nos dice que el máximo está en la posición 4 y su valor es 5.

Como puede ver, en el keyargumento, puede encontrar el máximo sobre cualquier objeto iterable definiendo un lambda apropiado.

Espero que contribuya.

PD: Como señaló @PaulOyster en un comentario. Con Python 3.xel miny maxpermitir una nueva palabra clave defaultque evite la excepción de aumento ValueErrorcuando el argumento es una lista vacía.max(enumerate(list), key=(lambda x:x[1]), default = -1)

jonaprieto
fuente
2
Esta es una mejor solución, ya que implica una sola pasada. Sin embargo, algunos comentarios: 1. no es necesario enumerar () la enumeración, 2. lambda será mejor entre paréntesis, 3. min () y max () ahora tienen un parámetro predeterminado (que se devuelve en una entrada vacía), por lo que puede usar (predeterminado = -1, por ejemplo) para evitar una excepción ValueError, y 4. cambie a max (), ya que esta era la pregunta original.
Paul Oyster
alrededor de 3 elementos, sí, simplemente funciona con Python 3.x. Mencionaré eso. Y arregló todo lo demás. ;)
jonaprieto
2
Esto solo encontraría la posición de uno de los elementos de valor máximo (el primero) cuando aparece más de una vez en la lista, por lo que no responde la pregunta que se hace.
Martineau
8

No puedo reproducir el rendimiento @ SilentGhost-beating citado por @martineau. Aquí está mi esfuerzo con las comparaciones:

=== 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

Resultados de una vieja laptop destacada que ejecuta Python 2.7 en 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
John Machin
fuente
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

Así es como lo hago habitualmente.

Arijit Laha
fuente
6

También puede usar el paquete numpy:

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

Esto devolverá una matriz numpy de todos los índices que contienen el valor máximo

si quieres convertir esto en una lista:

maximum_indices_list = maximum_indices.tolist()
usuario3569257
fuente
5
>>> max(enumerate([1,2,3,32,1,5,7,9]),key=lambda x: x[1])
>>> (3, 32)
Hari Roshan
fuente
Esto está mal. Intenta poner el número máximo en el medio de la lista.
goncalopp
1
Esto está mal. La pregunta dice "encontrar todas las posiciones del valor máximo".
Kapil
5

También se puede lograr una solución, que solo da la primera apariencia , usando numpy:

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

@shash respondió esto en otra parte

Una forma pitónica de encontrar el índice del elemento de lista máximo sería

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

¿Qué pasa uno ? Sin embargo, es más lento que la solución de @Silent_Ghost y, aún más, @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
serv-inc
fuente
2

Aquí está el valor máximo y los índices en los que aparece:

>>> 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]

Más tarde: para satisfacción 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]
hughdbrown
fuente
te das cuenta de lo ineficiente que es esto?
SilentGhost
1
Racionalizaciones: (1) "La optimización prematura es el ... etc." (2) Probablemente no importe. (3) Sigue siendo una buena solución. Tal vez lo recodifique para usar heapq, encontrar el máximo allí sería trivial.
hughdbrown
Si bien me encantaría ver su heapqsolución, dudo que funcione.
SilentGhost
2

Idea similar con una lista de comprensión pero sin enumerar

m = max(a)
[i for i in range(len(a)) if a[i] == m]
Salvador Dalí
fuente
No soy el votante negativo, pero tenga en cuenta que esto no se ve muy bien y no funcionará bien: iterar a través de los índices en lugar de a través de la lista es muy incómodo en Python, intenta evitar esto. Además, es ciertamente más lento que la solución con enumerate debido a la a[i]llamada.
yo '
1

Solo una línea:

idx = max(range(len(a)), key = lambda i: a[i])
divkakwani
fuente
Bien, pero no devuelve TODOS los índices, solo el primero.
iggy
1

Si desea obtener los índices de los nnúmeros más grandes en una lista llamada data, puede usar Pandas sort_values:

pd.Series(data).sort_values(ascending=False).index[0:n]
dannyg
fuente
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])

fuente
0

Este código no es tan sofisticado como las respuestas publicadas anteriormente, pero funcionará:

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

La lista en el código anterior contendría todas las posiciones del número máximo en la lista.

Sukrit Gupta
fuente
0

Puedes hacerlo de varias maneras.

La vieja forma convencional es,

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

Otra forma sin calcular la longitud de la lista y almacenar el valor máximo para cualquier 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)

¡Podemos hacerlo de manera pitónica e inteligente! Usando la comprensión de la lista solo en una línea,

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

Todos mis códigos están en Python 3.

Taohidul Islam
fuente