Skip to content

Instantly share code, notes, and snippets.

@lukegre
Created May 18, 2020 07:03
Show Gist options
  • Save lukegre/4f02380da80e178c0f4f177f6964747a to your computer and use it in GitHub Desktop.
Save lukegre/4f02380da80e178c0f4f177f6964747a to your computer and use it in GitHub Desktop.
A few functions to help with functions that have limits for the inputs
def input_limits(return_bool=True, return_info=True, **kwargs):
"""
This is a function wrapper for functions that have defined input limits.
The user can define the valid limits of any of the inputs.
Parameters
----------
return_bool: array-bool
returns a boolean array of the original function where valid or not
return_info: array-str
returns the reason output is questionable, with the function name
and the input argument name that are not in the valid range.
**kwargs: key-value pair
the key should be the name of one of the input arguments and the
value should ne a list or tuple of the upper and lower limit
where the limits are exclusive (>/< not >=/<=)
Returns
-------
dict:
key['output'] = the original data
key['bool'] = inputs are not within the range
key['info'] = reason that output is flagged (which variable)
Example
-------
>>> @input_limits(S=[5, 50])
def mult(T, S):
return T * S
>>> T = np.array([4, 2, 25, 13])
>>> S = np.array([5, 31, 34, 28])
>>> mult(T, S)
{'output': array([ 20, 62, 850, 364]),
'bool': array([False, True, True, True]),
'info': array(["'S' outside limits (5, 50) for mult; ", '', '', ''], dtype='<U38')}
"""
def wrap(f):
def f_foo(*args, **kw):
argnames = f.__code__.co_varnames
funcname = f.__name__
args_as_dict = {k:v for k, v in zip(argnames, args)}
inside = []
info = np.array([""] * args[0].size)
for key in kwargs:
# make sure wrapper keys are in the function
assert key in argnames, (
f"'{key}' given to 'input_limits' wrapper is not an "
f"argument for {funcname}. Must be one of the "
f"following: {argnames}"
)
# must be a list or tuple
assert isinstance(kwargs[key], (list, tuple)), (
f"limits for '{key}' must be a list or "
f"tuple of lower and upper limits"
)
# must have upper and lower limit
assert len(kwargs[key]) == 2, (
f"limits for '{key}' must be a list or "
f"tuple of lower and upper limits"
)
# separate lower and upper limits
lim0, lim1 = kwargs[key]
# create a dictionary from input arguments for ease of calling
arr = args_as_dict[key]
# check that the input is within the limits
arr_inside = np.logical_and(arr > lim0, arr < lim1)
# set an array with default arguments
msg = np.array(
[f"'{key}' outside limits {lim0, lim1} for {funcname}; "] * arr.size
)
# set valid inputs to no string input
msg[arr_inside] = ""
# add to previous string array
info = np.char.add(info, msg)
# append the bool string
inside += arr_inside,
inside = np.array(inside).all(0)
out = {'output': f(*args, **kw),}
if return_bool:
out['bool'] = inside
if return_info:
out['info'] = info
return out
return f_foo
return wrap
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment