创建 GeoDataFrame 时什么时候需要使用 GeoSeries,什么时候列表就足够了?

When do I need to use a GeoSeries when creating a GeoDataFrame, and when is a list enough?

import geopandas as gpd
import matplotlib.pyplot as plt
from shapely.geometry import Polygon, Point
import numpy as np

我定义了一个多边形:

polygon = Polygon([(0,0),(0,1),(1,1),(1,0)])

并创建一个随机点列表:

np.random.seed(42)
points = [Point([np.random.uniform(low=-1,high=1),
                 np.random.uniform(low=-1,high=1)]) for _ in range(1000)]

我想知道多边形内有哪些点。我通过首先将 points list 转换为 GeoSeries:

来创建一个带有名为 points 的列的 GeoDataFrame
gdf = gpd.GeoDataFrame(dict(points=gpd.GeoSeries(points)))

然后简单地做:

gdf.points.within(polygon)

其中 returns 个 pandas.core.series.Series 布尔值,表示哪些点在多边形内。

但是,如果我不从 list 而不是 GeoSeries 对象创建 GeoDataFrame:

gdf = gpd.GeoDataFrame(dict(points=points))

然后做:

gdf.points.within(polygon)

我得到:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-171-831eddc859a1> in <module>()
----> 1 gdf.points.within(polygon)

/usr/local/lib/python3.7/dist-packages/pandas/core/generic.py in __getattr__(self, name)
   5485         ):
   5486             return self[name]
-> 5487         return object.__getattribute__(self, name)
   5488 
   5489     def __setattr__(self, name: str, value) -> None:

AttributeError: 'Series' object has no attribute 'within'

geopandas.GeoDataFrame 页面上给出的示例中,GeoDataFrame 是从 list 创建的,而不是 shapely.geometry.Point 个对象的 GeoSeries

from shapely.geometry import Point
d = {'col1': ['name1', 'name2'], 'geometry': [Point(1, 2), Point(2, 1)]}
gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")

我什么时候需要先将我的 list 转换为 GeoSeries,以及什么时候可以在创建 [=19= 时将它们保留为 list ]s?

the docs for geopandas.GeoDataFrame 上,在你的示例中,有一个小注释:

Notice that the inferred dtype of ‘geometry’ columns is geometry.

可以看到here,也可以自己观察一下:

>>> import geopandas as gpd

>>> gpd.GeoDataFrame({'geometry': [Point(0,0)]}).dtypes
geometry    geometry
dtype: object

>>> gpd.GeoDataFrame({'geometryXXX': [Point(0,0)]}).dtypes
geometryXXX    object
dtype: object

来自 the docs for geopandas.GeoSeries:

A Series object designed to store shapely geometry objects.

...所以它会尝试将其创建的对象转换为 geometry dtype 是有道理的。事实上,当您尝试使用 non-shapely 个对象创建 GeoSeries 时,您会收到警告:

>>> gpd.GeoSeries([1,2,3])
<ipython-input-53-ca5248fcdaf8>:1: FutureWarning:     You are passing non-geometry data to the GeoSeries constructor. Currently,
    it falls back to returning a pandas Series. But in the future, we will start
    to raise a TypeError instead.
  gpd.GeoSeries([1,2,3])

...正如警告所说,将来会成为错误。


由于您不是创建 GeoSeries 对象(您使用的是列表),并且由于 列不是 geometryGeoDataFrame 使其 dtype 成为最通用的数据类型,它可以将其中的对象转换为 - object。因此,由于该列是数据类型 object 而不是 geometry,因此您不能调用 geometry 特定的方法,例如 within.

如果您需要使用列表,您有两个简单的选择。

方法1.将geometry=关键字参数传递给GeoDataFrame()

>>> gdf = gpd.GeoDataFrame({'points': [Point(0,0), Point(0,1)]}, geometry='points')
>>> gdf['points'].dtypes
<geopandas.array.GeometryDtype at 0x12882a1c0>
>>> gdf['points'].within
<bound method GeoPandasBase.within of 0    POINT (0.00000 0.00000)
1    POINT (0.00000 1.00000)
Name: points, dtype: geometry>

方法 2. 使用 astype 就像处理普通数据帧一样:

>>> gdf = gpd.GeoDataFrame({'points': [Point(0,0), Point(0,1)]})
>>> gdf['points'].dtype
dtype('O')
>>> gdf['points'].within
...
AttributeError: 'Series' object has no attribute 'within'

>>> gdf['points'] = gdf['points'].astype('geometry')
>>> gdf['points'].dtype
<geopandas.array.GeometryDtype at 0x122189e20>
>>> gdf['points'].within
<bound method GeoPandasBase.within of 0    POINT (0.00000 0.00000)
1    POINT (0.00000 1.00000)
Name: points, dtype: geometry>