Pytorch-Geometric & Geo-Pandas 框架学习指南
GeoPandas
GeoPandas 是一个开源项目,旨在让使用 python 处理地理空间数据变得更容易。GeoPandas 扩展了 pandas 使用的数据类型,允许对几何类型数据进行操作。其中相关的几何操作由 shapely 执行。此外,Geopandas 还依赖 fiona 进行文件访问,依赖 matplotlib 进行绘图。
Shapely 是基于笛卡尔坐标的几何对象操作和分析的Python库,底层基于GEOS和JTS库。Shapely不关心数据格式或坐标系,但可以很容易地与这些文件包集成。
文件读写
假设你有一个包含数据和几何图形的文件(如 GeoPackage、GeoJSON、Shapefile),您可以使用 geopandas.read_file() 读取,GeoPandas会自动检测文件类型并创建一个 GeoDataFrame。
例如本文中我们使用 nybb 数据集,它含有纽约各区的地图,可通过 geodatasets 库获取,使用 geodatasets.get_path() 下载数据集,并获取本地副本的路径。
1 | import geopandas |
Excel 文件
基本操作
计算面积
可以通过 GeoDataFrame.area 属性得到每个多边形(polygon or MultiPolygon)的面积,返回 pandas.Series 数据。
1 | # 设置索引,便于直观显示面积大小 |
边界、质心与距离
利用 GeoDataFrame.boundary 属性获取每个图形的边界:
1 | gdf["boundary"] = gdf.boundary |
值得注意的是,当我们把边界也保存为一个新的一列(column) 时,同一个 GeoDataFrame 中就存在了两个 column,一个 GeoDataFrame 只有一个 column 处于激活(active)状态,激活的 column 可以用于绘制。
通过 GeoDataFrame.centroid 获取每个图形的质心:
1 | gdf["centroid"] = gdf.centroid |
通过调用 GeoDataFrame.distance() 计算质心到指定点的距离:
1 | first_point = gdf["centroid"].iloc[0] #以第一行的点为例 |
因为 geopandas.GeoDataFrame 是数据类型 pandas.DataFrame 的子类,所以其相关属性和方法同样适用于这里。比如我们来计算上面得到的所有距离的平均值:
1 | gdf["distance"].mean() |
判断点面关系
1 | one_geometry.contains(Point([X,Y]) |
如果要批量判断由点构成的 GeoDataFrame 和由几何体构成的 GeoDataFrame 之间的点面关系(点是否在面的内部),参考本文【空间连接-点面关系Plus】一节。
绘制地图
GeoPandas 自然还可以绘制地图,从而使我们可以轻松查看地图在画布上效果。调用 GeoDataFrame.plot()。当需要按某一列的数值进行颜色编码时,则将该列作为第一个参数传入。
在下面的示例中,我们绘制了当前已激活的地图,并按 area 这一列进行了颜色编码,并显示图例(legend=True)。
1 | #普通单色图 |
变更激活地图
如果同一个 GeoDataFrame 数据内含有多个 geometry 属性的数据,我们可以通过 GeoDataFrame.set_geometry()指定所选择的那一列作为新的激活地图(active geometry),然后绘制它。
1 | gdf = gdf.set_geometry("centroid") |
图案叠加
当然我们还可以将两个 GeoSeries 叠加在一起,只需将其中一个画布作为 axis 即可。
1 | ax = gdf["geometry"].plot() |
网格图例
Choro legends — GeoPandas 0.14.0+0.g0eb2a5e.dirty documentation
背景底图
本示例将展示如何在使用 .plot() 方法创建的绘图上面添加背景底图。
该示例需要利用 contextily 库,它可以从多个来源(OpenStreetMap、CartoDB)来检索网络地图瓦片。可以查看 contextily 的介绍指南了解更多新功能。
1 | import contextily as cx |
更多底图 (basemap) 来源参见:A short look into providers objects — contextily 1.1.0 documentation
创建几何体
凸包
我们可以通过 GeoDataFrame.convex_hull 获取给定几何图形的凸包/凸多边形(convex hull):
1 | gdf["convex_hull"] = gdf.convex_hull |
缓冲版本
在某些情况下,我们可能需要使用 GeoDataFrame.buffer() 对几何体进行缓冲,该方法自动应用于已激活的几何体,当然我们也可以将它们直接应用于任何 GeoSeries。
此处的“缓冲”意味着对图形进行类似“膨胀”的处理,输入参数为膨胀直径
1 | # buffering the active geometry by 10 000 feet (geometry is already in feet) |
Voronoi 图
待更:Create a Python Voronoi Diagram with GeoPandas and Geoplot - wellsr.com
投影坐标系
每个 GeoSeries 都有自己的坐标参考系(Coordinate Reference System,CRS),可通过 GeoSeries.crs 访问,可以获知 GeoPandas 几何图形在地球表面的坐标位置。
WGS84 的授权码为 EPSG:4326。这种 CRS 是地理坐标:坐标是经纬度。
前面我们一直在用的示例地图可以通过 gdf.crs 查看其 CRS,然后通过 gdf.to_crs() 方法更改坐标系:
1 | print(gdf.crs) |
为了通过更丰富的坐标系绘制图像,我们还可以结合 CartoPy 库。
CartoPy 最初是在英国气象局开发的,目的是让科学家能够快速、方便、最重要的是准确地在地图上可视化他们的数据。它主要特点是面向对象的投影定义,以及在投影之间转换点、线、向量、多边形和图像的能力。
一个简单的示例如下,利用各种不同的投影来绘制世界地图:
1 | import cartopy.crs as ccrs |
- 使用 CartoPy 的参考系给 GeoPandas 绘制图像:
1 | # 读取/下载 世界地图 |
- 使用 GeoPandas 的数据对象给 CartoPy 绘图:
1 | fig = plt.figure() |
更多CartoPy的入门:Cartopy 系列:从入门到放弃 - 炸鸡人博客
更多坐标系参数:Cartopy 中的地图投影_cartopy.crs | CSDN
自建GeoDataFrame
经纬度数据
一个 GeoDataFrame 在 DataFrame 数据的基础上还需要一个 shapely 对象。
一般地,我们通过 geopandas.points_from_xy() 将给定的经纬度( Longitude 和 Latitude)转换为 shapely.Point 对象列表,然后在创建GeoDataFrame时将其作为一个 geometry。
1 | points_from_xy(df.Longitude, df.Latitude) |
在创建GeoDataFrame时还需要注意必须指定 crs 来正确解释数据所使用的参考系!
1 | # 以南美洲为例 |
此外还可以使用 WTK字符串 (Well-known Text)建立数据:
1 | df = pd.DataFrame( |
更多 WTK 描述几何对象的示例:WKT 描述的几何对象 — OGC标准规范 1.0 文档
空间栅格图像
Using GeoPandas with Rasterio to sample point data — GeoPandas 0.14.0+0.g0eb2a5e.dirty documentation
空间连接
空间连接(spatial join)使用二元谓词(如相交 intersects 和交叉 crosses),根据两个 GeoDataFrames 几何图形之间的空间关系将它们组合在一起。
常见的用例一般是点图层和多边形图层之间的空间连接,在这种情况下,需要保留点的几何图形并抓取相交多边形的属性。
1 | # 示例变量 |
Left/Right outer join
对 table1 和 table2 进行左连接Left Outer Join 时,以左为主。结果以 table1 为主,关联上 table2 的数据,显示左边的所有数据,然后右边显示的是和左边有交集部分的数据(若有多个关联,右表可重复)。右连接 Right Outer Join 类似。
在 Spatial Join 中,交集主要是指几何图形 geometry 的几何关系。
1 | join_left_df = pointdf.sjoin(polydf, how="left") |
Inner join
1 | join_inner_df = pointdf.sjoin(polydf, how="inner") |
判断点面关系Plus
我们可以利用空间关系结合 pandas.DataFrame.value_count() 批量对由点构成的 GeoDataFrame 和由几何体构成的 GeoDataFrame 之间的点面关系进行统计。
1 | result = gpd.sjoin(pointdf, polydf, predicate='within') #这会drop掉NaNs项 |
PyTorch Geo
PyTorch Geometric Library (简称 PyG) 是一个基于 PyTorch 的图神经网络库。它的运行速度很快,训练模型速度可以达到DGL(Deep Graph Library )v0.2 的40倍(数据来自论文)。除了出色的运行速度外,PyG中也集成了很多论文中提出的方法(GCN,SGC,GAT,SAGE等)和常用数据集。因此对于复现论文来说也相当方便。
图结构数据受理
PyG中通过 torch_geometric.data模块下的 Data类来接收并管理 Graph 数据。它包含 5 个属性,每一个属性都不是必须的,可以为空。
Data.x:存储每个节点及其特征,形状是[num_nodes, num_node_features]。Data.edge_index:存储节点之间的边,形状是[2, num_edges]。Data.pos:存储节点的坐标,形状是[num_nodes, num_dimensions]。Data.y:存储样本标签。如果是每个节点都有标签,那么形状是[num_nodes, *];如果是整张图只有一个标签,那么形状是[1, *]。Data.edge_attr: 存储边的特征,形状是[num_edges, num_edge_features]。
实际上,Data对象不仅仅限制于这些属性,我们可以通过data.face来扩展Data,以形状是 [3, num_faces] 的 torch.long 类型的张量来保存 3D mesh 中三角形的连接性。
下面给出一个构建PyG中的图结构数据的示例(带有三个节点的无权无向图):
1 | import torch |
它对应的图如下:
值得注意的是,edge_index中边的存储方式是给定两个list,第 1 个list是所有边的起始点,第 2 个list是所有边的目标节点。而不是由一个个 起点-终点对 给出的。如果想用后者的存储方式,可以对其转置调用 contiguous() 方法:
1 | import torch |
此外,尽管该图只有两条边,但我们仍然需要定义4个索引元组来说明边的两个方向。而且 edge_index 中的元素必须只包含范围为 {0, ..., num_nodes - 1} 的索引。
这是必要的,因为我们希望最终的数据表示尽可能紧凑,例如,我们希望通过 x[0] 和 x[1] 分别索引第一条边 (0, 1) 的源节点和目的节点特征。
可以通过运行 validate() 来检查最终的数据对象是否满足这些要求:
1 | data.validate(raise_on_error=True) |
其他操作还有:
1 | print(data.keys()) |
公开数据集
PyG 的 Dataset继承自torch.utils.data.Dataset,并且它也自带了很多公开的图数据集。(就像视觉领域的手写数字识别数据集那样)
以TUDataset为例,通过下列代码就可以加载数据集,root参数设置数据下载的位置。通过索引可以访问每一个数据。
1 | from torch_geometric.datasets import TUDataset |
从输出结果可以看出,数据集中的第一张图(dataset[0])含有 37 个节点,每个节点有 3 个特征,一共有 168/2 = 84 条边(无向图)。并且,整个这张图只有一个标签,也表明了整个数据集的分类目标是 图级别的(graph-level)。
我们可以用 Python 的切片(slices)方法进行数据集划分。还可以用 PyG 提供的 shuffle() 方法打乱顺序。
1 | dataset = dataset.shuffle() |
接下来看看另一个数据集:
1 | from torch_geometric.datasets import Planetoid |
数据集中的train_mask, val_mask 和 test_mask 是训练时划分使用的掩码。比如训练集中,要将节点 i 加入训练集,那么 train_mask[i] 就取 0。
所以在这里的数据集中,决定了将 140 个节点参加训练;同理,将 1000个节点划为测试集;将500个节点用于验证。
更多自带可用的公开数据集:torch_geometric.datasets — pytorch_geometric documentation
自定义数据集
尽管 PyG 已经包含许多有用的数据集,我们也可以通过继承torch_geometric.data.Dataset使用自己的数据集。PyG 提供 2 种不同的Dataset:
- InMemoryDataset:使用这个
Dataset会一次性把数据全部加载到内存中。 - Dataset:使用这个
Dataset每次加载一个数据到内存中,比较常用。
我们需要在自定义的Dataset的初始化方法中传入数据存放的路径,然后 PyG 会在这个路径下再划分 2 个文件夹:
raw_dir: 存放原始数据的路径,一般是 csv、mat 等格式processed_dir: 存放处理后的数据,一般是 pt 格式 ( 由我们重写process()方法实现)。
在 PyTorch 中,是没有这两个文件夹的。下面来说明一下这两个文件夹在 PyG 中的实际意义和处理逻辑。
待更
小批量样本
PyG 也重写了自己的 Loader 用于批量读取图数据。
1 | from torch_geometric.datasets import TUDataset |
此外,还增加了一个新的属性 torch_geometric.data.Batch 继承自 torch_geometric.data.Data
batch is a column vector which maps each node to its respective graph in the batch:
You can use it to, e.g., average node features in the node dimension for each graph individually:
数据变换与增强
1 | import torch_geometric.transforms as T |
通过 pre_transform 在将 ShapNet 数据读入之前先对其进行 KNN 聚类形成新的图。
1 | import torch_geometric.transforms as T |
通过 transform 对图数据进行随机的增强——在 0.01范围内随机变换节点坐标。
模型构建与训练
基础示例
此处展示了将简单的 GCN 模型 应用于 Cora citation 数据集实现分类任务的流程。
1 |
|
内置网络层
GNN Cheatsheet — pytorch_geometric documentation
自定义网络
【图算法】构建消息传递网络教程 Creating Message Passing Networks by Pytorch-geometric - LeonYi - 博客园 (cnblogs.com)



