Getting the index of the returned max or min item using max()/min() on a list

1,238,592

Solution 1

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

Solution 2

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

Solution 3

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).

Solution 4

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)

Solution 5

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)
Share:
1,238,592
Kevin Griffin
Author by

Kevin Griffin

A Ruby developer, looking to branch out!

Updated on January 26, 2022

Comments

  • Kevin Griffin
    Kevin Griffin over 2 years

    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.

  • Mike Graham
    Mike Graham about 14 years
    @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.
  • HoverHell
    HoverHell over 11 years
    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]
  • scry
    scry over 10 years
    Or use a lambda: key=lambda p: p[1]
  • Tom Karzes
    Tom Karzes over 8 years
    @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)
  • tagoma
    tagoma over 7 years
    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 ...")
  • Ishan Tomar
    Ishan Tomar over 7 years
    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
  • mmj
    mmj over 6 years
    Late but best answer (if you don't have need for speed).
  • kevlarr
    kevlarr over 6 years
    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.
  • Shashi Tunga
    Shashi Tunga over 6 years
    @too much php what to do when there is repetition of elements.?
  • RBF06
    RBF06 about 6 years
    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.
  • Ishan Tomar
    Ishan Tomar almost 6 years
    Thanks @RBF06 I will check it out.
  • James
    James over 5 years
    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 ....)
  • Cohensius
    Cohensius over 5 years
    In case of multiple occurrences of the maximum values, the indices corresponding to the first occurrence are returned.
  • Dev_Man
    Dev_Man over 5 years
    raw. I prefer this.
  • gg349
    gg349 over 5 years
    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.
  • Rakurai
    Rakurai over 5 years
    @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
    Rakurai over 5 years
    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.
  • Scott Anderson
    Scott Anderson over 4 years
    @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
  • jimh
    jimh about 4 years
    np.argmin does not work for floats. only the first suggestion works on ints and floats.
  • gg349
    gg349 about 4 years
    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
  • Robvh
    Robvh over 3 years
    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
  • IniMzungu
    IniMzungu over 3 years
    There's elegance in simplicity. This answer is easy to understand for beginners while providing a useful output.
  • Boris Verkhovskiy
    Boris Verkhovskiy about 3 years
    you can inline the if as well: return values.index(min(values) if is_min_value else max(values))
  • tejasvi88
    tejasvi88 about 3 years
    min([(j, i) for i, j in enumerate(values)]) to avoid expensive function calls.
  • Recessive
    Recessive about 3 years
    @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.
  • Pegasus
    Pegasus about 2 years
    this should be the best answer
  • Muhammad Yasirroni
    Muhammad Yasirroni about 2 years
    please add benchmark result as raw text or code as not everyone get access to imgur.
  • Muhammad Yasirroni
    Muhammad Yasirroni about 2 years
    This is the fastest for single value AFAIK.
  • Muhammad Yasirroni
    Muhammad Yasirroni about 2 years
    The accepted answer is the fastest in single value search AFAIK.
  • Freddy Mcloughlan
    Freddy Mcloughlan almost 2 years
    Please add this answer as a comment