8

How can I get the next minimum value to a value provided in python? Is there any inbuilt functions for it?

>>>num_list=[1,2,3,4]
>>> min(num_list)
1
>>> max(num_list)
4

How can I find the next lowest to 3 or next greatest to 2 ? The expected results are 2 and 3 respectievely.

cutteeth
  • 2,148
  • 3
  • 25
  • 45

7 Answers7

6

TL;DR Either min(n for n in my_list if n>lower_bound) or max(n for n in my_list if n<upper_bound)


A much faster alternative for finding the immediate minimum or the immediate maximum is numpy

>>> import numpy as np
>>> np.random.seed(10)
>>> a = np.random.random(10000)
>>> a[a>0.7].min()
0.69999533217645671
>>> a[a<0.7].max()
0.70003449227846715

If you are uncomfortable using the numpy machinery and want to deal simply with a list

>>> a = list(a)

then you can use the min and max builtins along with generator expressions

>>> min(n for n in a if n>0.7)
0.69999533217645671
>>> max(n for n in a if n<0.7)
0.70003449227846715
>>>

Using lists you have, of course, the same results but beware that there is a difference in performance: using ipython and %timeit to get the timings, I had 871 µs using numpy and 13.8 ms using regular lists for the 100000 elements array/list of the previous examples.

HTH, ciao


Post Scriptum

The solutions in my answer are all O(n), compared with the O(n log n) of methods that use sorting --- further, for large data sets the numpy approach should (italics because I have no testing at hand...) be affected by a small multiplicative factor.

gboffi
  • 22,939
  • 8
  • 54
  • 85
  • using `min(n for n in a if n>m)` solved my issue. Can I convert a regular list to type `np.random.random()` if the list is not generated by `np.random.seed(n)`? – cutteeth Apr 07 '15 at 06:04
  • 1
    In my understanding, you're asking "I have a _regular_ list, and, for speed reasons, I'd like to use `numpy`'... but it seems to me that you're not familiar with `numpy` so my advice is "stay with regular lists". If you feel like learning quite a bit of new stuff, here it is the recipe: `import numpy as np`, `my_list = list_computing_function()`, `my_array = np.array(my_list)` and eventually `nearmin5 = my_array[my_array>5].min()` but please don't apply it blindly, in your context it may even be slower than the list solution. – gboffi Apr 07 '15 at 06:52
  • can u please remove the numpy part of the answer? I check this answer whenever I get a notification on SO about the same. I think its better to remove numpy part or move it below `min and max part` which is the appropriate answer to the question. Thanks :) – cutteeth May 07 '17 at 18:24
5

I see your question is tagged [lower-bound] and [upperbound]. If your list is sorted, Python has an equivalent of C++ <algorithm>'s lower_bound and upper_bound. They're in the bisect module. They return the indices of the begin and immediately after the end of a range of some specific value.

In [1]: import bisect

In [2]: A = [0, 1, 3, 3, 5]

In [3]: A[bisect.bisect_left(A, 3)-1]
Out[3]: 1

In [4]: A[bisect.bisect_right(A, 3)]
Out[4]: 5
Juan Lopes
  • 10,143
  • 2
  • 25
  • 44
3

next lowest to 3:

max([x for x in num_list if x < 3])

next greatest to 2:

min([x for x in num_list if x > 2])
Ofir
  • 1,565
  • 3
  • 23
  • 41
  • 4
    You don't need list comprehensions here, i.e. just `max(x for x in num_list if x < 3)` is enough (and more efficient). – Frerich Raabe Apr 06 '15 at 13:21
2

Use heapq.nlargest and heapq.nsmallest

import heapq

num_list = [1, 2, 3, 4]

heapq.nlargest(2, num_list)
heapq.nsmallest(2, num_list)
#>>> [4, 3]
#>>> [1, 2]
Veedrac
  • 58,273
  • 15
  • 112
  • 169
1

You can use sorted :

>>> l=sorted(num_list,reverse=True)
>>> l[l.index(3)+1]
2

But as a more pythonic way as Frerich Raabe says in comment you dont need to sore whole of list you can find the max value on elements that are lower than 3 :

>>> max(i for i in num_list if i<3)
2

And for next largest after 2 you can use min :

>>> min(i for i in num_list if i>2)
3
Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • 1
    Sorting the entire list is overkill. Note that you only need to look at each element of the input list once to find e.g. the largest element which is smaller than 3. I.e. this can be done in linear time. – Frerich Raabe Apr 06 '15 at 13:23
  • Note that this is also buggy because you could get an IndexError – Shashank Apr 06 '15 at 13:29
  • @FrerichRaabe yes, the OP's question make me far of this elegant asnwer! thanks for reminding ! – Mazdak Apr 06 '15 at 13:30
  • @Shashank i removed the generator solution! – Mazdak Apr 06 '15 at 13:33
0

You could use following approach:

num_list = [1,2,3,4]   
inds = sorted(range(len(num_list)), key=lambda k: num_list[k])

Then, inds[1] will contain index of next lowest element and so on. Also, you could use the following code without sorting:

minv = min(num_list)    
nmin = min(nm for nm in num_list if nm > minv)
maxv = max(num_list)
nmax = max(nm for nm in num_list if nm < maxv)
kvorobiev
  • 5,012
  • 4
  • 29
  • 35
0

The provided answers are good, but if I could make a suggestion -- if there are ever times when the values could be repeated, such as

num_list = [2, 2, 4, 4, 6, 7, 8, 9]

...and so on, just sorting the list and getting the first index may not be what you're looking for.

By passing it through a set() first, you'll make sure that each entry is a singleton:

def sorted_ordered_list(sequence):
    return sorted(list(set(sequence)))

Then you can just index the returned list for whichever value you're looking for, from the lowest at index 0 to the highest.

Example:

>>> my_list = [1, 5, 4, 3, 6, 3, 8, 3, 6, 7, 4, 2, 6, 7, 9, 8, 8]
>>> sorted_ordered_list(my_list)
[1, 2, 3, 4, 5, 6, 7, 8, 9] # now index the list for the desired value
>>> 
  • This is a way but costly. Considering the original problem can be solved in linear time and fixed space. – wsysuper Apr 07 '15 at 10:33
  • sure, but it's a one-liner that uses naught but builtins, kind of like the OP asked for –  Apr 07 '15 at 13:40