Come trovare tutte le posizioni del valore massimo in un elenco?


152

Ho un elenco:

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'elemento massimo è 55 (due elementi nelle posizioni 9 e 12)

Devo trovare su quali posizioni si trova il valore massimo. Per favore aiuto.

Risposte:


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

4
Bella risposta breve se non ti dispiace fare più passaggi nell'elenco - il che è probabile.
martineau,

Tranne il grande 0 per questo è 2n, l'elenco viene ripetuto attraverso 2x, una volta per determinare il massimo e un'altra volta per trovare le posizioni del massimo. Un ciclo for che tiene traccia del massimo corrente e della sua posizione potrebbe essere più efficiente per elenchi molto lunghi.
Radtek,

1
@radtek big O è solo n. i principali coefficienti sono ignorati nella grande O
michaelsnowden del

1
Teoricamente O (N) e O (2N) sono uguali, ma praticamente, O (N) avrà sicuramente un tempo di esecuzione più breve, specialmente quando N si avvicina all'infinito.
Radtek,

314
a.index(max(a))

ti dirà l'indice della prima istanza dell'elemento più grande dell'elenco a.


8
Questo ti porterà solo alla prima istanza e ha chiesto tutti gli indici in cui si trova il valore più grande. Dovresti fare un ciclo su quella sezione usando per ottenere l'elenco rimanente in ogni caso e gestire l'eccezione quando non viene più trovata.
Jaydel,

10
Ho detto che avrebbe dato solo il primo caso. Se li desideri tutti, la soluzione di SilentGhost è molto più carina e meno soggetta a errori.
nmichaels,

7
Almeno quando ci sono arrivato, la domanda chiede esplicitamente un elenco nel caso di massimi multipli ...
emmagras

2
Tecnicamente, potresti usarlo per ottenere la prima istanza dell'elemento con il valore più grande e quindi impostarlo su un numero negativo ridicolmente grande, e quindi trovare il successivo elemento con il valore più grande, ma sarebbe troppo complesso.
Neil Chowdhury,

@nmichaels Esiste un modo migliore per ottenere tutte le posizioni di valore massimo in un elenco oltre alla risposta accettata?
shaik moeed

18

La risposta scelta (e la maggior parte delle altre) richiede almeno due passaggi nell'elenco.
Ecco una soluzione a passaggio singolo che potrebbe essere una scelta migliore per elenchi più lunghi.

A cura: per affrontare le due carenze evidenziate da @John Machin. Per (2) ho tentato di ottimizzare i test in base alla probabilità stimata di occorrenza di ciascuna condizione e inferenze consentite dai predecessori. È stato un po 'complicato capire i valori di inizializzazione corretti per max_vale max_indicesche ha funzionato per tutti i possibili casi, soprattutto se il massimo è stato il primo valore nell'elenco - ma credo che ora lo sia.

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 gestione dell'elenco vuoto richiede attenzione. Dovrebbe restituire []come pubblicizzato ("Elenco di restituzione"). Il codice dovrebbe essere semplicemente if not seq: return []. (2) Lo schema di test in loop non è ottimale: in media in elenchi casuali, la condizione val < maxvalsarà la più comune ma il codice sopra esegue 2 test invece di uno.
John Machin,

+1 al commento di @John Machin per aver colto l'incoerenza con la documentazione e non avermi permesso di pubblicare un codice non ottimale. Ad essere sincero, poiché una risposta era già stata accettata, ho perso un po 'di motivazione per continuare a lavorare sulla mia risposta, dal momento che presumevo che quasi nessuno lo avrebbe nemmeno guardato, ed è molto più lungo di tutti gli altri.
martineau,

1
@martineau: le risposte "accettate" non sono necessariamente "accettabili". In genere leggo tutte le risposte. Inclusa la tua revisione. Il che fa 3 test ora nel raro caso ==invece di 2 - la tua elifcondizione sarà sempre vera.
John Machin

@ John Machin: mi sono davvero ispirato e l'ho rivisto ulteriormente. Ora sono disponibili ulteriori test minimi, oltre ad alcune altre modifiche. Grazie per i vostri commenti e critiche costruttive. Ho catturato il sempre vero elifme stesso, FWIW. ;-)
martineau,

@John Machin: Hmmm, i tuoi risultati di temporizzazione sembrano contraddire i miei, quindi rimuoverò ciò che ho detto nella mia risposta sui tempi in modo da poter esaminare cosa sta succedendo ulteriormente. Grazie per il testa a testa. In realtà penso che un test di tempistica "reale" dovrebbe usare valori di elenco casuali.
martineau

10

Ho pensato a quanto segue e funziona come puoi vedere max, mine altri funzionano su elenchi come questi:

Quindi, ti preghiamo di considerare il prossimo elenco di esempio per scoprire la posizione del massimo nell'elenco a:

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

Usare il generatore enumerate e fare un casting

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

A questo punto, possiamo estrarre la posizione di max con

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

Quanto sopra ci dice, il massimo è nella posizione 4 e il suo valore è 5.

Come vedi, keynell'argomento, puoi trovare il massimo su qualsiasi oggetto iterabile definendo un lambda appropriato.

Spero che contribuisca.

PD: Come ha notato @PaulOyster in un commento. Con Python 3.xl' mine maxconsentire una nuova parola chiave defaultche evita l'eccezione rilancio ValueErrorquando argomento è la lista vuota.max(enumerate(list), key=(lambda x:x[1]), default = -1)


2
Questa è una soluzione migliore, poiché comporta un singolo passaggio. Alcuni commenti, tuttavia: 1. non è necessario elencare () l'enumerazione, 2. lambda è meglio essere tra parentesi, 3. min () e max () ora hanno un parametro predefinito (che viene restituito su input vuoto), quindi può usare esso (default = -1, per esempio) per evitare un'eccezione ValueError, e 4. cambia in max (), poiché questa era la domanda originale.
Paul Oyster,

circa 3 elementi, sì, funziona solo con Python 3.x. Lo citerò. E riparato tutto il resto. ;)
jonaprieto,

2
Ciò troverebbe la posizione di uno degli elementi con il massimo valore (il primo) quando si verifica più di una volta nell'elenco, quindi non risponde alla domanda posta.
martineau,

8

Non riesco a riprodurre la performance di @ SilentGhost citata da @martineau. Ecco il mio sforzo con i confronti:

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

Risultati di un vecchio laptop mal funzionante con Python 2.7 su 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

È così che di solito lo faccio.


6

Puoi anche usare il pacchetto numpy:

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

Ciò restituirà una matrice numpy di tutti gli indici che contengono il valore massimo

se vuoi trasformarlo in un elenco:

maximum_indices_list = maximum_indices.tolist()

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

Questo è sbagliato. Prova a mettere il numero massimo al centro dell'elenco.
goncalopp,

1
Questo è sbagliato. La domanda dice "trova tutte le posizioni del valore massimo".
Kapil

5

Anche una soluzione, che dà solo il primo aspetto , può essere raggiunta usando numpy:

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

3

@shash ha risposto a questo altrove

Un modo Pythonic per trovare l'indice dell'elemento elenco massimo sarebbe

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

Che fa passare . Tuttavia, è più lento della soluzione di @Silent_Ghost e, ancora di più, @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

Ecco il valore massimo e gli indici in cui appare:

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

Più tardi: per la soddisfazione di @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]

ti rendi conto di quanto sia inefficiente questo?
SilentGhost,

1
Razionalizzazioni: (1) "L'ottimizzazione prematura è il ... ecc." (2) Probabilmente non importa. (3) È ancora una buona soluzione. Forse lo ricodificherò per usarlo heapq: trovare il massimo sarebbe banale.
hughdbrown,

mentre mi piacerebbe vedere la tua heapqsoluzione, dubito che funzionerebbe.
SilentGhost,

2

Idea simile con una comprensione dell'elenco ma senza enumerazione

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

Non sono il downvoter, ma nota che questo non sembra davvero bello e non funzionerà bene: iterare attraverso gli indici invece che attraverso la lista è molto imbarazzante in Python, cerchi di evitarlo. Inoltre, è certamente più lento della soluzione con enumerato a causa della a[i]chiamata.
yo'

1

Solo una riga:

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

Bello, ma non restituisce TUTTI gli indici, solo il primo.
iggy,

1

Se vuoi ottenere gli indici dei nnumeri più grandi in un elenco chiamato data, puoi usare Panda 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

Questo codice non è sofisticato come le risposte postate in precedenza ma funzionerà:

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

il ilist nel codice sopra conterrebbe tutte le posizioni del numero massimo nell'elenco.


0

Puoi farlo in vari modi.

Il vecchio modo convenzionale è,

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

Un altro modo senza calcolare la lunghezza dell'elenco e memorizzare il valore massimo in qualsiasi variabile,

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)

Possiamo farlo in modo Pythonic e intelligente! Utilizzando la comprensione dell'elenco in una sola riga,

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

Tutti i miei codici sono in Python 3.

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.