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

4
  • Can you get duplicate values in your array? If so, are you only interested in the position of the first instance? Commented Jan 15, 2015 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 Commented Jan 15, 2015 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... Commented Jan 15, 2015 at 15:37
  • Be forewarned that, all the solutions given below will fail if the list has no item which is greater than 0. Commented Jan 15, 2015 at 15:46

8 Answers 8

49

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
Sign up to request clarification or add additional context in comments.

5 Comments

@user3001499 I was confused by the wording in your question and added a comment now to explain my confusion.
In the worst case this solution requires traversing a twice (if the smallest value is at the end of the list).
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 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
beware that this technique will raise a ValueError if none of the members of list a is more than zero
13

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.

3 Comments

first time i see using enumerate this way, great hint!
Upvoting this because it's the first to use enumerate (which avoids traversing a twice).
Upvoting for using the key parameter to the min function - instead of looping through with a generator.
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:

5 Comments

This should be accepted, it's more Pythonic and faster than other answers.
It would be nice if it could tolerate cases where there is no value >0.
@EL_DON - how about that? I tried several clunkier methods before realising min had a default argument.
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 it seems that it was added in 3.4
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)

Comments

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

1 Comment

That only gives the lowest value; it doesn't give the position in the array.
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)

1 Comment

This will return incorrect index because elements smaller than 0 are being deleted.
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.

2 Comments

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
True, I will leave the answer the way it is, however. For anyone interested in why this is the case, check out this: stackoverflow.com/questions/920645/…
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.