211

If I want the maximum value in a list, I can just write max(List), but what if I also need the index of the maximum value?

I can write something like this:

maximum=0
for i,value in enumerate(List):
    if value>maximum:
        maximum=value
        index=i

But it looks tedious to me.

And if I write:

List.index(max(List))

Then it will iterate the list twice.

Is there a better way?

Nameless One
  • 1,615
  • 2
  • 23
  • 39
Sunny88
  • 2,860
  • 3
  • 24
  • 25
  • What do you mean by "it will pass list two times"? List.index(max(List)) works for me. – mwcz May 31 '11 at 21:03
  • 18
    @mwc: It will iterate the list once to determine the maximum value, then iterate it a second time to find the index of that value. –  May 31 '11 at 21:04
  • 10
    Wouldn't list.index() be problematic if there are duplicated max values? – Logan Yang Feb 05 '14 at 00:14
  • @LoganYang yes there could be two items with same value. – Flo Jun 28 '17 at 11:16
  • If the order is not important, you could do something like List.sort()[-1] – Flo Jun 28 '17 at 11:17

11 Answers11

378

I think the accepted answer is great, but why don't you do it explicitly? I feel more people would understand your code, and that is in agreement with PEP 8:

max_value = max(my_list)
max_index = my_list.index(max_value)

This method is also about three times faster than the accepted answer:

import random
from datetime import datetime
import operator

def explicit(l):
    max_val = max(l)
    max_idx = l.index(max_val)
    return max_idx, max_val

def implicit(l):
    max_idx, max_val = max(enumerate(l), key=operator.itemgetter(1))
    return max_idx, max_val

if __name__ == "__main__":
    from timeit import Timer
    t = Timer("explicit(l)", "from __main__ import explicit, implicit; "
          "import random; import operator;"
          "l = [random.random() for _ in xrange(100)]")
    print "Explicit: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)

    t = Timer("implicit(l)", "from __main__ import explicit, implicit; "
          "import random; import operator;"
          "l = [random.random() for _ in xrange(100)]")
    print "Implicit: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)

Results as they run in my computer:

Explicit: 8.07 usec/pass
Implicit: 22.86 usec/pass

Other set:

Explicit: 6.80 usec/pass
Implicit: 19.01 usec/pass
Escualo
  • 40,844
  • 23
  • 87
  • 135
  • 4
    Didn't expect it to be faster. It is faster even when I replace l with "l = [random.random() for _ in xrange(10000000)]+[2]", which guarantees that last element is the largest. – Sunny88 Jun 01 '11 at 04:39
  • 21
    @Sunny88: For a simple list of numbers, the simple approach is faster. If you are after performance in this case, I'd suggest to use `numpy.argmax()`, which is another 30 times faster on my machine. If the list contains more complicated objects than mere numbers, the approach in my answer can become faster. Wnother advantage of that approach is that it can be used for arbitrary iterators, not only for lists. – Sven Marnach Jun 01 '11 at 15:51
  • @Sven-Marnach Would numpy be faster, if I had to convert my list to a numpy array first? Would it be faster for the simple example [0,1,0]? – tommy.carstensen Sep 08 '13 at 23:33
  • 1
    @Sven-Marnach I just checked. numpy.argmax is by far the slowest method, and it gives the wrong answer, if the array contains strings instead of floats or integers. – tommy.carstensen Sep 08 '13 at 23:47
  • 10
    Wouldn't list.index() be problematic if there are duplicated max values? – Logan Yang Feb 05 '14 at 00:15
  • @LoganYang I don't think so, when there are multiple max values, `max()` returns the first one only (this is guaranteed for Python 3), `list.index()` also returns the first one – Chris_Rands Jan 30 '18 at 15:10
213

There are many options, for example:

import operator
index, value = max(enumerate(my_list), key=operator.itemgetter(1))
wordsforthewise
  • 13,746
  • 5
  • 87
  • 117
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 3
    Ah, I have seen this in other places, but I thought that it will return just one value, not a tuple. – Sunny88 May 31 '11 at 21:09
  • 1
    @Sunny88: The `key` function is only used to decide which element is maximal. The elements are not changed. – Sven Marnach May 31 '11 at 21:12
  • 10
    @SvenMarnach Why not `key=lambda e: e[1]` instead and thereby avoid the import? – lifebalance Aug 07 '17 at 06:19
  • 10
    @lifebalance Using `itemgetter()` is faster, and avoiding an import isn't a goal worth pursuing. Avoiding external dependencies can be worthwhile in some cases, but an import from the standard library is a non-issue. – Sven Marnach Aug 07 '17 at 11:14
22

This answer is 33 times faster than @Escualo assuming that the list is very large, and assuming that it's already an np.array(). I had to turn down the number of test runs because the test is looking at 10000000 elements not just 100.

import random
from datetime import datetime
import operator
import numpy as np

def explicit(l):
    max_val = max(l)
    max_idx = l.index(max_val)
    return max_idx, max_val

def implicit(l):
    max_idx, max_val = max(enumerate(l), key=operator.itemgetter(1))
    return max_idx, max_val

def npmax(l):
    max_idx = np.argmax(l)
    max_val = l[max_idx]
    return (max_idx, max_val)

if __name__ == "__main__":
    from timeit import Timer

t = Timer("npmax(l)", "from __main__ import explicit, implicit, npmax; "
      "import random; import operator; import numpy as np;"
      "l = np.array([random.random() for _ in xrange(10000000)])")
print "Npmax: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

t = Timer("explicit(l)", "from __main__ import explicit, implicit; "
      "import random; import operator;"
      "l = [random.random() for _ in xrange(10000000)]")
print "Explicit: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

t = Timer("implicit(l)", "from __main__ import explicit, implicit; "
      "import random; import operator;"
      "l = [random.random() for _ in xrange(10000000)]")
print "Implicit: %.2f msec/pass" % (1000  * t.timeit(number=10)/10 )

Results on my computer:

Npmax: 8.78 msec/pass
Explicit: 290.01 msec/pass
Implicit: 790.27 msec/pass
benathon
  • 7,455
  • 2
  • 41
  • 70
  • Just to clarify: the speedup is just due to the numpy C implementation versus pure python? Or there's a way to get any improvement to @Escualo's answer using pure python? – max Dec 15 '16 at 01:18
  • If one would like to use python 3.6 one can do something like: "l = np.array([random.random() for _ in range(10000000)])") print (f"Npmax: {(1000 * t.timeit(number=10)/10 ):5.2f} msec/pass ") – Piotr Siejda Dec 07 '17 at 11:11
  • This was on 2.7 – benathon Dec 07 '17 at 11:11
  • 3
    Well, the speed of `numpy.argmax` looks amazing until you let it process a *standard* python list. Then the speed lies between explicit and implicit version. I guess `np.array` does not just create a list but it saves some extra info in it - like for example min and max values (just a hypothesis). – Miroslaw Opoka Mar 28 '19 at 11:58
  • @MiroslawOpoka I have tried to briefly check the source code and did not find any such "pre computed aggregates" (though I'm not sure at all since the code is not so simple, and many more general storage systems such as databases/storage formats do indeed record such min/max aggregates). Just the memory layout of numpy, plus the C compilation could account for a drastic speedup, though. – Joseph Stack Feb 11 '21 at 09:23
21

With Python's built-in library, it's pretty easy:

a = [2, 9, -10, 5, 18, 9] 
max(xrange(len(a)), key = lambda x: a[x])

This tells max to find the largest number in the list [0, 1, 2, ..., len(a)], using the custom function lambda x: a[x], which says that 0 is actually 2, 1 is actually 9, etc.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Sunil Kapil
  • 1,020
  • 13
  • 12
  • In Python 3, there is no xrange , if you want to write code that will run for both Python 2 and Python 3, you should use range(). – Chunde Huang Apr 25 '19 at 18:26
18

I would suggest a very simple way:

import numpy as np
l = [10, 22, 8, 8, 11]
print(np.argmax(l))
print(np.argmin(l))

Hope it helps.

CKE
  • 1,533
  • 19
  • 18
  • 29
Igor Manzhos
  • 235
  • 2
  • 5
12

This converts my_list to a list of tuples (v,i) where v is every item of my list and i is the correspondent index, then it gets the tuple with the maximun value and with its associated index too:

max([(v,i) for i,v in enumerate(my_list)])
ClimateUnboxed
  • 7,106
  • 3
  • 41
  • 86
Luis Sobrecueva
  • 680
  • 1
  • 6
  • 13
  • This is better because you can adapt it to use with something other than tuple. – wieczorek1990 Aug 11 '16 at 08:16
  • How exactly does this work? Can you break down the process? – clabe45 Oct 25 '17 at 00:14
  • Hi @clabe45, it converts my_list in a list of tuples (v,i) where v is every item of my list and i is the correspondent index, then it gets the tuple with the maximun value (and with its associated index too) – Luis Sobrecueva Oct 26 '17 at 07:57
  • 5
    Thanks, can you post that in the answer possibly? And how does `max` know to just take the first item of every tuple (`v`) into account when calculating the maximum value? – clabe45 Oct 26 '17 at 17:43
  • 1
    @clabe45 May be this reply comes too late, but for others (like me) who came across this thread now, here: https://stackoverflow.com/questions/18296755/python-max-function-using-key-and-lambda-expression is an explanation. Not this line: "By default max will will compare the items by the first index, if the first index is same then it'd compare the second index." So I gave it a try with list: l=[1,1,1] and then max([(v,i) for i,v in enumerate(l)]) and it gives me not the first 1 but the last one: (1,2) as result. I hope it explains :) – Anupam Jain Aug 08 '18 at 10:17
4
max([(value,index) for index,value in enumerate(your_list)]) #if maximum value is present more than once in your list then this will return index of the last occurrence

If maximum value in present more than once and you want to get all indices,

max_value = max(your_list)
maxIndexList = [index for index,value in enumerate(your_list) if value==max(your_list)]
Taohidul Islam
  • 5,246
  • 3
  • 26
  • 39
  • 1
    Yup. I almost posted an answer, but then I saw you had the same solution already with the same logic in your list comprehension one-liner. – WalyKu Feb 16 '18 at 12:56
2

Maybe you need a sorted list anyway?

Try this:

your_list = [13, 352, 2553, 0.5, 89, 0.4]
sorted_list = sorted(your_list)
index_of_higher_value = your_list.index(sorted_list[-1])
Mattias
  • 21
  • 4
  • 1. Sorting has higher time complexity. 2. `sorted_list` hasn't indexes but values, so it would not work. –  Jan 09 '18 at 22:57
0

I made some big lists. One is a list and one is a numpy array.

import numpy as np
import random
arrayv=np.random.randint(0,10,(100000000,1))
listv=[]
for i in range(0,100000000):
    listv.append(random.randint(0,9))

Using jupyter notebook's %%time function I can compare the speed of various things.

2 seconds:

%%time
listv.index(max(listv))

54.6 seconds:

%%time
listv.index(max(arrayv))

6.71 seconds:

%%time
np.argmax(listv)

103 ms:

%%time
np.argmax(arrayv)

numpy's arrays are crazy fast.

AndrewGraham
  • 310
  • 1
  • 8
0

List comprehension method:

Let's say you have some list List = [5,2,3,8]

Then [i for i in range(len(List)) if List[i] == max(List)] would be a pythonic list comprehension method to find the values "i" where List[i] == max(List).

It is easily scalable for arrays that are lists of lists, simply by doing a for loop.

For instance, with an arbitrary list of lists "array" and initalizing "index" as an empty list.

array = [[5, 0, 1, 1], 
[1, 0, 1, 5], 
[0, 1, 6, 0], 
[0, 4, 3, 0], 
[5, 2, 0, 0], 
[5, 0, 1, 1], 
[0, 6, 0, 1], 
[0, 1, 0, 6]]
index = []

for List in array:
    index.append([i for i in range(len(List)) if List[i] == max(List)])
index

Output: [[0], [3], [2], [1], [0], [0], [1], [3]]

ebehr
  • 167
  • 6
-1

Here is a complete solution to your question using Python's built-in functions:

# Create the List
numbers = input("Enter the elements of the list. Separate each value with a comma. Do not put a comma at the end.\n").split(",") 

# Convert the elements in the list (treated as strings) to integers
numberL = [int(element) for element in numbers] 

# Loop through the list with a for-loop

for elements in numberL:
    maxEle = max(numberL)
    indexMax = numberL.index(maxEle)

print(maxEle)
print(indexMax)
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128