同时在点几何和其他列上合并 GeoDataFrames
Merging GeoDataFrames on both Point geometry and other columns at the same time
如何同时在 Point
几何和任意其他列上合并两个 GeoDataFrame
?我意识到这个任务对于除 Point
以外的所有其他几何图形都是不明确的,因为 "equality" 没有很好地定义直线和多边形,但仍然如此。
如果我简单地尝试 gdf2.merge(gdf)
,下面的 MWE 会抛出一个错误,正确地抱怨
unhashable type: 'Point'.
如何解决这个问题?
import geopandas as gpd
import pandas as pd
from io import StringIO
import shapely
df = pd.read_csv(StringIO('''
Name Value x y
'a' 1.5 0. 0.
'b' 22 0. 1.
'c' 0.2 0. 1.
'''),sep=r"\s*",engine='python')
df2 = pd.read_csv(StringIO('''
Name OtherValue x y
'a' 9.9 0. 0.
'b' 4.5 0. 1.
'c' 2e3 1. 1.
'''),sep=r"\s*",engine='python')
def dataframe_to_geodataframe(df):
geometry = [shapely.geometry.Point(xy) for xy in zip(df.x, df.y)]
df = df.drop(['x','y'], axis=1)
gdf = gpd.GeoDataFrame(df, geometry=geometry)
return gdf
gdf = dataframe_to_geodataframe(df)
gdf2 = dataframe_to_geodataframe(df2)
gdf.merge(gdf2,how='left')
理想情况下输出类似于
Name Value geometry OtherValue
0 'a' 1.5 POINT (0 0) 9.9
1 'b' 22.0 POINT (0 1) 4.5
2 'c' 0.2 POINT (0 1) NaN
(当然取决于how
关键字)。
(我确实意识到在转换回普通 pandas DataFrames 后可以轻松完成此操作,但我觉得应该有一种无需来回转换即可完成此操作的方法。)
一种(可能是肮脏的)方法是通过扩展 class shapely.geometry.Point
:
使 Point
可散列
class HPoint(shapely.geometry.Point):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __hash__(self):
return hash(tuple(self.coords))
这是基于 Point
的相等运算符(通过父 class BaseGeometry
提供)只是 compares 坐标元组这一事实。
那么您可以将此 class 用作:
def dataframe_to_geodataframe(df):
geometry = [HPoint(xy) for xy in zip(df.x, df.y)]
df = df.drop(['x','y'], axis=1)
gdf = gpd.GeoDataFrame(df, geometry=geometry)
return gdf
gdf = dataframe_to_geodataframe(df)
gdf2 = dataframe_to_geodataframe(df2)
print(gdf2.merge(gdf, how='right'))
产生:
Name OtherValue geometry Value
0 'a' 9.9 POINT (0 0) 1.5
1 'b' 4.5 POINT (0 1) 22.0
2 'c' NaN POINT (0 1) 0.2
如何同时在 Point
几何和任意其他列上合并两个 GeoDataFrame
?我意识到这个任务对于除 Point
以外的所有其他几何图形都是不明确的,因为 "equality" 没有很好地定义直线和多边形,但仍然如此。
如果我简单地尝试 gdf2.merge(gdf)
,下面的 MWE 会抛出一个错误,正确地抱怨
unhashable type: 'Point'.
如何解决这个问题?
import geopandas as gpd
import pandas as pd
from io import StringIO
import shapely
df = pd.read_csv(StringIO('''
Name Value x y
'a' 1.5 0. 0.
'b' 22 0. 1.
'c' 0.2 0. 1.
'''),sep=r"\s*",engine='python')
df2 = pd.read_csv(StringIO('''
Name OtherValue x y
'a' 9.9 0. 0.
'b' 4.5 0. 1.
'c' 2e3 1. 1.
'''),sep=r"\s*",engine='python')
def dataframe_to_geodataframe(df):
geometry = [shapely.geometry.Point(xy) for xy in zip(df.x, df.y)]
df = df.drop(['x','y'], axis=1)
gdf = gpd.GeoDataFrame(df, geometry=geometry)
return gdf
gdf = dataframe_to_geodataframe(df)
gdf2 = dataframe_to_geodataframe(df2)
gdf.merge(gdf2,how='left')
理想情况下输出类似于
Name Value geometry OtherValue
0 'a' 1.5 POINT (0 0) 9.9
1 'b' 22.0 POINT (0 1) 4.5
2 'c' 0.2 POINT (0 1) NaN
(当然取决于how
关键字)。
(我确实意识到在转换回普通 pandas DataFrames 后可以轻松完成此操作,但我觉得应该有一种无需来回转换即可完成此操作的方法。)
一种(可能是肮脏的)方法是通过扩展 class shapely.geometry.Point
:
Point
可散列
class HPoint(shapely.geometry.Point):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __hash__(self):
return hash(tuple(self.coords))
这是基于 Point
的相等运算符(通过父 class BaseGeometry
提供)只是 compares 坐标元组这一事实。
那么您可以将此 class 用作:
def dataframe_to_geodataframe(df):
geometry = [HPoint(xy) for xy in zip(df.x, df.y)]
df = df.drop(['x','y'], axis=1)
gdf = gpd.GeoDataFrame(df, geometry=geometry)
return gdf
gdf = dataframe_to_geodataframe(df)
gdf2 = dataframe_to_geodataframe(df2)
print(gdf2.merge(gdf, how='right'))
产生:
Name OtherValue geometry Value
0 'a' 9.9 POINT (0 0) 1.5
1 'b' 4.5 POINT (0 1) 22.0
2 'c' NaN POINT (0 1) 0.2