Estoy trabajando con datos espaciales por primera vez. Tengo que comparar dos marcos de datos que tienen detalles de latitud y longitud. He convertido ambos a marcos de datos de GeoPandas.

import pandas as pd
from pandas import DataFrame
import geopandas as gpd
from neighbors import nearest_neighbor


df = pd.DataFrame([[1973,22.525158,88.330775],[1976,72.85136,19.10840],[898,91.78523,26.15012]],columns=['id', 'lat', 'long'])
gdf1 = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.long,df.lat))

df2 = pd.DataFrame([['06c979eaa59f',29.873870,76.965620],['19aedbb2e743',20.087574,76.180045],['5060a3931a43',31.289770,75.572340]],columns=['id','lat','lon']) 
gdf2 = gpd.GeoDataFrame(df2, geometry=gpd.points_from_xy(df2.lon,df2.lat))

Mi DF1 tiene 1 millón de filas y df2 tiene alrededor de 7000 filas. Estoy tratando de obtener los vecinos más cercanos de DF2 para cada registro en DF1.

He probado dos métodos. Ambos se ejecutan muy rápido y los resultados son viables. Sin embargo, no son precisos.

Método 1:

Por favor revise este enlace

En esta página, he utilizado el método de vecinos más cercanos de sklearn.neighbors. Esto devuelve los resultados en metros. Sin embargo, cuando verifico manualmente la distancia entre la latitud de dos marcos de datos, siempre encuentro que el vecino más cercano devuelve 1/4 de la distancia.

Por ejemplo, si la distancia devuelta por el método anterior es de 125 metros, tanto google map como https: // www. geodatasource.com/distance-calculator devuelve una distancia de unos 500 metros. La diferencia de distancia sigue fluctuando alrededor de 4 veces el resultado devuelto.

Método 2:

En el segundo método, seguí el código proporcionado en gis.stackexchange.com.

https://gis.stackexchange.com/questions/222315/geopandas-find-nearest-point-in-other-dataframe

import itertools
from operator import itemgetter

import geopandas as gpd
import numpy as np
import pandas as pd

from scipy.spatial import cKDTree
from shapely.geometry import Point, LineString

df = pd.DataFrame([[1973,22.525158,88.330775],[1976,72.85136,19.10840],[898,91.78523,26.15012]],columns=['id', 'lat', 'long'])
gdf1 = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.long,df.lat))

df2 = pd.DataFrame([['06c979eaa59f',29.873870,76.965620],['19aedbb2e743',20.087574,76.180045],['5060a3931a43',31.289770,75.572340]],columns=['id','lat','lon']) 
gdf2 = gpd.GeoDataFrame(df2, geometry=gpd.points_from_xy(df2.lon,df2.lat))

En esto, reemplacé el gpd1 y gpd2 con mis propios marcos de datos.

def ckdnearest(gdfA, gdfB, gdfB_cols=['id']):
    # resetting the index of gdfA and gdfB here.
    gdfA = gdfA.reset_index(drop=True)
    gdfB = gdfB.reset_index(drop=True)
    A = np.concatenate(
        [np.array(geom.coords) for geom in gdfA.geometry.to_list()])
    B = [np.array(geom.coords) for geom in gdfB.geometry.to_list()]
    B_ix = tuple(itertools.chain.from_iterable(
        [itertools.repeat(i, x) for i, x in enumerate(list(map(len, B)))]))
    B = np.concatenate(B)
    ckd_tree = cKDTree(B)
    dist, idx = ckd_tree.query(A, k=1)
    idx = itemgetter(*idx)(B_ix)
    gdf = pd.concat(
        [gdfA, gdfB.loc[idx, gdfB_cols].reset_index(drop=True),
         pd.Series(dist, name='dist')], axis=1)
    return gdf

c = ckdnearest(gdf1, gdf2)

Lo anterior se ejecuta muy rápido y devuelve el resultado. Sin embargo, los valores de distancia devueltos son al menos 100 veces más bajos que los que obtengo.

Multiplicador: 107.655914

enter image description here

En la imagen de Excel anterior, la primera columna indica los resultados devueltos por Python, mientras que la segunda columna los resultados devueltos por el mismo sitio web que se indicó anteriormente. Si bien estas aproximaciones en los resultados me ayudan a comenzar, quiero resultados precisos. ¿Cómo comparo los dos marcos de datos dados anteriormente y obtengo la distancia más cercana más precisa para cada fila en DF1?

2
Apricot 4 mar. 2021 a las 20:04

1 respuesta

La mejor respuesta

Cuando trabaje con datos espaciales, debe tener en cuenta que las coordenadas de sus puntos se proyectan en un plano desde una esfera. En Mercator, la distancia de proyección entre los puntos de latitud está en grados, no en metros. Y la conversión depende de la latitud de los puntos, ya que 1 grado en el ecuador será menos metros que 1 grado en latitudes altas.

Puede consultar esta discusión para ver posibles soluciones a este problema: https://gis.stackexchange.com/ preguntas / 293310 / cómo-utilizar-geoseries-distancia-para-obtener-la-respuesta-correcta

Para darle un ejemplo, una posibilidad es que convierta su geodataframe a la proyección UTM que cubre su región. Por ejemplo, Bélgica se cruza con la zona UTM 31N EPSG: 32631. La proyección de Mercator tiene un código epsg EPSG: 4326. Para convertir un GeoDataFrame / GeoSeries, debe proporcionar el CRS al crearlo:

s = gpd.GeoSeries(points, crs=4326)

Donde puntos es una lista de shapely.geometry.Point

Y luego convertir a un UTM dado:

s_utm = s.to_crs(epsg=32631)

Ahora, la distancia que calculará entre los puntos en s_utm estará en metros.

Sin embargo, debe asegurarse de que sus puntos caigan en la zona UTM dada o el resultado será inexacto. La respuesta que vinculé sugiere otros métodos que podrían funcionar también y podrían aplicarse a todo el conjunto de puntos.

También puede intentar convertir a EPSG 32663 (WGS 84 / Cilíndrico equidistante mundial) que debería preservar las distancias.

Otra opción podría ser usar geopy que permite calcular la distancia geodésica con geopy.geodesic.distance

1
dzang 4 mar. 2021 a las 17:35