561

I'm using Python's max and min functions on lists for a minimax algorithm, and I need the index of the value returned by max() or min(). In other words, I need to know which move produced the max (at a first player's turn) or min (second player) value.

for i in range(9):
    new_board = current_board.new_board_with_move([i / 3, i % 3], player)

    if new_board:
        temp = min_max(new_board, depth + 1, not is_min_level)  
        values.append(temp)

if is_min_level:
    return min(values)
else:
    return max(values)

I need to be able to return the actual index of the min or max value, not just the value.

Boris
  • 7,044
  • 6
  • 62
  • 63
Kevin Griffin
  • 11,989
  • 7
  • 26
  • 22

24 Answers24

554

Say that you have a list values = [3,6,1,5], and need the index of the smallest element, i.e. index_min = 2 in this case.

Avoid the solution with itemgetter() presented in the other answers, and use instead

index_min = min(range(len(values)), key=values.__getitem__)

because it doesn't require to import operator nor to use enumerate, and it is always faster(benchmark below) than a solution using itemgetter().

If you are dealing with numpy arrays or can afford numpy as a dependency, consider also using

import numpy as np
index_min = np.argmin(values)

This will be faster than the first solution even if you apply it to a pure Python list if:

  • it is larger than a few elements (about 2**4 elements on my machine)
  • you can afford the memory copy from a pure list to a numpy array

as this benchmark points out: enter image description here

I have run the benchmark on my machine with python 2.7 for the two solutions above (blue: pure python, first solution) (red, numpy solution) and for the standard solution based on itemgetter() (black, reference solution). The same benchmark with python 3.5 showed that the methods compare exactly the same of the python 2.7 case presented above

gg349
  • 18,958
  • 3
  • 47
  • 58
  • 1
    A very strong +1. I love the benchmarking of the proposed solutions and the rules of thumb you have summarized. As I suggested in another answer below, could you provide (or link to) your test code so others might reproduce your results? Machines and libraries change over time, and it would allow comparing to other solutions. – Rakurai Jan 14 '19 at 17:23
  • np.argmin does not work for floats. only the first suggestion works on ints and floats. – jimh Feb 19 '20 at 18:51
  • I think you are mistaken, try `import numpy as np; x = [2.3, -1.4]; np.argmin(x)`. You will see that `argmin` works on floats too – gg349 Feb 24 '20 at 15:01
506
if is_min_level:
    return values.index(min(values))
else:
    return values.index(max(values))
Boris
  • 7,044
  • 6
  • 62
  • 63
too much php
  • 81,874
  • 33
  • 123
  • 133
  • 48
    @KevinGriffin, Note that this gets you only one of possibly several occurrences of the minimum/maximum. This may not be what you want, for example if it's possible to increase your gain the same two ways, but one of them hurts the other player more. I do not know if this is a case you need to consider. – Mike Graham Mar 19 '10 at 00:54
  • 92
    @Kashyap It's actually O(N), not O(N^2). In the min case, first min(values) is evaluated, which is O(N), then values.index() is called, which is also O(N). O(N) + O(N) = O(N). The argument to index is only evaluated once. It's equivalent to: `tmp = min(values); return values.index(tmp)` – Tom Karzes Oct 21 '15 at 11:17
  • @too much php what to do when there is repetition of elements.? – Shashi Tunga Jan 27 '18 at 19:39
  • @ShashiTunga [list].index() returns only the first occurence of something, it is not guaranteed that it is exclusive, the minimum value might not be unique within the list – Scott Anderson Jan 16 '20 at 18:50
  • you can inline the `if` as well: `return values.index(min(values) if is_min_value else max(values))` – Boris Feb 19 '21 at 14:06
  • @TomKarzes This is true, however it is still (potentially) twice as slow as it needs to be. If the minimum of a list is calculated by doing a linear scan, the index could be calculated in the same way. Ideally, the list should only be scanned once. – Recessive May 02 '21 at 02:58
352

You can find the min/max index and value at the same time if you enumerate the items in the list, but perform min/max on the original values of the list. Like so:

import operator
min_index, min_value = min(enumerate(values), key=operator.itemgetter(1))
max_index, max_value = max(enumerate(values), key=operator.itemgetter(1))

This way the list will only be traversed once for min (or max).

jamylak
  • 111,593
  • 23
  • 218
  • 220
Matt Anderson
  • 17,538
  • 11
  • 38
  • 55
129

If you want to find the index of max within a list of numbers (which seems your case), then I suggest you use numpy:

import numpy as np
ind = np.argmax(mylist)
dr.haz
  • 1,337
  • 1
  • 9
  • 5
  • 2
    In case of multiple occurrences of the maximum values, the indices corresponding to the first occurrence are returned. – Cohensius Nov 06 '18 at 15:11
43

Possibly a simpler solution would be to turn the array of values into an array of value,index-pairs, and take the max/min of that. This would give the largest/smallest index that has the max/min (i.e. pairs are compared by first comparing the first element, and then comparing the second element if the first ones are the same). Note that it's not necessary to actually create the array, because min/max allow generators as input.

values = [3,4,5]
(m,i) = max((v,i) for i,v in enumerate(values))
print (m,i) #(5, 2)
Ant6n
  • 1,628
  • 1
  • 16
  • 24
32
list=[1.1412, 4.3453, 5.8709, 0.1314]
list.index(min(list))

Will give you first index of minimum.

Andy
  • 421
  • 4
  • 3
22

I think the best thing to do is convert the list to a numpy array and use this function :

a = np.array(list)
idx = np.argmax(a)
Akshaya Natarajan
  • 1,427
  • 12
  • 14
14

I was also interested in this and compared some of the suggested solutions using perfplot (a pet project of mine).

Turns out that numpy's argmin,

numpy.argmin(x)

is the fastest method for large enough lists, even with the implicit conversion from the input list to a numpy.array.

enter image description here


Code for generating the plot:

import numpy
import operator
import perfplot


def min_enumerate(a):
    return min(enumerate(a), key=lambda x: x[1])[0]


def min_enumerate_itemgetter(a):
    min_index, min_value = min(enumerate(a), key=operator.itemgetter(1))
    return min_index


def getitem(a):
    return min(range(len(a)), key=a.__getitem__)


def np_argmin(a):
    return numpy.argmin(a)


perfplot.show(
    setup=lambda n: numpy.random.rand(n).tolist(),
    kernels=[
        min_enumerate,
        min_enumerate_itemgetter,
        getitem,
        np_argmin,
        ],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    )
Nico Schlömer
  • 37,093
  • 21
  • 139
  • 189
  • Notice that the same conclusion is posted already above in my answer, more than 2 years ago, with more information on when and why argmin can be used or not. Consider deleting the answer, which is also not giving merit to what is already been proposed on this same page. Consider also reviewing your other answers on SO for similar behavior: you appear to not cite the actual answer providing the best solution in your performance analyses. This is rather bad, especially for somebody with >10K rep that has been around long enough to know better. – gg349 Jan 03 '19 at 16:22
  • @gg349, very good points, but he does provide the source code for generating the results, making this easily reproducible and adaptable to comparing other solutions. I agree that he might consider removing this answer as a duplicate, but perhaps you could add value to your answer by including or linking to the code you used? – Rakurai Jan 14 '19 at 17:18
10

After you get the maximum values, try this:

max_val = max(list)
index_max = list.index(max_val)

Much simpler than a lot of options.

alpha_989
  • 3,797
  • 28
  • 42
8

Use a numpy array and the argmax() function

 a=np.array([1,2,3])
 b=np.argmax(a)
 print(b) #2
John Misquita
  • 119
  • 1
  • 4
8

I think the answer above solves your problem but I thought I'd share a method that gives you the minimum and all the indices the minimum appears in.

minval = min(mylist)
ind = [i for i, v in enumerate(mylist) if v == minval]

This passes the list twice but is still quite fast. It is however slightly slower than finding the index of the first encounter of the minimum. So if you need just one of the minima, use Matt Anderson's solution, if you need them all, use this.

Community
  • 1
  • 1
  • 2
    I like this because it uses base Python, and I find list comprehension easier to understand than itemgetter, lambda etc.(and flexible enough to solve a variety of tasks, such as this ....) – James Sep 16 '18 at 11:02
  • 1
    raw. I prefer this. – Dev_Man Dec 05 '18 at 18:45
  • 1
    I really appreciate this answer as it deals with multiple occurences and most of the other answers deal with just one occurence, which is unusable for me. +1 – Robvh Dec 11 '20 at 10:18
  • There's elegance in simplicity. This answer is easy to understand for beginners while providing a useful output. – IniMzungu Feb 01 '21 at 18:02
6

Use numpy module's function numpy.where

import numpy as n
x = n.array((3,3,4,7,4,56,65,1))

For index of minimum value:

idx = n.where(x==x.min())[0]

For index of maximum value:

idx = n.where(x==x.max())[0]

In fact, this function is much more powerful. You can pose all kinds of boolean operations For index of value between 3 and 60:

idx = n.where((x>3)&(x<60))[0]
idx
array([2, 3, 4, 5])
x[idx]
array([ 4,  7,  4, 56])
Ishan Tomar
  • 1,252
  • 1
  • 14
  • 19
  • index in python starts at 0. index returned shall be 6 (for 65), while your code returns 7 (OP's question was "Getting the index ...") – tagoma Aug 25 '16 at 04:02
  • In the command, I have queried for index of minimum value (here: 1) whose index IS 7. 65 is the maximum value of elements in the array. If you type: n.where(x==x.max())[0] you will get index of max. value which is 65 here. Its index will come out to be 6 – Ishan Tomar Aug 25 '16 at 09:15
  • use of numpy: probably prohibited in this application. But if you are going to use numpy, you're much better of just using `argmin()` instead of what you did here. – RBF06 Apr 09 '18 at 21:47
  • Thanks @RBF06 I will check it out. – Ishan Tomar Jul 10 '18 at 11:28
5

This is simply possible using the built-in enumerate() and max() function and the optional key argument of the max() function and a simple lambda expression:

theList = [1, 5, 10]
maxIndex, maxValue = max(enumerate(theList), key=lambda v: v[1])
# => (2, 10)

In the docs for max() it says that the key argument expects a function like in the list.sort() function. Also see the Sorting How To.

It works the same for min(). Btw it returns the first max/min value.

Simon Hänisch
  • 3,634
  • 1
  • 27
  • 35
5

Say you have a list such as:

a = [9,8,7]

The following two methods are pretty compact ways to get a tuple with the minimum element and its index. Both take a similar time to process. I better like the zip method, but that is my taste.

zip method

element, index = min(list(zip(a, range(len(a)))))

min(list(zip(a, range(len(a)))))
(7, 2)

timeit min(list(zip(a, range(len(a)))))
1.36 µs ± 107 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

enumerate method

index, element = min(list(enumerate(a)), key=lambda x:x[1])

min(list(enumerate(a)), key=lambda x:x[1])
(2, 7)

timeit min(list(enumerate(a)), key=lambda x:x[1])
1.45 µs ± 78.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Pablo MPA
  • 51
  • 1
  • 2
4

Why bother to add indices first and then reverse them? Enumerate() function is just a special case of zip() function usage. Let's use it in appropiate way:

my_indexed_list = zip(my_list, range(len(my_list)))

min_value, min_index = min(my_indexed_list)
max_value, max_index = max(my_indexed_list)
sophist
  • 41
  • 2
4

As long as you know how to use lambda and the "key" argument, a simple solution is:

max_index = max( range( len(my_list) ), key = lambda index : my_list[ index ] )
Veiga
  • 212
  • 1
  • 9
  • Very clean! And unlike the accepted answer, this is **true** O(n), right? I know that O(2n) is considered O(n), but for very large `n` it can be noticeably slower. – kevlarr Dec 05 '17 at 16:04
4

Simple as that :

stuff = [2, 4, 8, 15, 11]

index = stuff.index(max(stuff))
3

Pandas has now got a much more gentle solution, try it:

df[column].idxmax()

2

Just a minor addition to what has already been said. values.index(min(values)) seems to return the smallest index of min. The following gets the largest index:

    values.reverse()
    (values.index(min(values)) + len(values) - 1) % len(values)
    values.reverse()

The last line can be left out if the side effect of reversing in place does not matter.

To iterate through all occurrences

    indices = []
    i = -1
    for _ in range(values.count(min(values))):
      i = values[i + 1:].index(min(values)) + i + 1
      indices.append(i)

For the sake of brevity. It is probably a better idea to cache min(values), values.count(min) outside the loop.

hyperbolic
  • 41
  • 3
  • 2
    `reversed(…)` instead of `….reverse()` is likely preferable as it doesn't mutate and returns a generator anyway. And all occurrences could also be `minv = min(values); indices = [i for i, v in enumerate(values) if v == minv]` – HoverHell Nov 13 '12 at 12:30
2

A simple way for finding the indexes with minimal value in a list if you don't want to import additional modules:

min_value = min(values)
indexes_with_min_value = [i for i in range(0,len(values)) if values[i] == min_value]

Then choose for example the first one:

choosen = indexes_with_min_value[0]
1

Dont have high enough rep to comment on existing answer.

But for https://stackoverflow.com/a/11825864/3920439 answer

This works for integers, but does not work for array of floats (at least in python 3.6) It will raise TypeError: list indices must be integers or slices, not float

0

https://docs.python.org/3/library/functions.html#max

If multiple items are maximal, the function returns the first one encountered. This is consistent with other sort-stability preserving tools such as sorted(iterable, key=keyfunc, reverse=True)[0]

To get more than just the first use the sort method.

import operator

x = [2, 5, 7, 4, 8, 2, 6, 1, 7, 1, 8, 3, 4, 9, 3, 6, 5, 0, 9, 0]

min = False
max = True

min_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = min )

max_val_index = sorted( list(zip(x, range(len(x)))), key = operator.itemgetter(0), reverse = max )


min_val_index[0]
>(0, 17)

max_val_index[0]
>(9, 13)

import ittertools

max_val = max_val_index[0][0]

maxes = [n for n in itertools.takewhile(lambda x: x[0] == max_val, max_val_index)]
The Demz
  • 6,336
  • 4
  • 33
  • 42
0

What about this:

a=[1,55,2,36,35,34,98,0]
max_index=dict(zip(a,range(len(a))))[max(a)]

It creates a dictionary from the items in a as keys and their indexes as values, thus dict(zip(a,range(len(a))))[max(a)] returns the value that corresponds to the key max(a) which is the index of the maximum in a. I'm a beginner in python so I don't know about the computational complexity of this solution.

0

Assuming you have a following list my_list = [1,2,3,4,5,6,7,8,9,10] and we know that if we do max(my_list) it will return 10 and min(my_list) will return 1. Now we want to get the index of the maximum or minimum element we can do the following.

my_list = [1,2,3,4,5,6,7,8,9,10]

max_value = max(my_list) # returns 10
max_value_index = my_list.index(max_value) # retuns 9

#to get an index of minimum value

min_value = min(my_list) # returns 1
min_value_index = my_list.index(min_value) # retuns 0
Hadi Mir
  • 2,100
  • 1
  • 16
  • 21