I am trying to find an efficient way to calculate the distance to the nearest neighbour for a set of coordinates of form (lat, lon):
[[51.51045038114607, -0.1393407528617875],
[51.5084300350736, -0.1261805976142865],
[51.37912856172232, -0.1038613174724213]]
I previously had a working (i thought!) piece of code which used sklearn's NearestNeighbors to reduce the algorithmic complexity of this task:
from sklearn.neighbors import NearestNeighbors
from sklearn.metrics.pairwise import haversine_distances
from math import sin, cos, sqrt, atan2, radians
# coordinates
coords = [[51.51045038114607, -0.1393407528617875],
[51.5084300350736, -0.1261805976142865],
[51.37912856172232, -0.1038613174724213]]
# tree method that reduces algorithmic complexity from O(n^2) to O(Nlog(N))
nbrs = NearestNeighbors(n_neighbors=2,
metric=_haversine_distance
).fit(coords)
distances, indices = nbrs.kneighbors(coords)
# the outputted distances
result = distances[:, 1]
The output is as follows:
array([ 1.48095104, 1.48095104, 14.59484348])
Which used my own version of the haversine distance as the distance metric
def _haversine_distance(p1, p2):
"""
p1: array of two floats, the first point
p2: array of two floats, the second point
return: Returns a float value, the haversine distance
"""
lon1, lat1 = p1
lon2, lat2 = p2
# convert decimal degrees to radians
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
# get the deltas
dlon = lon2 - lon1
dlat = lat2 - lat1
# haversine formula
a = np.sin(dlat/2)**2 + (np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2)
c = 2 * np.arcsin(np.sqrt(a))
# approximate radius of earth in km
R = 6373.0
# convert to km distance
distance = R * c
return distance
These distances are wrong, my first question is, why is this? Is there any way I can correct this while retaining the algorithmic simplicity of the NearestNeighbors method?
I then discovered I can get the correct answer by using the geopy.distance method, however this does not come with in-build techniques to reduce the complexity and therefore computation time
import geopy.distance
coords_1 = (51.51045038, -0.13934075)
coords_2 = (51.50843004, -0.1261806)
geopy.distance.geodesic(coords_1, coords_2).km
My second question is then, are there any implementations of this method that reduce the complexity, otherwise I will be forced to use nested for loops to check the distance between every point and all others.
Any help appreciated!
Related Question Vectorised Haversine formula with a pandas dataframe