Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounding an array to values given in another array

Say I have an array:

values = np.array([1.1,2.2,3.3,4.4,2.1,8.4])

I want to round these values to members of an arbitrary array, say:

rounds = np.array([1.,3.5,5.1,6.7,9.2])

ideally returning an array of rounded numbers and an array of the residues:

rounded = np.array([1.,1.,3.5,5.1,1.,9.2])
residues = np.array([-0.1,-1.2,0.2,0.7,-1.1,0.6])

Is there a good pythonic way of doing this?

like image 801
Kieran Hunt Avatar asked Jan 01 '26 21:01

Kieran Hunt


2 Answers

One option is this:

>>> x = np.subtract.outer(values, rounds)
>>> y = np.argmin(abs(x), axis=1)

And then rounded and residues are, respectively:

>>> rounds[y]
array([ 1. ,  1. ,  3.5,  5.1,  1. ,  9.2])

>>> rounds[y] - values
array([-0.1, -1.2,  0.2,  0.7, -1.1,  0.8])

Essentially x is a 2D array of every value in values minus every value in rounds. y is a 1D array of the index of the minimum absolute value of each row of x. This y is then used to index rounds.

I should caveat this answer by noting that if len(values) * len(rounds) is big (e.g. starting to exceed 10e8), memory usage may start to become of concern. In this case, you could consider building up y iteratively instead to avoid having to allocate a large block of memory to x.

like image 76
Alex Riley Avatar answered Jan 03 '26 10:01

Alex Riley


As the items in rounds array are sorted(or if not sort them) we can do this is O(n logn) time using numpy.searchsorted:

from functools import partial

def closest(rounds, x):
   ind = np.searchsorted(rounds, x, side='right')
   length = len(rounds)
   if ind in (0, length) :
      return rounds[ind]
   else:
      left, right = rounds[ind-1], rounds[ind]
      val = min((left, right), key=lambda y:abs(x-y))
      return val

f = partial(closest, rounds)
rounded = np.apply_along_axis(f, 1, values[:,None])[:,0]
residues = rounded - values
print repr(rounded)
print repr(residues)

Output:

array([ 1. ,  1. ,  3.5,  5.1,  1. ,  9.2])
array([-0.1, -1.2,  0.2,  0.7, -1.1,  0.8])
like image 43
Ashwini Chaudhary Avatar answered Jan 03 '26 10:01

Ashwini Chaudhary



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!