我正在努力解决以下问题。让我们假设一个数据帧(从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 请参阅下面的更新解决方案
答案 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很难在单个点上运行。因此,我将其设置为读取所有要点,并将该列表附加为新列。