How to limit a number to be within a specified range? (Python) [duplicate]
Asked Answered
N

5

67

I want to limit a number to be within a certain range. Currently, I am doing the following:

minN = 1
maxN = 10
n = something() #some return value from a function
n = max(minN, n)
n = min(maxN, n)

This keeps it within minN and maxN, but it doesn't look very nice. How could I do it better?

PS: FYI, I am using Python 2.6.

Norvil answered 13/5, 2011 at 19:34 Comment(0)
S
127
def clamp(n, minn, maxn):
    return max(min(maxn, n), minn)

or functionally equivalent:

clamp = lambda n, minn, maxn: max(min(maxn, n), minn)

now, you use:

n = clamp(n, 7, 42)

or make it perfectly clear:

n = minn if n < minn else maxn if n > maxn else n

even clearer:

def clamp(n, minn, maxn):
    if n < minn:
        return minn
    elif n > maxn:
        return maxn
    else:
        return n
Sartorius answered 13/5, 2011 at 19:41 Comment(2)
def clamp(n, minn, maxn): return min(max(n, minn), maxn) slightly improves readability with arguments in the same order.Batista
The fastest solution (at least in my tests on multiple single random values against the more readable contender np.clip()).Exothermic
W
86

Simply use numpy.clip() (doc):

n = np.clip(n, minN, maxN)

It also works for whole arrays:

my_array = np.clip(my_array, minN, maxN)
Watterson answered 15/7, 2014 at 10:32 Comment(1)
Thanks. Was having a heck of a time trying not to write a custom calculation twice and not wanting to write my own function. :-)Scleritis
C
59

If you want to be cute, you can do:

n = sorted([minN, n, maxN])[1]
Chasse answered 13/5, 2011 at 19:40 Comment(6)
This will require more comparisons than the other approaches.Nananne
That's why I called it "cute" and not "practical." ;) However, it's highly unlikely that the inefficiency of this code will cause a meaningful performance problem in most cases.Chasse
Woah, that really is cute! I also like how it is invariant under interchange of minN and maxN. This is definitely my favorite clamp function. +1 ^_^Residential
if someone is interested what version works faster: both are fast but min(max(...)...) is about 1.4 times faster. Details: python -m timeit -s "min_n = 10; max_n = 15" "for x in range(30): max(min(x, max_n), min_n)":7.28 usec per loop. python -m timeit -s "min_n = 10; max_n = 15" "for x in range(30): sorted([min_n, x, max_n])[1]": 10.2 usec per loop. "min_n = 1000; max_n = 15000" "for x in range(-15000, 30000): ...": 11 msec per loop, "min_n = 1000; max_n = 15000" "for x in range(-15000, 30000): ...": 14.8 msec per loopKennet
no points for readability though :)Charades
readability: nested min-max needs more care that the parameters are in the right order, there's much less that can go wrong here. if you are reading for what the programmer intended maybe minmax is easier to read, but if you're reading for how the code functions I like this one.Conger
N
4

Define a class and have a method for setting the value which performs those validations.

Something vaguely like the below:

class BoundedNumber(object):
    def __init__(self, value, min_=1, max_=10):
        self.min_ = min_
        self.max_ = max_
        self.set(value)

    def set(self, newValue):
        self.n = max(self.min_, min(self.max_, newValue))

# usage

bounded = BoundedNumber(something())
bounded.set(someOtherThing())

bounded2 = BoundedNumber(someValue(), min_=8, max_=10)
bounded2.set(5)    # bounded2.n = 8
Nananne answered 13/5, 2011 at 19:43 Comment(8)
Well, it's extra development time to create, but it's SO REUSABLE! :-PNananne
i am sure it can even be extended to check for invalid input numbers like NaN or +/-inf.Sartorius
Yeah, and of course it could also be configured to have different bounds as well. :-)Nananne
and it can be plugged into a user interface for automatic input validation ! the possibilities are endless... you definitely should patent such an invention.Sartorius
Thanks. Downvoter: Is it because this doesn't feel very "Pythonic" or do you have an ACTUAL issue with my answer?Nananne
i get an error with that NameError: global name 'minN' is not defined python2.7Cyprinoid
a similar class could raise an exception if an operation causes a number to go out of range? similar to the ranged types in Ada that help catch errors en.wikipedia.org/wiki/Ada_%28programming_language%29#Data_typesLarios
I like this for more complicated examples, e.g. vectors or co-ordinates in a layout system SpritePosition = BoundVector(max=(screenwidth,screenheight)) solves a very common use case for me.Evident
C
0

Could you not string together some one-line python conditional statements?

I came across this question when looking for a way to limit pixel values between 0 and 255, and didn't think that using max() and min() was very readable so wrote the following function:

def clamp(x, minn, maxx):
   return x if x > minm and x < maxx else (minn if x < minn else maxx)

I would be interested to see how someone more experienced than me would find this way of clamping a value. I assume it must be less efficient than using min() and max(), but it may be useful for someone looking for a more readable (to me at least) function.

Champaigne answered 30/9, 2017 at 14:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.