python: round float values to interval limits / grid

Multi tool use
python: round float values to interval limits / grid
I've got an array of (random) floating point numbers. I want to round each value up to a limit of an arbitrary grid. See the following example:
import numpy as np
np.random.seed(1)
# setup
sample = np.random.normal(loc=20, scale=6, size=10)
intervals = [-np.inf, 10, 12, 15, 18, 21, 25, 30, np.inf]
# round each interval up
for i in range(len(intervals) - 1):
sample[np.logical_and(sample > intervals[i], sample <= intervals[i+1])] = intervals[i+1]
This results in:
[ 30. 18. 18. 15. 30. 10. inf 18. 25. 21.]
My question is: How can I avoid the for-loop? I'm sure there's some way using numpy's array magic that I don't see right now.
4 Answers
4
If intervals
is sorted, you can use np.searchsorted
:
intervals
np.searchsorted
np.array(intervals)[np.searchsorted(intervals, sample)]
# array([ 30., 18., 18., 15., 30., 10., inf, 18., 25., 21.])
searchsorted
returns the index of the interval where the element belongs to:
searchsorted
np.searchsorted(intervals, sample)
# array([7, 4, 4, 3, 7, 1, 8, 4, 6, 5])
The default side='left'
returns the smallest index of such interval and the result falls into the left open, right close scenario.
side='left'
Did not run a check but:
from bisect import bisect
for index, value in enumerate(sample):
sample[index] = intervals[ bisect( intervals, value)]
You can use Pandas cut()
:
cut()
import pandas as pd
pd.cut(sample, intervals, labels=intervals[1:]).tolist()
If values
is a 1D arrays with your values, you could do something like
values
diff = values < intervals[:, None]
t = np.argmax(diff, axis=0)
new_values = intervals[t]
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.