如何删除X和Y坐标在多边形之外的数据框行

时间:2018-02-09 15:48:49

标签: python-3.x pandas dataframe geospatial point-in-polygon

我正在努力解决以下问题。让我们假设一个数据帧(从txt文件加载)具有以下结构(以及数千行):

filter = ['Contract A full', 'Contract B full']
foo.head()

数据代表X Y和Z坐标。

我还有一组定义闭合多边形的点。这些是一个numpy数组:

         X            Y       Z 
 0  125417.5112  536361.8752 -1750.0
 1  127517.7647  533925.8644 -1750.0
 2  128144.1000  533199.4000 -1750.0
 3  128578.8385  532904.9288 -1750.0
 4  125417.5112  536361.8752 -1750.0
 ....

如何过滤我的数据框以删除不属于闭合多边形的行?

我尝试使用polypoints array([[ 125417.5112, 536361.8752], [ 127517.7647, 533925.8644], [ 128144.1 , 533199.4 ], .... [ 125417.5112, 536361.8752]]) shapely.geometry定义多边形。通过做:

polygon

这很好用。但我不知道如何继续这样做。

非常感谢帮助

---- ---- EDIT 请参阅下面的更新解决方案

3 个答案:

答案 0 :(得分:2)

我对shapely不太熟悉。也许他们有真正的熊猫支持。 Afaik,他们支持矢量化的numpy功能,所以我不会感到惊讶 找出哪个点在给定多边形内的一种方法是使用pandas apply()函数:

import pandas as pd
from shapely.geometry import Polygon, Point
#your dataframe of points
df = pd.DataFrame([[0, 0, 0], [1, 2, 3], [2, 2, 2], [3, 2, 1] ], columns = list("XYZ"))
#your polygon points
polygon1_list = [(1, 1), (1, 3), (3, 3), (3, 1)]
#adding a column that contains a boolean variable for each point
df["polygon1"] = df.apply(lambda row: Polygon(polygon1_list).contains(Point(row["X"], row["Y"])), axis = 1)
print(df)

我的玩具数据集的输出

   X  Y  Z  polygon1
0  0  0  0   False
1  1  2  3   False
2  2  2  2    True
3  3  2  1   False

在形状上,contains实际上意味着在多边形内,这排除了边界。如果要包含边框,则应使用intersects

df["polygon1"] = df.apply(lambda row: Polygon(polygon1_list).intersects(Point(row["X"], row["Y"])), axis = 1)

现在你的问题的答案很简单。只需在此新列中删除包含False的行:

df = df.drop(df[~df["polygon1"]].index)

不幸的是,您仍然必须遍历多边形列表。如果有人知道某种方式,如何在没有(显式)循环的情况下测试所有点和所有多边形将会很有趣。我已经看到了一个MultiPolygon构造函数类on their website,所以也许在一个类中组合所有多边形就可以了。但提前测试这是一个有效的选择。如果MultiPolygon的成员沿着一条线触及无数个点,则无效。

编辑:貌似,在Python 2.7中,这不起作用。 See akozi's answer for a 2.7 compatible answer.

答案 1 :(得分:2)

@MrT建议的原始解决方案效果很好。然而,正如@Rutger Kassies所建议的那样,我也看到了地图,我也找到了另一个解决方案。首先需要安装geopandas包。然后以下代码适用于我:

import geopandas as gpd
from shapely.geometry import Point, Polygon, MultiPolygon
# load the data that should be cropped by the polygon
# this assumes that the csv file already includes 
# a geometry column with point data as performed below
dat_gpd = gpd.GeoDataFrame.from_csv(r'{}\data_to_crop.csv'.format(savedir), sep='\t')

# load the data of the polygon as a dataframe
arr_df = pd.DataFrame(data, columns=['X','Y','Z'])

# make shapely points out of the X and Y coordinates
point_data = [Point(xy) for xy in zip(arr_df.X, arr_df.Y)]

# assign shapely points as geometry to a geodataframe
# Like this you can also inspect the individual points if needed
arr_gpd = gpd.GeoDataFrame(arr_df, geometry=point_data)

# define a shapely polygon from X and Y coordinates of the shapely points
polygo = Polygon([[p.x, p.y] for p in arr_gpd.geometry])

# assing defined polygon to a new dataframe
pol_gpd= gpd.GeoDataFrame()
pol_gpd['geometry'] = None
pol_gpd.loc[0,'geometry'] = polygo

# define a new dataframe from the spatial join of the dataframe with the data to be cropped
# and the dataframe with the polygon data, using the within function.
dat_fin = gpd.sjoin(dat_gpd, pol_gpd, op = 'within')

希望如果有人遇到类似的问题,这会有所帮助。此外,可以找到有关空间连接的更多信息on the geopandas website。请注意,此功能不需要在多边形之间进行操作,但也可以使用点和多边形

- 编辑 -

%timeit gpd.sjoin(dat_gpd, pol_gpd, op = 'within')
31.8 s ± 108 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dat_gpd['inpoly'] = dat_gpd.apply(lambda row: polygo.intersects(Point(row["X"], row["Y"])), axis = 1)
1min 26s ± 389 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

看起来geo-pandas功能要快得多。虽然公平地认为非地理熊猫解决方案还必须将X和Y转换为匀称点元素,然后执行交叉点评估

答案 2 :(得分:1)

我很难模仿Python 2.7中建议的exact solution Mr T。因此,这是我必须在Python 2.7中使用时的微小区别。

from shaply.geometry.polygon import Polygon
inside = Polygon(poly_points).contains_points(zip(df.X.values, df.Y.values))
df['inside'] = inside
df = df.drop(df[~df['inside']].index)

似乎旧版本的contains_points很难在单个点上运行。因此,我将其设置为读取所有要点,并将该列表附加为新列。