Reputation: 107
I am trying to get both the closest value and its index in a sorted list in python.
In MATLAB this is possible with:
[closest_val,index] = min(abs(array - target))
I was wondering if there is a similar way this can be implemented in python.
I've seen posts which do one or the other, but I haven't seen both done together.
Link to finding closest value in list post.
Upvotes: 0
Views: 2952
Reputation: 46849
this is an option:
lst = [
13.09409,
12.18347,
11.33447,
10.32184,
9.544922,
8.813385,
]
target = 11.5
res = min(enumerate(lst), key=lambda x: abs(target - x[1]))
# (2, 11.33447)
enumerate
iterates over your list in index, value
pairs. the key
of the min
method tells it to only consider the value
.
note that python starts indexing at 0
; matlab at 1
as far as i remember. if you want that same behavior:
res = min(enumerate(lst, start=1), key=lambda x: abs(target - x[1]))
# (3, 11.33447)
if the list is big, i strongly suggest you use bisect
as suggested in this answer.
Upvotes: 6
Reputation: 198324
bisect
wasn't used in the linked question because the list was not sorted. Here, we don't have the same problem, and we can use bisect
for the speed it provides:
import bisect
def find_closest_index(a, x):
i = bisect.bisect_left(a, x)
if i >= len(a):
i = len(a) - 1
elif i and a[i] - x > x - a[i - 1]:
i = i - 1
return (i, a[i])
find_closest_index([1, 2, 3, 7, 10, 11], 0) # => 0, 1
find_closest_index([1, 2, 3, 7, 10, 11], 7) # => 3, 7
find_closest_index([1, 2, 3, 7, 10, 11], 8) # => 3, 7
find_closest_index([1, 2, 3, 7, 10, 11], 9) # => 4, 10
find_closest_index([1, 2, 3, 7, 10, 11], 12) # => 5, 11
EDIT: In case of descending array:
def bisect_left_rev(a, x, lo=0, hi=None):
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if a[mid] > x: lo = mid+1
else: hi = mid
return lo
def find_closest_index_rev(a, x):
i = bisect_left_rev(a, x)
if i >= len(a):
i = len(a) - 1
elif i and a[i] - x < x - a[i - 1]:
i = i - 1
return (i, a[i])
find_closest_index_rev([11, 10, 7, 3, 2, 1], 0) # => 5, 1
find_closest_index_rev([11, 10, 7, 3, 2, 1], 7) # => 2, 7
find_closest_index_rev([11, 10, 7, 3, 2, 1], 8) # => 2, 7
find_closest_index_rev([11, 10, 7, 3, 2, 1], 9) # => 1, 10
find_closest_index_rev([11, 10, 7, 3, 2, 1], 12) # => 0, 11
Upvotes: 4