2015-06-09 47 views
5

Sto cercando di eseguire l'equivalente di ArcPy Generate Near Table utilizzando Geopandas/Shapely. Sono molto nuovo a Geopandas e Shapely e ho sviluppato una metodologia che funziona, ma mi chiedo se c'è un modo più efficiente di farlo.Calcola la distanza dalla caratteristica più vicina con le Geopandas

Ho set di dati di due file di punti - Centri di censimento e ristoranti. Sto cercando di trovare, per ogni centroide del blocco censito, la distanza dal suo ristorante più vicino. Non ci sono restrizioni in termini di ristorante stesso essendo il ristorante più vicino per più blocchi.

Il motivo per cui questo diventa un po 'più complicato per me è perché il Geopandas Distance function calcola elementwise, la corrispondenza in base all'indice. Pertanto, la mia metodologia generale consiste nel trasformare il file Restaurants in un file multipunto e quindi impostare l'indice del file dei blocchi su tutti come lo stesso valore. Quindi tutti i centroidi dei blocchi e i ristoranti hanno lo stesso valore di indice.

import pandas as pd 
import geopandas as gpd 
from shapely.geometry import Polygon, Point, MultiPoint 

Ora leggete nel blocco Centroid and Restaurant Shapefiles:

Blocks=gpd.read_file(BlockShp) 
Restaurants=gpd.read_file(RestaurantShp) 

Dal momento che la funzione di distanza Geopandas calcola la distanza elementwise, ho convertire il geoserie ristorante ad un MultiPoint geoserie:

RestMulti=gpd.GeoSeries(Restaurants.unary_union) 
RestMulti.crs=Restaurants.crs 
RestMulti.reset_index(drop=True) 

Quindi ho impostato l'indice per i blocchi uguale a 0 (lo stesso valore del multipunto di ristoranti) come soluzione per il calcolo elementwise.

Blocks.index=[0]*len(Blocks) 

Infine, utilizzare la funzione di distanza Geopandas per calcolare la distanza dal più vicino ristorante per ogni baricentro Block.

Blocks['Distance']=Blocks.distance(RestMulti) 

Si prega di offrire qualche suggerimento su come qualsiasi aspetto di questo potrebbe essere migliorato. Non sono legato all'utilizzo di Geopandas o Shapely, ma sto cercando di imparare un'alternativa ad ArcPy.

Grazie per l'aiuto!

+0

determinare vicini più prossimi è un compito abbastanza straight-forward nel solo NumPy. vedere la fine di questa presentazione (https://speakerdeck.com/jakevdp/losing-your-loops-fast-numerical-computing-with-numpy-pycon-2015) per una soluzione pura-numpy confrontata con una funzione di apprendimento scikit . –

+0

Dovresti essere in grado di iterare i tuoi blocchi e quindi semplicemente calcolare la distanza per tutti i ristoranti in quel particolare blocco (usando la funzionalità di geopandas incorporata). Seleziona il minimo e sei pronto? Per ulteriori informazioni, potresti postare dove trovare gli shapefile sorgente? – shongololo

risposta

6

Se ho letto correttamente il problema, Blocchi e Ristoranti possono avere dimensioni molto diverse. Per questo motivo, probabilmente è un approccio sbagliato tentare di forzare in un formato tabella reindicando.

Vorrei semplicemente passare in rassegna i blocchi e ottenere la distanza minima dai ristoranti (proprio come suggeriva @shongololo).

Sto per essere leggermente più generale (perché ho già scritto questo codice) e fare una distanza da punti a linee, ma lo stesso codice dovrebbe funzionare da punti a punti o da poligoni a poligoni. Inizierò con un GeoDataFrame per i punti e creerò una nuova colonna che ha la distanza minima dalle linee.

%matplotlib inline 
import matplotlib.pyplot as plt 
import shapely.geometry as geom 
import numpy as np 
import pandas as pd 
import geopandas as gpd 

lines = gpd.GeoSeries(
    [geom.LineString(((1.4, 3), (0, 0))), 
     geom.LineString(((1.1, 2.), (0.1, 0.4))), 
     geom.LineString(((-0.1, 3.), (1, 2.)))]) 

# 10 points 
n = 10 
points = gpd.GeoSeries([geom.Point(x, y) for x, y in np.random.uniform(0, 3, (n, 2))]) 

# Put the points in a dataframe, with some other random column 
df_points = gpd.GeoDataFrame(np.array([points, np.random.randn(n)]).T) 
df_points.columns = ['Geometry', 'Property1'] 

points.plot() 
lines.plot() 

enter image description here

ora ottenere la distanza tra i punti di linee e solo salvare la distanza minima per ogni punto (vedi sotto per una versione con applicazione)

min_dist = np.empty(n) 
for i, point in enumerate(points): 
    min_dist[i] = np.min([point.distance(line) for line in lines]) 
df_points['min_dist_to_lines'] = min_dist 
df_points.head(3) 

che dà

Geometry          Property1 min_dist_to_lines 
0 POINT (0.2479424516236574 2.944916965334865) 2.621823 0.193293 
1 POINT (1.465768457667432 2.605673714922998)  0.6074484 0.226353 
2 POINT (2.831645235202689 1.125073838462032)  0.657191 1.940127 

---- EDIT ----

(tratta da un problema di github) Utilizzando apply è più bello e più coerente con quanto si farebbe in pandas:

def min_distance(point, lines): 
    return lines.distance(point).min() 

df_points['min_dist_to_lines'] = df_points.geometry.apply(min_distance, df_lines)