การเพิ่มประสิทธิภาพระยะทาง GeoPandas
ฉันได้สร้างฟังก์ชันต่อไปนี้ที่วัดระยะทางจากจุดหนึ่งใน 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
ตารางป้อนข้อมูลของฉันมีลักษณะเช่นนี้
และตารางผลลัพธ์จะเป็นแบบนี้
คำตอบ
สำหรับการดำเนินการเชิงพื้นที่ประเภทนี้คุณควรพยายามใช้ดัชนีเชิงพื้นที่เสมอ หากคุณสนใจในระยะทางที่น้อยที่สุดเท่านั้นสิ่งต่อไปนี้ควรเป็นตัวเลือกที่ค่อนข้างมีประสิทธิภาพ
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) ซึ่งสามารถเร่งความเร็วโค้ดด้านบนตามลำดับขนาด
การวนซ้ำแถวใน (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