GeoPandas mesafe optimizasyonu
Bir GDF'deki bir noktadan başka bir GDF'deki tüm noktalara olan mesafeyi ölçen ve her nokta için en kısa mesafeyle bir tablo geri döndüren aşağıdaki işlevi oluşturdum. Bir noktada iyi çalışıyor ancak 4000 puanlık bir masam olduğu gerçeğini ihmal ettim ve bu yüzden 10 dakika sürüyor. PostGIS'de çalıştırdım ve bir saniyeden daha kısa bir süreye indirebilirim. Bunu Python'da yapmanın PostGIS hızıyla eşleşebilecek bir yolu var mı?
def get_distance_to(gdf_in, aoi_df, aoi):
dist_df_list = list()
for row in range(len(gdf_in)):
single_row = gdf_in.iloc[row]
distances = aoi_df.geometry.distance(single_row.geometry)
dist_list = distances.to_list()
closest_aoi = min(dist_list)
single_row["dist_to_"+aoi] = closest_aoi
df = single_row.to_frame().T
dist_df_list.append(df)
completed_distances = pd.concat(dist_df_list, ignore_index=True, sort=False)
return completed_distances
girdi tablolarım şuna benzer
ve çıktı tablosu şuna benzer
Yanıtlar
Bu tür herhangi bir uzamsal işlem için, her zaman uzamsal indeksi kullanmaya çalışmalısınız. Yalnızca minimum mesafeyle ilgileniyorsanız, aşağıdakiler size nispeten yüksek performanslı bir seçenek sunmalıdır.
import geopandas as gpd
from shapely.geometry import Point
import pandas as pd
import random
gdf = gpd.GeoDataFrame(geometry=[Point(random.randint(0, 1000), random.randint(0, 1000)) for _ in range(1000)])
gdf2 = gpd.GeoDataFrame(geometry=[Point(random.randint(0, 1000), random.randint(0, 1000)) for _ in range(1000)])
def get_nearest_distance(left, right, initial_buffer):
"""get distance from left to right"""
buffered = left.buffer(initial_buffer)
distances = []
for i in range(len(buffered)):
geom = buffered.geometry.iloc[i]
query = right.sindex.query(geom)
while query.size == 0:
query = right.sindex.query(geom.buffer(b))
b += initial_buffer
distances.append(right.iloc[query].distance(left.geometry.iloc[i]).min())
return pd.Series(distances, index=left.index)
gdf['distance_to_x'] = get_nearest_distance(gdf, gdf2, 50)
1000 ila 1000 nokta için, yaklaşık bir dakika @ gen kodunun sürmesine kıyasla bir saniyeden daha azdır.
Verimli hale getirmek için, initial_buffersadece birkaç nokta olacağını düşündüğünüz mesafeyi tahmin etmelisiniz . Hiçbiri girilmezse, arabelleği bir miktar çarpana kadar genişler.
Genel olarak, GeoPandas'tan en iyi performansı almak istiyorsanız, en son sürümü (bu kod 0.8 gerektirir) ve isteğe bağlı bağımlılık pygeos'u (https://geopandas.readthedocs.io/en/latest/getting_started/install.html#using-the-optional-pygeos-dependency), yukarıdaki kodu büyüklük sırasına göre hızlandırabilir.
(Geo) Pandalarda (Geo) DataFrame'deki satırları yineleyin çok yavaştır, örneğin DataFrame üzerinde yineleme için optimum yaklaşım konusuna bakın
Pandalarda yineleme bir anti-modeldir ve yalnızca diğer tüm seçenekleri tükettiğinizde yapmanız gereken bir şeydir. ( Pandalar'da bir DataFrame'deki satırlar üzerinde yineleme nasıl yapılır )
Kullanmayı (Geo)DataFrame.apply()ve biçimli bir şekilde kullanmayı deneyebilirsiniz : GeoPandalarda olduğu gibi en yakın nokta: Yineleme olmadan diğer veri çerçevesindeki en yakın noktayı bulunfor (açıklamaya bakın)
import geopandas as gpd
from shapely.geometry import Point
from shapely.ops import nearest_points
gpd1 = gpd.read_file("point1.shp") # red points
gpd2 = gpd.read_file("point2.shp") # blue points
pts3 = gpd2.geometry.unary_union
def near(point, pts=pts3):
# find the nearest point and return the corresponding value
nearest = gpd2.geometry == nearest_points(point, pts)[1]
return gpd2[nearest].id.values[0],gpd2[nearest].geometry.values[0]
gpd1['Nearest'] = gpd1.apply(lambda row: near(row.geometry)[0], axis=1)
gpd1['geom2'] = gpd1.apply(lambda row: near(row.geometry)[1], axis=1)
print(gpd1)
id geometry Nearest geom2
0 1 POINT (-0.99013 0.48096) 3 POINT (-0.77574 0.64739)
1 2 POINT (-1.00987 0.08039) 4 POINT (-0.73060 0.10860)
2 3 POINT (-0.71932 -0.13117) 5 POINT (-0.57827 -0.08039)
3 4 POINT (-0.90268 -0.28914) 5 POINT (-0.57827 -0.08039)
Mesafeyi hesaplayın
gpd1['distance'] = gpd1.apply(lambda row: row.geometry.distance(row.geom2), axis=1)
gpd1.drop('geom2', axis=1, inplace=True)
print(gpd1)
id geometry Nearest distance
0 1 POINT (-0.99013 0.48096) 3 0.271406
1 2 POINT (-1.00987 0.08039) 4 0.280688
2 3 POINT (-0.71932 -0.13117) 5 0.149905
3 4 POINT (-0.90268 -0.28914) 5 0.385759