การเพิ่มประสิทธิภาพระยะทาง GeoPandas

Oct 15 2020

ฉันได้สร้างฟังก์ชันต่อไปนี้ที่วัดระยะทางจากจุดหนึ่งใน GDF ไปยังทุกจุดใน GDF อื่นและส่งคืนตารางกลับด้วยระยะทางที่สั้นที่สุดสำหรับแต่ละจุด มันทำงานได้ดีสำหรับจุดหนึ่ง แต่ฉันละเลยความจริงที่ว่าฉันมีตาราง 4000 คะแนนดังนั้นจึงใช้เวลา 10 นาที ฉันรันใน PostGIS แล้วและสามารถลงได้ไม่ถึงหนึ่งวินาที มีวิธีทำใน Python ที่ตรงกับความเร็ว PostGIS หรือไม่?

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

ตารางป้อนข้อมูลของฉันมีลักษณะเช่นนี้

และตารางผลลัพธ์จะเป็นแบบนี้

คำตอบ

3 martinfleis Oct 15 2020 at 17:07

สำหรับการดำเนินการเชิงพื้นที่ประเภทนี้คุณควรพยายามใช้ดัชนีเชิงพื้นที่เสมอ หากคุณสนใจในระยะทางที่น้อยที่สุดเท่านั้นสิ่งต่อไปนี้ควรเป็นตัวเลือกที่ค่อนข้างมีประสิทธิภาพ

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)

สำหรับ 1,000 ถึง 1,000 คะแนนนั้นน้อยกว่าหนึ่งวินาทีเมื่อเทียบกับเวลาประมาณหนึ่งนาทีของรหัสของยีน @

เพื่อให้มีประสิทธิภาพคุณควรคาดเดาinitial_bufferระยะทางที่คุณคิดว่าจะเป็นเพียงไม่กี่จุด หากไม่มีอยู่จะขยายบัฟเฟอร์จนกว่าจะกระทบบางส่วน

โดยทั่วไปหากคุณต้องการประสิทธิภาพที่ดีที่สุดจาก GeoPandas คุณควรใช้เวอร์ชันล่าสุด (รหัสนี้ต้องใช้ 0.8) และ pygeos อ้างอิงที่เป็นทางเลือก (https://geopandas.readthedocs.io/en/latest/getting_started/install.html#using-the-optional-pygeos-dependency) ซึ่งสามารถเร่งความเร็วโค้ดด้านบนตามลำดับขนาด

2 gene Oct 15 2020 at 15:22

การวนซ้ำแถวใน (Geo) DataFrame ใน (Geo) Pandas นั้นช้ามากโปรดดูแนวทางที่เหมาะสมที่สุดสำหรับการทำซ้ำบน DataFrameเช่น

การทำซ้ำใน Pandas เป็นการต่อต้านรูปแบบและเป็นสิ่งที่คุณควรทำเมื่อคุณใช้ตัวเลือกอื่น ๆ หมด ( วิธีการวนซ้ำแถวใน DataFrame ใน Pandas )

คุณสามารถลองใช้(Geo)DataFrame.apply()และหุ่นดี: จุดที่ใกล้ที่สุดเช่นเดียวกับในGeoPandas: ค้นหาจุดที่ใกล้ที่สุดในกรอบข้อมูลอื่นโดยไม่ต้องforวนซ้ำ (ดูความคิดเห็น)

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)

คำนวณระยะทาง

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