6

I have the following list of lists of values and I want to find the min value among all the values.

Q = [[8.85008011807927, 4.129896248976861, 5.556804136197901], 
     [8.047707185696948, 7.140707521433818, 7.150610818529693],  
     [7.5326340018228555, 7.065307672838521, 6.862894377422498]]

I was planning to do something like:

min(min(Q))

I tried this approach on a smaller example and it works:

>>>b = [[2,2],[1,9]]
>>>min(b)
[1, 9]
>>>min(min(b))
1

But using this on my original list Q it returns the wrong result:

>>> min(Q)
[7.5326340018228555, 7.065307672838521, 6.862894377422498]
>>> min(min(Q))
6.862894377422498

Why is this approach wrong and why?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Alvin
  • 383
  • 5
  • 16
  • 5
    As for why it's wrong, lists are sorted by their first element; subsequent elements are only used to break ties if the first elements are equal. – Wooble Feb 28 '14 at 15:17

9 Answers9

7

Lists are compared using their lexicographical order1 (i.e. first elements compared, then the second, then the third and so on), so just because list_a < list_b doesn't mean that the smallest element in list_a is less than the smallest element in list_b, which is why your approach doesn't work in the general case.

For example, consider this:

>>> l1 = [3, 0]
>>> l2 = [2, 1]
>>> 
>>> min(l1, l2)
[2, 1]

The reason min(l1, l2) is [2, 1] is because the first element of l1 (3) is initially compared with that of l2 (2). Now, 2 < 3, so l2 is returned as the minimum without any further comparisons. However, it is l1 that really contains the smallest number out of both lists (0) which occurs after the initial element. Therefore, taking the min of min(l1, l2) gives us the incorrect result of 1.

A good way to address this would be to find the minimum of the "flattened" list, which can be obtained with a generator:

>>> Q = [[8.85008011807927, 4.129896248976861, 5.556804136197901], 
...      [8.047707185696948, 7.140707521433818, 7.150610818529693],  
...      [7.5326340018228555, 7.065307672838521, 6.862894377422498]]
>>> 
>>> min(a for sub in Q for a in sub)  # <--
4.129896248976861

(+1 to @Ffisegydd for posting a solution along these lines first.)


1 From http://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types:

Sequence objects may be compared to other objects with the same sequence type. The comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted. If two items to be compared are themselves sequences of the same type, the lexicographical comparison is carried out recursively. If all items of two sequences compare equal, the sequences are considered equal. If one sequence is an initial sub-sequence of the other, the shorter sequence is the smaller (lesser) one.

arshajii
  • 127,459
  • 24
  • 238
  • 287
3

Your approach didn't work properly because, that is how Python sequence comparison is done

I want to find the min value among all the values.

If you want to find the minimum of all the values, you can do something like this

print min(map(min, Q))
# 4.12989624898
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
3

You can use a generator expression coupled with the min function to find the answer:

Q = [[8.85008011807927, 4.129896248976861, 5.556804136197901], 
     [8.047707185696948, 7.140707521433818, 7.150610818529693],  
     [7.5326340018228555, 7.065307672838521, 6.862894377422498]]

minimum = min(i for j in Q for i in j)
print(minimum) # 4.12989624898

This generator expression flattens your list of lists and then simply returns the minimum value.

Community
  • 1
  • 1
Ffisegydd
  • 51,807
  • 15
  • 147
  • 125
1

min(map(min,Q)) ist the command you're looking for.

Denis
  • 136
  • 6
  • @Denis, probably why you got downvoted is because you included the code, without any explanation - so that would be my recommendation next time. as the comment to the other answer (which DID have at least some explanation) shows, having code doesn't always mean you can learn something from it, or understand why it works. – Corley Brigman Feb 28 '14 at 15:52
  • Please Denis, integrate your answer with an explanation. +1 for the correct answer. – Joe Taras Feb 28 '14 at 16:00
  • Thanks for the feedback guys. You're right, retrospectively I admit the answer was not very helpful, especially since the question explicitly pointed torwards "why" does it not work. I'll work on that! – Denis Feb 28 '14 at 16:41
1
  • min(Q) returns the "minimum" list in Q, which is the list that has the smallest first element.
  • Therefore, min(min(Q)) returns the smallest element of the list with the smalles first element, which is not what you want.

You could use

min(min(x) for x in Q)

instead, which returns the smallest of the minimums of all lists in Q.

Frank
  • 1,565
  • 1
  • 10
  • 9
1

What you really want is to flatten that list and then find the minimum:

min(value for row in Q for value in row)
vartec
  • 131,205
  • 36
  • 218
  • 244
1

There are lots of answers, but the easiest way IMHO is to make the 'list of lists' into a single list using itertools.chain.from_iterable:

from itertools import chain

min(chain.from_iterable(Q))

or the shorter and just as easy to read (to me) version:

min(chain(*Q))
Corley Brigman
  • 11,633
  • 5
  • 33
  • 40
0

I think I found why, min applied on a list of lists will compare the first values of each sublist.

>>> b=[[3,1],[2,5]]
>>> min(b)
[2, 5]
Alvin
  • 383
  • 5
  • 16
  • just a comment: in the future, since this doesn't answer the question but is an addition to your original question, you should just edit this back into your original question. – Corley Brigman Feb 28 '14 at 15:59
  • @CorleyBrigman His question was "Why is this approach wrong?", doesn't this answer it? – falstro Mar 01 '14 at 10:09
  • sort of... it explains why the original approach didn't work, but isn't an answer by itself. i didn't vote it down or anything, just posted the comment. – Corley Brigman Mar 02 '14 at 18:46
  • 1
    sorry, I'll try to keep in mind, any improvement of the question gets to edit the original question, any answer is a separate post. thank you – Alvin Mar 03 '14 at 13:32
0

min(Q) does not always return the list that must contain Minimum of all values. That's why your approach is wrong. You must find the min value of all list and make another list. then find min of that list, that's it.

Vijay Kumar
  • 345
  • 1
  • 7