amq*_*ack 7 python shapely geopandas
这里有很多关于有效匹配多边形中的点的问题(示例:Here和Here)。这些中感兴趣的主要变量是大量的点 N 和多边形顶点的数量 V。这些都很好且有用,但我正在查看大量的点 N 和多边形 G。这也意味着我的输出将有所不同(我主要看到的输出由落在多边形内的点组成,但在这里我想知道附加到点的多边形)。
\n我有一个包含大量多边形(数十万)的形状文件。多边形可以接触,但它们之间几乎没有重叠(内部的任何重叠都可能是错误的结果 - 想想人口普查区块组)。我还有一个包含点(数百万)的 csv,我想根据点所在的多边形(如果有)对这些点进行分类。有些可能不会落入多边形(继续我的示例,想想海洋上的点)。下面我设置了一个玩具示例来研究这个问题。
\n设置:
\nimport numpy as np\nfrom shapely.geometry import shape, MultiPolygon, Point, Polygon\nimport geopandas as gpd\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom shapely.strtree import STRtree\n\n#setup:\nnp.random.seed(12345)\n\n# shape gridsize:\ngridsize=10\navgpointspergridspace=10 #point density\nRun Code Online (Sandbox Code Playgroud)\n创建多边形的地理数据框(模拟使用 geopandas 导入的 shapefile):
\n# creating a geodataframe (shapefile imported via geopandas):\ngarr=np.empty((gridsize,gridsize),dtype=object)\nfor i in range(gridsize):\n for j in range(gridsize):\n garr[i,j]=Point(i,j)\n\n# polygons:\npoly_list=[]\nfor i in range(gridsize-1):\n for j in range(gridsize-1):\n temp_points=[garr[i,j],garr[i,j+1],garr[i+1,j+1],garr[i+1,j],garr[i,j]]\n poly=Polygon([[p.x,p.y] for p in temp_points])\n poly_list+=[poly]\n\n# creating a geodataframe, including some additional numeric and string variables:\ngdf=gpd.GeoDataFrame()\ngdf[\'geometry\']= poly_list\ngdf[\'id\']=list(range(len(gdf[\'geometry\'])))\ngdf[\'numeric\']=0\ngdf[\'string\']=\'foo\'\n\n# creating some holes in the grid:\ngdf[\'included\']=[np.random.choice([True,False],p=[.95,.05]) for x in range(len(gdf))]\ngdf_polys=gdf[gdf[\'included\']]\nRun Code Online (Sandbox Code Playgroud)\n生成点(模拟通过 pandas 导入的 csv):
\n# creating a pandas dataframe with points (csv of coordinates imported to pandas):\nnpoints=(gridsize+2)**2*10\nfgridend=gridsize+1\nfgridstart=-1\n\nxlist=[]\nylist=[]\npoints=[]\nfor i in range(npoints):\n x=fgridstart+np.random.random()*fgridend\n y=fgridstart+np.random.random()*fgridend\n xlist+=[x]\n ylist+=[y]\n\ndf=pd.DataFrame(list(zip(xlist,ylist)),columns=[\'x\',\'y\'])\n\ncoords=[Point(xy) for xy in zip(df[\'x\'],df[\'y\'])]\n\ngdf_points=gpd.GeoDataFrame(df,geometry=coords)\nRun Code Online (Sandbox Code Playgroud)\n绘制结果:
\nfig, ax = plt.subplots(figsize=[10,10])\ngdf_polys.plot(ax=ax,facecolor=\'gray\',alpha=.2,edgecolor=\'black\',lw=2)\ngdf_points.plot(ax=ax)\nRun Code Online (Sandbox Code Playgroud)\n返回:
\n\n我现在想要将点与多边形匹配。因此,所需的输出将是一个附加列,其中gdf_points包含与该点关联的多边形的标识符(使用该gdf_polys[\'id\']列)。我的代码非常慢,但产生正确的结果如下:
def id_gen(row):\n point=row[\'geometry\']\n out=0\n for i,poly in shapes_list:\n if poly.contains(point):\n out=i\n break\n return out\n \n#shapes_list=gdf_polys[\'geometry\']\nshapes_list=[(gdf_polys[\'id\'].iloc[i],gdf_polys[\'geometry\'].iloc[i]) for i in range(len(gdf_polys[\'geometry\']))]\npoint_list=[]\ngdf_points[\'poly\']=gdf_points.apply(id_gen,axis=1)\nRun Code Online (Sandbox Code Playgroud)\n返回:
\n x y geometry poly\n0 4.865555 1.777419 POINT (4.86555 1.77742) 37\n1 6.929483 3.041826 POINT (6.92948 3.04183) 57\n2 4.485133 1.492326 POINT (4.48513 1.49233) 37\n3 2.889222 6.159370 POINT (2.88922 6.15937) 24\n4 2.442262 7.456090 POINT (2.44226 7.45609) 25\n... ... ... ... ...\n1435 6.414556 5.254309 POINT (6.41456 5.25431) 59\n1436 6.409027 4.454615 POINT (6.40903 4.45461) 58\n1437 5.763154 2.770337 POINT (5.76315 2.77034) 47\n1438 9.613874 1.371165 POINT (9.61387 1.37116) 0\n1439 6.013953 3.622011 POINT (6.01395 3.62201) 57\n1440 rows \xc3\x97 4 columns\nRun Code Online (Sandbox Code Playgroud)\n我应该注意到,实际的形状文件将具有比该网格复杂得多的形状。我认为有几个地方可以加快速度:
\n开始基准测试:\n网格大小为 10,点密度为 10(1440 点):花费约 180ms\n网格大小为 20,点密度为 10(4840 点):花费约 2.8s\n网格大小为30,点密度为 10(10240 点):花费了大约 12.8 秒\n网格大小为 50,点密度为 10(27040 点):花费了大约 1.5 分钟\n所以我们可以看到这个比例很差。
\ngeopandas 没有将其视为多边形中的质量点,而是有一种在这里有用的空间连接方法。它实际上相当快,至少在这个玩具示例中似乎并没有受到多边形数量的影响(不过我不能排除这可能是由于这些多边形的简单性)。
空间连接采用两个地理数据帧并将它们合并在一起。在本例中,我希望将多边形的属性附加到位于其中的点。所以我的代码如下所示:
joined=gpd.sjoin(gdf_points,gdf_polys,how='left',op='within')
Run Code Online (Sandbox Code Playgroud)
返回:
x y geometry poly index_right id numeric string included
0 18.651358 26.920261 POINT (18.65136 26.92026) 908 908.0 908.0 0.0 foo True
1 38.577101 1.505424 POINT (38.57710 1.50542) 1863 1863.0 1863.0 0.0 foo True
2 15.430436 15.543219 POINT (15.43044 15.54322) 750 750.0 750.0 0.0 foo True
3 44.928141 7.726345 POINT (44.92814 7.72635) 2163 2163.0 2163.0 0.0 foo True
4 34.259632 5.373809 POINT (34.25963 5.37381) 1671 1671.0 1671.0 0.0 foo True
... ... ... ... ... ... ... ... ... ...
27035 32.386086 23.440186 POINT (32.38609 23.44019) 1591 1591.0 1591.0 0.0 foo True
27036 7.569414 1.836633 POINT (7.56941 1.83663) 344 344.0 344.0 0.0 foo True
27037 1.141440 34.739388 POINT (1.14144 34.73939) 83 83.0 83.0 0.0 foo True
27038 -0.770784 14.027607 POINT (-0.77078 14.02761) 0 NaN NaN NaN NaN NaN
27039 12.695803 33.405048 POINT (12.69580 33.40505) 621 621.0 621.0 0.0 foo True
Run Code Online (Sandbox Code Playgroud)
与我最初的代码相比,这非常快。运行我测试的最大大小(27k 点)需要不到 60 毫秒(与之前的代码相比,需要 1.5 分钟)。扩展到我的一些实际工作中,100 万个点只需要 13 秒多一点就可以匹配到不到 20 万个多边形,其中大多数多边形比我的玩具示例中使用的几何形状复杂得多。这似乎是一种易于管理的方法,但我有兴趣学习进一步提高效率的方法。