Say I have a list [1,2,3,4,5,6,7]
. I want to find the 3 closest numbers to, say, 6.5. Then the returned value would be [5,6,7]
.
Finding one closest number is not that tricky in python, which can be done using
JavaScript
x
2
1
min(myList, key=lambda x:abs(x-myNumber))
2
But I am trying not to put a loop around this to find k closest numbers. Is there a pythonic way to achieve the above task?
Advertisement
Answer
The short answer
The heapq.nsmallest() function will do this neatly and efficiently:
JavaScript
1
5
1
>>> from heapq import nsmallest
2
>>> s = [1,2,3,4,5,6,7]
3
>>> nsmallest(3, s, key=lambda x: abs(x - 6.5))
4
[6, 7, 5]
5
Essentially this says, “Give me the three input values that have the smallest absolute difference from the number 6.5“.
Optimizing for repeated lookups
In the comments, @Phylliida, asked how to optimize for repeated lookups with differing start points. One approach would be to pre-sort the data and then use bisect to locate the center of a small search segment:
JavaScript
1
8
1
from bisect import bisect
2
3
def k_nearest(k, center, sorted_data):
4
'Return *k* members of *sorted_data* nearest to *center*'
5
i = bisect(sorted_data, center)
6
segment = sorted_data[max(i-k, 0) : i+k]
7
return nsmallest(k, segment, key=lambda x: abs(x - center))
8
For example:
JavaScript
1
10
10
1
>>> s.sort()
2
>>> k_nearest(3, 6.5, s)
3
[6, 7, 5]
4
>>> k_nearest(3, 0.5, s)
5
[1, 2, 3]
6
>>> k_nearest(3, 4.5, s)
7
[4, 5, 3]
8
>>> k_nearest(3, 5.0, s)
9
[5, 4, 6]
10