Given a pandas data frame containing objects with ids and latitudes and longitudes:
id latitude longitude
0 a 52.617960 1.717728
1 b 51.773427 -4.455351
2 c 52.206543 -4.345786
I would like to find the closest object with a different id in the same data frame resulting in the following data:
closest_id closest_latitude closest_longitude distance id latitude longitude
0 c 52.206543 -4.345786 413.676582 a 52.617960 1.717728
1 c 52.206543 -4.345786 48.741132 b 51.773427 -4.455351
2 b 51.773427 -4.455351 48.741132 c 52.206543 -4.345786
My working (AFIK) but clumsy/rooky/non pythonic attempt can be found below. Any improvement suggestions would be very much welcome.
import pandas as pd
from math import radians, cos, sin, asin, sqrt
def dist(lat1, long1, lat2, long2):
"""
Calculate the great circle distance between two points
on the earth (specified in decimal degrees)
"""
# convert decimal degrees to radians
lat1, long1, lat2, long2 = map(radians, [lat1, long1, lat2, long2])
# haversine formula
dlon = long2 - long1
dlat = lat2 - lat1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
# Radius of earth in kilometers is 6371
km = 6371* c
return km
test_data = {'id':['a','b','c'], 'latitude':[52.61796, 51.773427, 52.206543], 'longitude':[1.717728, -4.455351, -4.345786]}
haves = pd.DataFrame(test_data)
def find_nearest(lat, long, id, df):
# guess very inefficient
filter_data = df.copy()
filter_data.drop(filter_data.index[filter_data['id'] == id], inplace = True)
distances = filter_data.apply(lambda row: dist(lat, long, row['latitude'], row['longitude']), axis=1)
#print(distances)
dic = dict()
dic['closest_id'] = filter_data.loc[distances.idxmin(), 'id']
dic['closest_latitude'] = filter_data.loc[distances.idxmin(), 'latitude']
dic['closest_longitude'] = filter_data.loc[distances.idxmin(), 'longitude']
dic['distance'] = distances[distances.idxmin()]
#print(dic)
return dic
print(haves)
wants = pd.DataFrame()
for index, row in haves.iterrows():
dic = find_nearest(row['latitude'], row['longitude'], row['id'], haves)
dic['id'] = row['id']
dic['latitude'] = row['latitude']
dic['longitude'] = row['longitude']
wants = wants.append(dic, ignore_index=True)
print(wants)