debugpythonMinor
Function that generates steps time series from user given values
Viewed 0 times
generatesuserfunctiontimethatvaluesseriesfromgivensteps
Problem
I created simple function, that generate values of series representing repeating sequence of steps in time. User can define:
If the size is not defined, the size of returned data should be determined by number of repeats.
So the call
should return
The function follows
I would appreciate any feedback. Especially I am not sure about effectivity of the error raising as it is implemented ri
- step values
- width of the steps
- how many times the step sequence should repeat
- or the size of returned series
If the size is not defined, the size of returned data should be determined by number of repeats.
So the call
steps(2, [1, 2, 3], repeat=2)should return
[1 1 2 2 3 3 1 1 2 2 3 3]The function follows
def steps(step_width, values, repeat=1, size=None):
"""
This function generates steps from given values.
**Args:**
* `step_width` - desired width of every step (int)
* `values` - values for steps (1d array)
**Kwargs:**
* `repeat` - number of step sequence repetions (int), this value is used,
if the `size` is not defined
* `size` - size of output data in samples (int), if the `size` is used,
the `repeat` is ignored.
**Returns:**
* array of values representing desired steps (1d array)
"""
try:
step_width = int(step_width)
except:
raise ValueError('Step width must be an int.')
try:
repeat = int(repeat)
except:
raise ValueError('Repeat arg must be an int.')
try:
values = np.array(values)
except:
raise ValueError('Values must be a numpy array or similar.')
# generate steps
x = np.repeat(values, step_width)
if size is None:
# repeat according to the desired repetions
x_full = np.tile(x, repeat)
else:
try:
repeat = int(repeat)
except:
raise ValueError('Repeat arg must be an int.')
# repeat till size is reached and crop the data to match size
repeat = int(np.ceil(size / float(len(x))))
x_full = np.tile(x, repeat)[:size]
return x_fullI would appreciate any feedback. Especially I am not sure about effectivity of the error raising as it is implemented ri
Solution
Your type checks are distracting, they are probably better extracted to a separate function.
Note that this has slightly different behaviour than your code: you would e.g. accept a
If you don't mind the conversion, then you probably don't need to wrap your calls to
I would also refactor your code to have the output creation happen in a single, common place, after some manipulation of the arguments:
def check_type_or_raise(obj, expected_type, obj_name):
if not isinstance(obj, expected_type):
raise TypeError(
"'{}' must be {}, not {}".format(
obj_name,
expected_type.__name,
obj.__class__.__name__)Note that this has slightly different behaviour than your code: you would e.g. accept a
step_width of 2.5 and convert it to a 2. This is typically not what you want, so I think it is better to raise for such cases.If you don't mind the conversion, then you probably don't need to wrap your calls to
int() in a try and re-raise, as you will already get a nice error, e.g.:>>> int('abc')
ValueError: invalid literal for int() with base 10: 'abc'I would also refactor your code to have the output creation happen in a single, common place, after some manipulation of the arguments:
def steps(step_width, values, repeat=1, size=None):
check_type_or_raise(step_width, int, 'step_width')
check_type_or_raise(repeat, int, 'repeat')
values = np.asarray(values)
if size is not None:
check_type_or_raise(size, int, 'size')
# This does rounded up integer division without floating point
repeat = (size - 1) // (len(values) * step_width) + 1
return np.tile(np.repeat(values, step_width), repeat)[:size]Code Snippets
def check_type_or_raise(obj, expected_type, obj_name):
if not isinstance(obj, expected_type):
raise TypeError(
"'{}' must be {}, not {}".format(
obj_name,
expected_type.__name,
obj.__class__.__name__)>>> int('abc')
ValueError: invalid literal for int() with base 10: 'abc'def steps(step_width, values, repeat=1, size=None):
check_type_or_raise(step_width, int, 'step_width')
check_type_or_raise(repeat, int, 'repeat')
values = np.asarray(values)
if size is not None:
check_type_or_raise(size, int, 'size')
# This does rounded up integer division without floating point
repeat = (size - 1) // (len(values) * step_width) + 1
return np.tile(np.repeat(values, step_width), repeat)[:size]Context
StackExchange Code Review Q#163081, answer score: 5
Revisions (0)
No revisions yet.