Python объединяет/соединяет два фрейма данных по геометрическому условию

#python #pandas #performance #geopandas

Вопрос:

У меня есть рамка геоданных точек df_points (~50 тыс.) и рамка геоданных полигонов df_polygons (~50 тыс.).

Я хочу объединить 2 кадра данных , сохранив столбцы df_points , и сопоставить столбцы при df_polygons условии, что точка присутствует в многоугольнике.

 import geopandas as gpd
from shapely.geometry import Point, Polygon

_polygons = [ Polygon([(5, 5), (5, 13), (13, 13), (13, 5)]), Polygon([(10, 10), (10, 15), (15, 15), (15, 10)]) ]
_pnts = [Point(3, 3), Point(8, 8), Point(11, 11)]
df_polygons = gpd.GeoDataFrame(geometry=_polygons, index=['foo', 'bar']).reset_index()
df_points = gpd.GeoDataFrame(geometry=_pnts, index=['A', 'B', 'C']).reset_index()
 

df_points выглядит как:

 > df_points
    index   geometry
0   A       POINT (3.00000 3.00000)
1   B       POINT (8.00000 8.00000)
2   C       POINT (11.00000 11.00000)
 

df_polygons выглядит как:

 > df_polygons
    index   geometry
0   foo     POLYGON ((5.00000 5.00000, 5.00000 13.00000, 1...
1   bar     POLYGON ((10.00000 10.00000, 10.00000 15.00000...
 

результат может выглядеть так:

     index   geometry_points            geometry_index   geometry_polygons
0   A       POINT (3.00000 3.00000)    []               []
1   B       POINT (8.00000 8.00000)    ['foo']          [Polygon([(5, 5), (5, 13), (13, 13), (13, 5)])]
2   C       POINT (11.00000 11.00000)  ['foo','bar']    [Polygon([(5, 5), (5, 13), (13, 13), (13, 5)]), Polygon([(10, 10), (10, 15), (15, 15), (15, 10)]]
 

Есть ли в любом случае возможность эффективного объединения кадров данных?

Ответ №1:

Используйте пространственное соединение ( gpd.sjoin ):

 # Rename 'index' columns to avoid FutureWarning
dfp = df_points.rename(columns={'index': 'point'})
dfa = df_polygons.rename(columns={'index': 'area'})

# Find points within polygons
out = gpd.sjoin(dfp, dfa, how='inner', op='within')

# Reduce rows
out = out.groupby('point') 
         .agg({'area': lambda x: x.tolist() if x.any() else [],
               'index_right': lambda x: dfa.loc[x, 'geometry'].tolist()
                                            if ~x.all() else []}) 
         .reset_index()

# Append columns
dfp = dfp.merge(out, on='point')
 

Выход:

 >>> dfp
  point                   geometry        area                                        index_right
0     A    POINT (3.00000 3.00000)          []                                                 []
1     B    POINT (8.00000 8.00000)       [foo]          [POLYGON ((5 5, 5 13, 13 13, 13 5, 5 5))]
2     C  POINT (11.00000 11.00000)  [foo, bar]  [POLYGON ((5 5, 5 13, 13 13, 13 5, 5 5)), POLY...