26

I am looking to find the lowest positive value in an array and its position in the list. If a value within the list is duplicated, only the FIRST instance is of interest. This is what I have which does what I want but includes 0.

print "Position:", myArray.index(min(myArray))
print "Value:", min(myArray)

for example, as it stands if,

myArray = [4, 8, 0, 1, 5]

then Position: 2, Value: 0

I want it to present position: 3, value: 1

Air
  • 8,274
  • 2
  • 53
  • 88
user3001499
  • 811
  • 4
  • 16
  • 32
  • Can you get duplicate values in your array? If so, are you only interested in the position of the first instance? – Mike P Jan 15 '15 at 15:29
  • 1
    duplicates are very possible, and yes first instance is what is of interest, thanks for pointing this out, will amend question – user3001499 Jan 15 '15 at 15:30
  • 1
    I'm confused by the problem description: it says 'I am looking to find the minimum value in an array that is greater than 0 and its corresponding position' which to me reads like the task is to find the smallest value which is greater than zero and greater than its corresponding position. I guess that's actually meant is to find the smallest value (and its position) which is greater than zero... – Frerich Raabe Jan 15 '15 at 15:37
  • Be forewarned that, all the solutions given below will fail if the list has no item which is greater than 0. – thefourtheye Jan 15 '15 at 15:46

8 Answers8

48

You can use a generator expression with min. This will set m as the minimum value in a that is greater than 0. It then uses list.index to find the index of the first time this value appears.

a = [4, 8, 0, 1, 5]

m = min(i for i in a if i > 0)

print("Position:", a.index(m))
print("Value:", m)
# Position: 3
# Value: 1
Ffisegydd
  • 51,807
  • 15
  • 147
  • 125
  • @user3001499 I was confused by the wording in your question and added a comment now to explain my confusion. – Frerich Raabe Jan 15 '15 at 15:37
  • 1
    In the worst case this solution requires traversing `a` twice (if the smallest value is at the end of the list). – Frerich Raabe Jan 15 '15 at 15:38
  • Yeah, honestly if I was accepting I'd accept thefourtheye's answer below (which is why I upvoted it). @user3001499 I'd suggest you go with thefourtheye's solution (and won't feel bad if you unaccept me for him :) ) – Ffisegydd Jan 15 '15 at 15:40
  • @Ffisegydd I am new to python and have been advised to avoid lambda expressions until I am more confident in using the language, so thanks for the advice but for now I will be using your answer – user3001499 Jan 15 '15 at 15:47
  • 1
    beware that this technique will raise a ValueError if none of the members of list `a` is more than zero – Thruston Apr 08 '21 at 15:04
12

You can use the min function and enumerate function, like this

result = min(enumerate(a), key=lambda x: x[1] if x[1] > 0 else float('inf'))
print("Position : {}, Value : {}".format(*result)
# Position : 3, Value : 1

This makes sure that, if the value is greater than 0, then use that value for the minimum value comparison otherwise use the maximum possible value (float('inf')).

Since we iterate along with the actual index of the items, we don't have to find the actual index with another loop.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
4

Here is another way of doing it with a generator expression. Note how the values coming from enumerate (a and b) are swapped in the tuple to sort correctly.

value,position = min(((b,a) for a,b in enumerate(myArray) if b>0), default=(None,None))

The default argument will be returned when the generator expression returns nothing (i.e. there are no items greater than 0). The default can be set to whatever makes sense in the surrounding program logic - here returning None will allow you to test with either if value: or if position:

neil
  • 3,387
  • 1
  • 14
  • 11
  • 1
    This should be accepted, it's more Pythonic and faster than other answers. – petabyte Jul 04 '16 at 05:22
  • It would be nice if it could tolerate cases where there is no value >0. – EL_DON Nov 03 '16 at 23:33
  • 1
    @EL_DON - how about that? I tried several clunkier methods before realising `min` had a default argument. – neil Nov 04 '16 at 18:57
  • My version doesn't seem to have a default argument for `min`. Looks like a good solution, though. Also, I found out that if I form my inputs properly, I don't run into the problem. Imagine that. – EL_DON Nov 04 '16 at 19:30
  • @EL_DON it seems that it was added in 3.4 – neil Nov 04 '16 at 22:17
1

add a filter then :

myArray = [4, 8, 0, 1, 5]
result = min(filter(lambda x: x > 0, myArray))
print result # 1
print myArray.index(result) # 3
markcial
  • 9,041
  • 4
  • 31
  • 41
1
def find_min_position(array):
    plus_array = [elem for elem in array if elem > 0]
    min_elem = min(plus_array)
    return min_elem, array.index(min_elem)

In : find_min_position([4, 8, 0, 1, 5])
Out: (1, 3)
sokoli
  • 501
  • 3
  • 10
0
import numpy as np

x = np.array([1,2,0,5,10])
x = np.extract(x>0,x)
min_index = np.amin(x)
min_value = np.argmin(x)
sramij
  • 4,775
  • 5
  • 33
  • 55
0

the complicated / algorithmic way:

int min = array[0], i = 1
list smallest //list of indexes of the smallest element 

// find the first element greater than 0
while (min <= 0 and i < array.length) {
    min = array[i]
    i++
}

// find the first instance of the smallest element greater than 0
while (i < array.length) {
    if (array[i] < min and array[i] > 0) {
        clear the list
        min = array[i]
        list.append(i)
    }
    else if (array[i] == min) {
        list.append(i)
    }
    i++;
}

the first instance of the smallest element greater than 0 is now the first element that you added to the list.

edit: you'll also have a list of every index of the smallest value. Some simple checks can tell you if there are no elements in the array greater than 0, or if the list is empty, etc.

JHaps
  • 41
  • 3
  • I would say that using for is way more pythonic than using while, especially in this situation. Also if you need the index and value, enumerate is the way to go, as pointed on thefourtheye answer – bwagner Jan 15 '15 at 16:34
  • True, I will leave the answer the way it is, however. For anyone interested in why this is the case, check out this: http://stackoverflow.com/questions/920645/when-to-use-while-or-the-for-in-python – JHaps Jan 15 '15 at 18:19
0

Here's what I did in NumPy:

the_array = numpy.array([2, -4, 0, 5, -inf, 1, -3)]
the_array[the_array <= 0] = 'inf'

min_number = numpy.amin(the_array) # or numpy.argmin() for getting the index

The idea is to convert all numbers that are <= 0 to a very large number, like inf.

Shahriar
  • 1,855
  • 2
  • 21
  • 45