I have an array of values that I want to replace with from an array of choices based on which choice is linearly closest.
The catch is the size of the choices is defined at runtime.
import numpy as np
a = np.array([[0, 0, 0], [4, 4, 4], [9, 9, 9]])
choices = np.array([1, 5, 10])
If choices was static in size, I would simply use np.where
d = np.where(np.abs(a - choices[0]) > np.abs(a - choices[1]),
np.where(np.abs(a - choices[0]) > np.abs(a - choices[2]), choices[0], choices[2]),
np.where(np.abs(a - choices[1]) > np.abs(a - choices[2]), choices[1], choices[2]))
To get the output:
>>d
>>[[1, 1, 1], [5, 5, 5], [10, 10, 10]]
Is there a way to do this more dynamically while still preserving the vectorization.
I love
broadcasting
and would have gone that way myself too. But, with large arrays, I would like to suggest another approach withnp.searchsorted
that keeps it memory efficient and thus achieves performance benefits, like so -Please note that if the elements in
choices
are not sorted, we need to add in the additional argumentsorter
withnp.searchsorted
.Runtime test -
Related post : Find elements of array one nearest to elements of array two
To explain wwii's excellent answer in a little more detail:
The idea is to create a new dimension which does the job of comparing each element of
a
to each element inchoices
using numpy broadcasting. This is easily done for an arbitrary number of dimensions ina
using the ellipsis syntax:Taking
argmin
along the axis you just created (the last axis, with label -1) gives you the desired index inchoices
that you want to substitute:Which finally allows you to choose those elements from
choices
:For a non-symmetric shape:
Let's say
a
had shape(2, 5)
:Then you'd get:
This is hard to read, but what it's saying is,
b
has shape:The first two dimensions came from the shape of
a
, which is also(2, 5)
. The last dimension is the one you just created. To get a better idea:Note how
b[:, :, i]
is the absolute difference betweena
andchoices[i]
, for eachi = 1, 2, 3
.Hope that helps explain this a little more clearly.
Subtract choices from
a
, find the index of the minimum of the result, substitute.The extra dimension was added to
a
so that each element ofchoices
would be subtracted from each element ofa
.choices
was broadcast againsta
in the third dimension, This link has a decent graphic.b.shape
is (3,3,3). EricsBroadcastingDoc is a pretty good explanation and has a graphic 3-d example at the end.For the second example:
The final assignment uses an Index Array or Integer Array Indexing.
In the second example, notice that there was a tie for element
a[0,1]
, either one or five could have been substituted.