Это похоже на использование bisect_left, но оно позволит вам передать массив целей
def find_closest(A, target):
#A must be sorted
idx = A.searchsorted(target)
idx = np.clip(idx, 1, len(A)-1)
left = A[idx-1]
right = A[idx]
idx -= target - left < right - target
return idx
Некоторое объяснение:
Сначала общий случай: idx = A.searchsorted(target)
возвращаетиндекс для каждого target
, такой что target
находится между A[index - 1]
и A[index]
.Я называю это left
и right
, поэтому мы знаем, что left < target <= right
.target - left < right - target
равно True
(или 1), когда цель ближе к left
и False
(или 0), когда цель ближе к right
.
Теперь особый случай: когда target
меньше всех элементов A
, idx = 0
.idx = np.clip(idx, 1, len(A)-1)
заменяет все значения idx
<1 на 1, поэтому <code>idx=1.В этом случае left = A[0]
, right = A[1]
и мы знаем, что target <= left <= right
.Поэтому мы знаем, что target - left <= 0
и right - target >= 0
, поэтому target - left < right - target
равно True
, если target == left == right
и idx - True = 0
.
Существует еще один особый случай, если target
больше, чем все элементыA
, в этом случае idx = A.searchsorted(target)
и np.clip(idx, 1, len(A)-1)
заменяет len(A)
на len(A) - 1
, поэтому idx=len(A) -1
и target - left < right - target
заканчиваются False
, поэтому idx возвращает len(A) -1
.Я позволю вам работать с логикой самостоятельно.
Например:
In [163]: A = np.arange(0, 20.)
In [164]: target = np.array([-2, 100., 2., 2.4, 2.5, 2.6])
In [165]: find_closest(A, target)
Out[165]: array([ 0, 19, 2, 2, 3, 3])