GeoPandas mesafe optimizasyonu

Oct 15 2020

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

3 martinfleis Oct 15 2020 at 17:07

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.

2 gene Oct 15 2020 at 15:22

(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