Jak znaleźć wszystkie pozycje o maksymalnej wartości na liście?

152

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

max elementu to 55 (dwa elementy na pozycjach 9 i 12)

Muszę dowiedzieć się, na których pozycjach znajduje się maksymalna wartość. Proszę pomóż.

Pion
źródło

Odpowiedzi:

210
>>> m = max(a)
>>> [i for i, j in enumerate(a) if j == m]
[9, 12]
SilentGhost
źródło
4
Ładna, krótka odpowiedź, jeśli nie masz nic przeciwko wielokrotnemu przejściu przez listę - co jest prawdopodobne.
martineau
Z wyjątkiem dużego 0 dla tego 2n, lista jest iterowana przez 2x, raz w celu określenia max, a innym razem w celu znalezienia pozycji max. Pętla for, która śledzi bieżące maksimum i jego pozycję, może być bardziej wydajna w przypadku naprawdę długich list.
radtek
1
@radtek big O to po prostu n. wiodące współczynniki są ignorowane w wielkim O
michaelsnowden
1
Teoretycznie O (N) i O (2N) są takie same, ale w praktyce O (N) z pewnością będzie miało krótszy czas działania, zwłaszcza gdy N zbliża się do nieskończoności.
radtek
314
a.index(max(a))

poda indeks pierwszej instancji elementu listy o największej wartości a.

nmichaels
źródło
8
To jednak da ci tylko pierwszą instancję i poprosił o wszystkie indeksy, w których znaleziono największą wartość. Będziesz musiał zapętlić to za pomocą plasterka, aby uzyskać pozostałą listę w każdym przypadku i obsłużyć wyjątek, gdy nie zostanie już znaleziony.
jaydel
10
Wspomniałem, że dałoby to tylko pierwszą instancję. Jeśli chcesz je wszystkie, rozwiązanie SilentGhost jest znacznie ładniejsze i mniej podatne na błędy.
nmichaels
7
Przynajmniej jak do tego doszedłem, pytanie wprost prosi o listę w przypadku wielokrotnych maksimów ...
emmagras
2
Technicznie rzecz biorąc, możesz użyć tego, aby uzyskać pierwsze wystąpienie elementu o największej wartości, a następnie ustawić go na śmiesznie dużą liczbę ujemną, a następnie znaleźć następny element o największej wartości, ale byłoby to zbyt skomplikowane.
Neil Chowdhury,
@nmichaels Czy istnieje najlepszy sposób na uzyskanie wszystkich pozycji o maksymalnej wartości na liście niż zaakceptowana odpowiedź?
shaik moeed
18

Wybrana odpowiedź (i większość innych) wymaga co najmniej dwóch przejść przez listę.
Oto rozwiązanie jednoprzebiegowe, które może być lepszym wyborem w przypadku dłuższych list.

Edytowano: aby usunąć dwa niedociągnięcia wskazane przez @John Machin. Dla (2) podjąłem próbę optymalizacji testów na podstawie oszacowanego prawdopodobieństwa wystąpienia każdego warunku i wniosków uzyskanych od poprzedników. To było trochę trudne zastanawianie się odpowiednie wartości dla inicjowania max_vali max_indicesktóry pracował dla wszystkich możliwych przypadkach, zwłaszcza jeśli max okazał się być pierwsza wartość na liście - ale uważam, że teraz robi.

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
źródło
4
(1) Obsługa pustej listy wymaga uwagi. Powinien powrócić []zgodnie z reklamą („Lista zwrotów”). Kod powinien być prosty if not seq: return []. (2) Schemat testowania w pętli jest nieoptymalny: średnio na listach losowych warunek val < maxvalbędzie najczęściej, ale powyższy kod wymaga 2 testów zamiast jednego.
John Machin
+1 do komentarza @John Machin za wyłapanie niespójności z dokumentacją i nie pozwolenie mi na publikowanie nieoptymalnego kodu. Prawdę mówiąc, skoro odpowiedź została już zaakceptowana, straciłem trochę motywacji do dalszej pracy nad odpowiedzią, ponieważ zakładałem, że mało kto w ogóle na nią spojrzy - a to o wiele dłużej niż wszyscy inni.
martineau
1
@martineau: „zaakceptowane” odpowiedzi niekoniecznie są „akceptowalne”. Generalnie czytam wszystkie odpowiedzi. W tym Twoja wersja. Który przeprowadza teraz 3 testy w rzadkim przypadku ==zamiast 2 - twój elifstan zawsze będzie prawdziwy.
John Machin,
@John Machin: Naprawdę zainspirowałem się i poprawiłem to jeszcze bardziej. Teraz nie ma minimalnych dodatkowych testów, plus kilka innych poprawek. Dziękuję za uwagi i konstruktywną krytykę. Sam złapałem zawsze Prawdę elif, FWIW. ;-)
martineau
@John Machin: Hmmm, wyniki twojego pomiaru czasu wydają się być sprzeczne z moimi, więc usunę to, co powiedziałem w mojej odpowiedzi na temat synchronizacji, aby móc sprawdzić, co się dzieje dalej. Dzięki za ostrzeżenie. Właściwie myślę, że „prawdziwy” test czasu wymagałby użycia losowych wartości z listy.
martineau
10

Wymyśliłem następujące i działa tak, jak widać max, mina inne funkcje na listach takich jak te:

Tak więc, rozważ następną przykładową listę, aby znaleźć pozycję maksimum na liście a:

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

Korzystanie z generatora enumerate i wykonanie odlewu

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

W tym momencie możemy wyodrębnić pozycję max za pomocą

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

Powyższe mówi nam, że maksimum jest na pozycji 4, a jego wartość to 5.

Jak widzisz, w keyargumencie możesz znaleźć maksimum dla dowolnego iterowalnego obiektu, definiując odpowiednią lambdę.

Mam nadzieję, że to przyczyni się.

PD: Jak zauważył @PaulOyster w komentarzu. Z i umożliwiają nowe słowo kluczowe , które unikają wyjątek podbicia gdy argument jest pusta lista.Python 3.xminmaxdefaultValueErrormax(enumerate(list), key=(lambda x:x[1]), default = -1)

jonaprieto
źródło
2
To lepsze rozwiązanie, ponieważ wymaga jednego przejścia. Kilka komentarzy: 1. nie ma potrzeby wyliczania () wyliczenia, 2. lambda lepiej być w nawiasach, 3. min () i max () mają teraz domyślny parametr (który jest zwracany przy pustym wejściu), więc można użyć it (domyślnie = -1, na przykład), aby uniknąć wyjątku ValueError, i 4. zmień na max (), ponieważ było to pierwotne pytanie.
Paul Oyster,
około 3 pozycji, tak, działa tylko z Pythonem 3.x. Wspomnę o tym. I naprawiłem wszystko inne. ;)
jonaprieto
2
Pozwoli to znaleźć pozycję jednego z elementów o maksymalnej wartości (pierwszego) tylko wtedy, gdy wystąpi on więcej niż raz na liście - więc nie odpowiada na zadane pytanie.
martineau
8

Nie mogę odtworzyć wydajności @ SilentGhost cytowanej przez @martineau. Oto mój wysiłek z porównaniami:

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

Wyniki ze zniszczonego starego laptopa z systemem Python 2.7 w systemie 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
źródło
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

Tak zwykle robię.

Arijit Laha
źródło
6

Możesz także użyć pakietu numpy:

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

To zwróci tablicę numpy wszystkich indeksów, które zawierają wartość maksymalną

jeśli chcesz zamienić to na listę:

maximum_indices_list = maximum_indices.tolist()
user3569257
źródło
5
>>> max(enumerate([1,2,3,32,1,5,7,9]),key=lambda x: x[1])
>>> (3, 32)
Hari Roshan
źródło
To jest źle. Spróbuj umieścić maksymalną liczbę na środku listy.
goncalopp
1
To jest źle. Pytanie brzmi: „znajdź wszystkie pozycje maksymalnej wartości”.
Kapil
5

Również rozwiązanie, które daje tylko pierwszy wygląd , można uzyskać stosując numpy:

>>> import numpy as np
>>> a_np = np.array(a)
>>> np.argmax(a_np)
9
Zielony
źródło
3

@shash odpowiedział na to gdzie indziej

Pythonowym sposobem na znalezienie indeksu maksymalnego elementu listy byłby

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

Który przechodzi . Jednak jest wolniejszy niż rozwiązanie @Silent_Ghost, a tym bardziej @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
źródło
2

Oto maksymalna wartość i indeksy, w których się pojawia:

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

Później: dla satysfakcji @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
źródło
zdajesz sobie sprawę, jakie to nieefektywne?
SilentGhost
1
Racjonalizacje: (1) „Przedwczesna optymalizacja to ... itd.” (2) To prawdopodobnie nie ma znaczenia. (3) To wciąż dobre rozwiązanie. Może przekoduję go na użycie heapq- znalezienie tam maksimum byłoby trywialne.
hughdbrown
chociaż chciałbym zobaczyć twoje heapqrozwiązanie, wątpię, żeby zadziałało.
SilentGhost
2

Podobny pomysł ze zrozumieniem listy, ale bez wyliczania

m = max(a)
[i for i in range(len(a)) if a[i] == m]
Salvador Dali
źródło
Nie jestem zwolennikiem krytyki, ale zauważ, że nie wygląda to zbyt dobrze i nie będzie działać dobrze: iterowanie indeksów zamiast listy jest bardzo niewygodne w Pythonie, starasz się tego uniknąć. Jak dobrze, z pewnością jest wolniejszy niż rozwiązanie z wyliczaniem z powodu a[i]połączenia.
yo”
1

Tylko jedna linia:

idx = max(range(len(a)), key = lambda i: a[i])
divkakwani
źródło
Ładnie, ale nie zwraca WSZYSTKICH indeksów, tylko pierwszy.
iggy
1

Jeśli chcesz uzyskać indeksy największych nliczb na wywołanej liście data, możesz użyć Pand sort_values:

pd.Series(data).sort_values(ascending=False).index[0:n]
dannyg
źródło
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])

źródło
0

Ten kod nie jest tak wyrafinowany jak odpowiedzi zamieszczone wcześniej, ale zadziała:

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 w powyższym kodzie zawierałby wszystkie pozycje maksymalnej liczby na liście.

Sukrit Gupta
źródło
0

Możesz to zrobić na różne sposoby.

Stary konwencjonalny sposób to

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

Innym sposobem bez obliczania długości listy i przechowywania maksymalnej wartości dowolnej zmiennej,

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)

Możemy to zrobić w Pythonic i sprytnie! Korzystanie ze zrozumienia list tylko w jednej linii,

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

Wszystkie moje kody są w Pythonie 3.

Taohidul Islam
źródło