2015-06-09 11 views
5

Szukam odpowiednika ArcPy Generate Near Table za pomocą Geopandas/Shapely. Jestem bardzo nowy w Geopandas i Shapely i opracowałem metodologię, która działa, ale zastanawiam się, czy jest na to skuteczniejszy sposób.Oblicz odległość do najbliższej funkcji za pomocą Geopandas

Mam dwa pliki danych punktowych - Census Block Centroids i restauracje. Poszukuję, dla każdego centroida Census Block, odległości do najbliższej restauracji. Nie ma żadnych ograniczeń dotyczących tej samej restauracji, która jest najbliższą restauracją dla wielu bloków.

Powodem, dla mnie staje się to nieco bardziej skomplikowane, ponieważ Geopandas Distance function oblicza elementwise, dopasowując na podstawie indeksu. Dlatego moją ogólną metodologią jest przekształcenie pliku restauracji w plik wielopunktowy, a następnie ustawienie indeksu pliku bloków tak, aby wszystkie miały tę samą wartość. Następnie wszystkie centroidy blokowe i restauracje mają tę samą wartość indeksu.

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

teraz przeczytać w Shapefiles Blok ciężkości i restauracji:

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

Ponieważ funkcja odległość Geopandas oblicza odległość elementwise, przekonwertować GeoSeries restauracja jest GeoSeries MultiPoint:

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

Następnie ustawiam indeks dla bloków równy 0 (ta sama wartość, co Restauracje wielopunktowe), jako operację dla obliczeń elementarnych.

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

Na koniec używam funkcji odległości geograficznej, aby obliczyć odległość do najbliższej restauracji dla każdego centroidu bloku.

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

Proszę zaproponować wszelkie sugestie dotyczące poprawy tego aspektu. Nie jestem związany z używaniem Geopandas lub Shapely, ale szukam sposobu na poznanie alternatywy dla ArcPy.

Dzięki za pomoc!

+0

ustalania najbliższych sąsiadów jest dość prosta zadanie w sam numpy. zobacz koniec tej prezentacji (https://speakerdeck.com/jakevdp/losing-your-loops-fast-numerical-computing-with-numpy-pycon-2015) dla sprawdzenia czystego numpy w połączeniu z funkcją uczenia się naukowego . –

+0

Powinieneś być w stanie powtórzyć swoje bloki, a następnie po prostu obliczyć odległość dla wszystkich restauracji do tego konkretnego bloku (przy użyciu wbudowanej funkcji geopandas). Wybierz minimum i gotowe? Aby uzyskać dodatkową pomoc, możesz opublikować, gdzie można znaleźć pliki shapefile? – shongololo

Odpowiedz

6

Jeśli dobrze rozumiem Twój problem, bloki i restauracje mogą mieć bardzo różne wymiary. Z tego powodu prawdopodobnie jest to złe podejście, aby spróbować wymusić na formacie tabeli przez ponowne indeksowanie.

Chciałbym po prostu zapętlić bloki i uzyskać minimalną odległość do restauracji (tak jak sugerował @shongololo).

Będę trochę bardziej ogólny (ponieważ już ten kod został napisany) i zrobię odległość od punktów do linii, ale ten sam kod powinien działać od punktów do punktów lub od wielokątów do wielokątów. Zacznę od GeoDataFrame dla punktów i utworzę nową kolumnę, która ma minimalną odległość do linii.

%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

Teraz dostać odległość od punktu do linii i tylko zaoszczędzić minimalną odległość dla każdego punktu (patrz poniżej dla wersji z zastosowania)

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) 

co daje

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 

---- EDYCJA ----

(zaczerpnięte z emisji github) Korzystanie apply jest ładniejszy i bardziej zgodne z tym, jak chcesz to zrobić w 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) 
Powiązane problemy