Nearest Neighbour Analysis with Geospatial data | by Abdishakur | Towards Data Science

Finding the nearest neighbour of an object to another is a common spatial data analysis task. For example, determining the closest service point to your home is an optimisation problem that requires performing Nearest Neighbour Analysis with spatial data.
In this tutorial, we perform Nearest Neighbourhood Analysis with Bike Sharing dataset from Chicago City. The dataset consists of two separate files. The first one is bike stations and the second is randomly generated points. We want to determine which station is near to any given point in the second dataset — the randomly generated points. The randomly generated dataset can represent a real case where the rider wants to determine which Bike station is nearest to park the bicycle.
We read the data with pandas and call the first data frame stations where we have attributes like station name, capacity and coordinates.
stations = pd.read_csv(“data/stations.csv”)stations.head()
The second table holds the randomly generated points. We call this Dataframe points. We have only three columns in this table, the id and coordinates of X and Y.
points = pd.read_csv(“data/points.csv”)points.head()
To perform any Geographic processing task, including the Nearest Neighbour Analysis for this tutorial, we need to convert the data into a Geodataframe using Geopandas. This will allow us to calculate any Geoprocessing task available in Geopandas as well as any Pandas methods we want to use.
The following function converts both datasets into a Geopandas Geodataframe. This creates an additional column where the Geometry is stored. We also construct a Coordinate reference system for the dataset, in this case, EPSG:4326.
def create_gdf(df, x=”xcoord”, y=”ycoord”):    return gpd.GeoDataFrame(df,     geometry=gpd.points_from_xy(df[y], df[x]),              crs={“init”:”EPSG:4326"})stations_gdf = create_gdf(stations)points_gdf = create_gdf(points)
Now, let us visualise the data in a map using Folium Python Library.
m = folium.Map([41.805379, -87.601501], tiles=”CartoDb dark_matter”)locs_stations = zip(stations_gdf.ycoord, stations_gdf.xcoord)locs_points = zip(points_gdf.ycoord, points_gdf.xcoord)for location in locs_stations:    folium.CircleMarker(location=location,         color=”red”,   radius=4).add_to(m)for location in locs_points:    folium.CircleMarker(location=location,         color=”white”, radius=2).add_to(m)m
The map shown below visualises station_gdf with red colour and points_gdf in white colour. The goal is to find out the closest station for the randomly generated points.
In the next section, we perform the nearest neighbour analysis using Geopandas and Shapely functionality.
We want to find the nearest Bike station to the other random points. We can use Shapely function nearest_points functionality to find out which geometry is closest to each location and in the process also save other attributes, for example, the name of the station. Here, we use caclulate_nearest function that takes the destination (station_gdf ) and the value we want to store from this dataset(station name).
def calculate_nearest(row, destination, val, col=”geometry”):    # 1 - create unary union        dest_unary = destination[“geometry”].unary_union    # 2 - find closest point    nearest_geom = nearest_points(row[col], dest_unary)    # 3 - Find the corresponding geom    match_geom = destination.loc[destination.geometry                 == nearest_geom[1]]    # 4 - get the corresponding value    match_value = match_geom[val].to_numpy()[0]    return match_value
We apply this function to the second dataset points_gdf to derive each random point’s nearest geometry (from station_gdf) and also the station name of the closest geometry.
# Get the nearest geometrypoints_gdf[“nearest_geom”] = points_gdf.apply(calculate_nearest, destination=stations_gdf, val=”geometry”, axis=1)# Get the nearest Bike station namepoints_gdf[“nearest_station”] = points_gdf.apply(calculate_nearest, destination=stations_gdf, val=”name”, axis=1)points_gdf.head()
The output is the following table where we have the two additional columns we created above.
Now, we have both nearest_station and nearest_geom in the points_gdf . for instance, in id 2, we can see that the nearest station is Lincoln Ave & Belle Plane Ave station. We need to verify our results, let create a line Geodataframe from geometry and nearest_geom which helps us explore the data visually.
# Create LineString Geometrypoints_gdf[‘line’] = points_gdf.apply(lambda row: LineString([row[‘geometry’], row[‘nearest_geom’]]), axis=1)# Create Line Geodataframeline_gdf = points_gdf[["id", "nearest_station", "line"]].set_geometry('line')# Set the Coordinate referenceline_gdf.crs = crs={"init":"epsg:4326"}
We have destination points station_gdf, nearest points inpoints_gdf and now theline_gdf that connects both datasets. Let us visualise all of them in one plot.
m = folium.Map([41.805379, -87.601501],zoom_start = 12,      tiles=”CartoDb dark_matter”)locs_stations = zip(stations_gdf.ycoord, stations_gdf.xcoord)locs_points = zip(points_gdf.ycoord, points_gdf.xcoord)for location in locs_stations:    folium.CircleMarker(location=location,               color=”red”,  radius=8).add_to(m)for location in locs_points:    folium.CircleMarker(location=location,               color=”white”, radius=4).add_to(m)folium.GeoJson(line_gdf).add_to(m)m.save(“map2.html”)m
You can easily explore the dataset and see the connection between the nearest station of each point with the help of the map shown below.
The map shows Bike stations in Red circle, the random points in White and the connection line in Blue. You can explore further the interactive map in the accompanying notebook. The final result is accurate and also can be replicated in any other dataset of your choice.
In this tutorial, we covered how to perform Nearest Neighbourhood Analysis using Geopandas and Shapely using Chicago Bike stations. We also explored how we can easily construct and visualise the result of the analysis with Folium.
The code for this tutorial is available in this Github repository:
github.com
Google Colab Notebook can be accessed directly from here: