ArcGIS处理数据太慢?GeoPandas高效分析实战(附:完整源码)

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

引言

《ArcGIS处理数据太慢?GeoPandas高效分析实战(附:完整源码)》这篇教程,专门解决一个很常见的问题:同样是缓冲区、叠加、空间连接、字段统计,为什么在 ArcGIS 里处理几十万条要素会越来越慢,而用 Python 和 GeoPandas 可以更适合批量分析与自动化处理。

本文不会简单说“GeoPandas一定比ArcGIS快”。更准确的说法是:当你的任务属于批量、重复、规则明确的矢量空间分析时,GeoPandas可以减少手工操作、避免界面等待,并且更容易把数据清洗、空间分析和结果导出串成一个可复用流程。

本文示例以一个典型场景为主:已有一批点位数据和行政区面数据,需要完成坐标系统一、空间连接、按行政区统计点数量,并导出结果。这个流程在 ArcGIS Pro 中可以做,但如果每天、每周都要重复,GeoPandas更适合写成脚本。

ArcGIS处理数据太慢 GeoPandas高效分析空间连接统计流程
GeoPandas适合把重复性的矢量空间分析流程脚本化,例如坐标系统一、空间连接、分组统计和结果导出。

背景

很多GIS同学遇到“ArcGIS处理数据太慢”,并不是因为软件本身不能处理,而是任务类型不适合一直靠手工界面完成。尤其是以下几类工作,ArcGIS Pro 或 ArcMap 中频繁点击工具、等待进度条、反复检查参数,很容易消耗大量时间。

  • 同一套空间分析流程需要对多个城市、多个年份、多个图层重复执行。
  • 数据量较大,例如几十万点、上百万线段、复杂行政区面。
  • 每次处理前都要做字段清洗、坐标系检查、空几何过滤、重复值删除。
  • 分析结果还要继续做统计汇总、表格导出、字段重命名。
  • 团队需要可复现流程,而不是依赖某个人的手工操作记录。

GeoPandas是Python生态中的矢量GIS分析库,可以理解为“带几何字段的Pandas”。它适合处理 Shapefile、GeoPackage、GeoJSON、PostGIS 等常见GIS数据,并能与 Shapely、Pyogrio、Fiona、PyProj、Rtree 或 PyGEOS/Shapely 2.x 等库配合完成空间分析。

如果你的需求是制图排版、复杂编辑、影像可视化、交互检查,ArcGIS仍然很强。但如果你的需求是批量数据处理、自动化空间统计、重复分析流程,GeoPandas高效分析会更有优势。

原理

要判断一个任务是否适合从 ArcGIS 转到 GeoPandas,先要理解性能瓶颈通常来自哪里。

1. 界面操作慢,不等于算法一定慢

ArcGIS中的很多工具本身很成熟,但手工使用时会产生额外成本,例如打开工程、加载图层、选择输入输出、设置字段、写临时文件、等待界面刷新。单次操作问题不大,但重复上百次时,界面成本会被放大。

2. GeoPandas适合把流程写成代码

GeoPandas的核心优势不是“按钮更少”,而是可以把一整套处理过程写成可重复运行的脚本。只要输入数据路径不变,或者用配置参数控制路径,就能自动完成处理。

3. 空间索引决定空间连接效率

在做空间连接、相交判断、最近邻分析时,如果没有空间索引,程序可能需要进行大量几何两两比较。GeoPandas在执行 sjoin 等操作时会使用空间索引来减少候选几何数量,从而提升效率。

4. 坐标系会直接影响结果和速度

经纬度坐标系适合表达位置,但不适合直接计算面积、距离和缓冲区。对于GeoPandas高效分析,建议先统一坐标系,并在需要距离或面积计算时转换到合适的投影坐标系。

一句话原则:ArcGIS适合交互式GIS工作,GeoPandas适合可重复、可批量、可审计的矢量数据分析流程。

步骤

下面给出一个完整实战流程:读取点数据和行政区面数据,统一坐标系,做空间连接,按行政区统计点数量,并输出结果文件。这个案例可以替代很多在 ArcGIS 中反复执行“空间连接 + 汇总统计”的工作。

步骤1:准备Python环境

建议使用独立环境安装GeoPandas,避免和已有ArcGIS Python环境混在一起。推荐使用 conda 或 mamba。

conda create -n gis-geopandas python=3.11 -y
conda activate gis-geopandas
conda install -c conda-forge geopandas pyogrio openpyxl -y

如果你更习惯 pip,也可以使用:

python -m venv .venv
.venvScriptsactivate
pip install geopandas pyogrio openpyxl

在Windows环境下,GIS相关库涉及GDAL、PROJ等依赖。初学者优先推荐 conda-forge,因为它会自动处理大部分底层依赖。

步骤2:准备输入数据

本文假设有两个输入数据:

  • 点数据:data/poi.gpkg,图层名为 poi,表示兴趣点、监测点或事件点。
  • 行政区面:data/admin.gpkg,图层名为 admin,包含字段 name 表示行政区名称。

如果你的数据是 Shapefile,也可以读取 .shp 文件。但在批量处理时,更建议使用 GeoPackage,因为它对中文字段、长字段名和多图层管理更友好。

步骤3:读取数据并检查基本信息

import geopandas as gpd
from pathlib import Path

base_dir = Path("data")
poi_path = base_dir / "poi.gpkg"
admin_path = base_dir / "admin.gpkg"

poi = gpd.read_file(poi_path, layer="poi", engine="pyogrio")
admin = gpd.read_file(admin_path, layer="admin", engine="pyogrio")

print("POI数量:", len(poi))
print("行政区数量:", len(admin))
print("POI坐标系:", poi.crs)
print("行政区坐标系:", admin.crs)
print("POI字段:", poi.columns.tolist())
print("行政区字段:", admin.columns.tolist())

这一步非常重要。很多所谓“ArcGIS处理数据太慢”或“空间连接结果不对”,其实是因为坐标系为空、字段名不一致、几何无效或数据中混入了空几何。

步骤4:清理空几何和无效几何

poi = poi[poi.geometry.notna()].copy()
admin = admin[admin.geometry.notna()].copy()

poi = poi[~poi.geometry.is_empty].copy()
admin = admin[~admin.geometry.is_empty].copy()

admin["geometry"] = admin.geometry.make_valid()

print("清理后POI数量:", len(poi))
print("清理后行政区数量:", len(admin))

make_valid 用于修复部分无效面几何,例如自相交、多边形环错误等。如果面数据来自第三方或历史数据,建议在空间连接前先做这一步。

步骤5:统一坐标系

空间连接要求两个图层在同一坐标参考系统中。否则即使程序可以运行,结果也可能完全错误。

if poi.crs is None:
    raise ValueError("POI数据缺少坐标系,请先定义正确CRS。")

if admin.crs is None:
    raise ValueError("行政区数据缺少坐标系,请先定义正确CRS。")

if poi.crs != admin.crs:
    poi = poi.to_crs(admin.crs)

print("统一后的POI坐标系:", poi.crs)
print("行政区坐标系:", admin.crs)

注意:to_crs 是坐标转换,不是“定义投影”。如果原始数据本身没有正确CRS,不能随便用 to_crs,必须先确认它原来到底是什么坐标系。

步骤6:执行空间连接

这里使用 gpd.sjoin,判断每个点落在哪个行政区面内。常见谓词包括 withinintersectscontains。对于点落面统计,通常使用 within

joined = gpd.sjoin(
    poi,
    admin[["name", "geometry"]],
    how="left",
    predicate="within"
)

print(joined.head())
print("连接后记录数:", len(joined))
print("未匹配行政区的点数量:", joined["name"].isna().sum())

如果发现大量点没有匹配行政区,优先检查三件事:坐标系是否一致、点是否落在行政区范围外、行政区面是否存在缝隙或边界错误。

步骤7:按行政区统计点数量

stat = (
    joined
    .groupby("name", dropna=False)
    .size()
    .reset_index(name="poi_count")
)

stat["name"] = stat["name"].fillna("未匹配行政区")

print(stat.sort_values("poi_count", ascending=False).head(10))

这一步相当于在 ArcGIS 中做空间连接后,再做汇总统计。用GeoPandas写成脚本后,可以很方便地嵌入到日报、周报或批量质检流程中。

步骤8:把统计结果回写到行政区面

admin_result = admin.merge(
    stat[stat["name"] != "未匹配行政区"],
    on="name",
    how="left"
)

admin_result["poi_count"] = admin_result["poi_count"].fillna(0).astype(int)

print(admin_result[["name", "poi_count"]].head())

回写后的行政区面可以直接用于分级设色制图,也可以继续在 QGIS、ArcGIS Pro 或 WebGIS 中展示。

步骤9:导出GeoPackage和Excel结果

output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

admin_result.to_file(
    output_dir / "admin_poi_count.gpkg",
    layer="admin_poi_count",
    driver="GPKG"
)

stat.to_excel(
    output_dir / "poi_count_by_admin.xlsx",
    index=False
)

print("处理完成:")
print(output_dir / "admin_poi_count.gpkg")
print(output_dir / "poi_count_by_admin.xlsx")

GeoPackage适合保存空间结果,Excel适合给非GIS同事查看统计表。如果要继续进入PostGIS,也可以使用 to_postgis,但需要额外配置数据库连接。

完整源码

import geopandas as gpd
from pathlib import Path

def main():
    base_dir = Path("data")
    output_dir = Path("output")
    output_dir.mkdir(exist_ok=True)

    poi_path = base_dir / "poi.gpkg"
    admin_path = base_dir / "admin.gpkg"

    poi = gpd.read_file(poi_path, layer="poi", engine="pyogrio")
    admin = gpd.read_file(admin_path, layer="admin", engine="pyogrio")

    print("POI数量:", len(poi))
    print("行政区数量:", len(admin))
    print("POI坐标系:", poi.crs)
    print("行政区坐标系:", admin.crs)

    poi = poi[poi.geometry.notna()].copy()
    admin = admin[admin.geometry.notna()].copy()

    poi = poi[~poi.geometry.is_empty].copy()
    admin = admin[~admin.geometry.is_empty].copy()

    admin["geometry"] = admin.geometry.make_valid()

    if poi.crs is None:
        raise ValueError("POI数据缺少坐标系,请先定义正确CRS。")

    if admin.crs is None:
        raise ValueError("行政区数据缺少坐标系,请先定义正确CRS。")

    if poi.crs != admin.crs:
        poi = poi.to_crs(admin.crs)

    joined = gpd.sjoin(
        poi,
        admin[["name", "geometry"]],
        how="left",
        predicate="within"
    )

    unmatched_count = joined["name"].isna().sum()
    print("未匹配行政区的点数量:", unmatched_count)

    stat = (
        joined
        .groupby("name", dropna=False)
        .size()
        .reset_index(name="poi_count")
    )

    stat["name"] = stat["name"].fillna("未匹配行政区")

    admin_stat = stat[stat["name"] != "未匹配行政区"].copy()

    admin_result = admin.merge(
        admin_stat,
        on="name",
        how="left"
    )

    admin_result["poi_count"] = admin_result["poi_count"].fillna(0).astype(int)

    admin_result.to_file(
        output_dir / "admin_poi_count.gpkg",
        layer="admin_poi_count",
        driver="GPKG"
    )

    stat.to_excel(
        output_dir / "poi_count_by_admin.xlsx",
        index=False
    )

    print("处理完成。")
    print("空间结果:", output_dir / "admin_poi_count.gpkg")
    print("统计表:", output_dir / "poi_count_by_admin.xlsx")

if __name__ == "__main__":
    main()

常见坑

1. 坐标系看起来一样,实际EPSG不同

很多数据都显示为 WGS 84,但有的数据是 EPSG:4326,有的数据可能是被错误定义的投影坐标。不要只看图层能否叠在一起,要检查 gdf.crs,必要时回到数据来源确认原始坐标系。

2. 用经纬度直接做距离和缓冲区

如果你要做缓冲区、距离筛选、面积统计,不建议在 EPSG:4326 这类经纬度坐标系下直接计算。应先转换到适合当地的投影坐标系,例如高斯克吕格、UTM或地方投影。

3. Shapefile字段名被截断

Shapefile字段名长度有限,中文编码也容易出问题。GeoPandas处理批量数据时,建议优先使用 GeoPackage,减少字段截断和编码错误。

4. 空间连接结果重复

如果一个点落在多个面中,或者行政区面存在重叠,sjoin 会产生多条匹配记录。此时不要急着认为GeoPandas错了,应检查面数据是否重叠,或者业务上是否允许一对多关系。

5. 数据太大时内存不足

GeoPandas主要在内存中处理数据。如果数据达到千万级要素,单机内存可能成为瓶颈。可以考虑分块处理、先做空间范围裁剪、使用PostGIS,或者使用Dask-GeoPandas等并行方案。

6. ArcGIS和GeoPandas结果边界不完全一致

不同软件底层几何引擎、容差处理、边界谓词可能存在差异。对于边界点,withincontainsintersects 的结果可能不同。需要根据业务定义选择合适的空间关系。

方法比较

方法 适合场景 优点 限制
ArcGIS Pro手工工具 一次性分析、交互检查、制图输出 界面友好,工具体系完整,适合可视化检查 重复任务效率低,参数容易手工设错
ArcPy脚本 ArcGIS体系内自动化处理 兼容ArcGIS工具箱和地理数据库能力 依赖ArcGIS许可和环境,跨平台不如开源生态灵活
GeoPandas脚本 批量矢量分析、空间连接、字段统计、格式转换 代码简洁,易与Pandas结合,适合自动化流程 超大数据受内存限制,复杂编辑和制图不如桌面GIS直观
PostGIS 多人共享、千万级数据、服务端空间查询 空间索引强,适合数据库级管理和WebGIS后台 需要数据库维护能力,初学成本高于本地脚本

如果只是偶尔处理一个图层,ArcGIS Pro很方便。如果你正在反复执行同一类空间连接、裁剪、汇总统计,GeoPandas高效分析更值得投入学习。如果数据已经进入数据库并服务于WebGIS,PostGIS通常更合适。

检查清单

在把“ArcGIS处理数据太慢”的流程迁移到GeoPandas之前,可以按下面清单逐项检查。

  • 数据格式:优先使用 GeoPackage,避免Shapefile字段名和编码问题。
  • 坐标系:所有输入图层必须有正确CRS,空间分析前保持一致。
  • 几何质量:过滤空几何,修复无效面,检查是否存在重叠面或缝隙。
  • 字段名称:提前确认用于关联和统计的字段,例如 namecodetype
  • 空间关系:点落面统计常用 within,边界也要算入时可评估 intersects
  • 性能瓶颈:如果空间连接慢,先检查数据范围、要素数量、空间索引和几何复杂度。
  • 结果验证:抽样对比ArcGIS结果,重点检查未匹配点、边界点和统计总数。
  • 输出格式:空间结果导出GPKG,统计表导出XLSX或CSV,便于后续使用。

FAQ

ArcGIS处理数据太慢,一定要换GeoPandas吗?

不一定。如果你只是偶尔做一次分析,ArcGIS Pro的图形界面更直观。如果你需要重复处理、多批数据、自动汇总和导出,GeoPandas更适合把流程脚本化。

GeoPandas高效分析是否能完全替代ArcGIS?

不能简单替代。GeoPandas适合矢量数据处理、空间连接、字段统计和自动化流程;ArcGIS在交互编辑、地图制图、企业GIS平台和部分高级分析工具方面仍然有优势。

为什么GeoPandas空间连接结果和ArcGIS空间连接结果不同?

常见原因包括坐标系不同、几何无效、面边界重叠、空间谓词选择不同,以及边界点处理规则不同。建议先统一CRS,再抽样检查边界点和未匹配点。

GeoPandas处理百万级数据会不会很慢?

取决于数据类型、几何复杂度、内存大小和分析方法。百万级点落面统计通常可以尝试GeoPandas,但如果是复杂面叠加或千万级数据,建议考虑PostGIS、分块处理或先简化几何。

已有Shapefile数据,可以直接用于本文代码吗?

可以,把 gpd.read_file 的路径改成 .shp 文件即可。但如果字段较多、中文字段较多或需要长期维护,建议先转换为GeoPackage。

ArcPy和GeoPandas应该学哪个?

如果你的工作高度依赖ArcGIS许可、地理数据库和工具箱,ArcPy很有价值。如果你希望做开源GIS分析、批量数据处理、WebGIS后台数据预处理,GeoPandas更灵活。两者并不冲突。

结论

当你觉得ArcGIS处理数据太慢时,先不要急着换软件,而要判断任务是否属于重复性、批量化、规则明确的矢量分析。如果是,GeoPandas高效分析可以把原本依赖手工点击的空间连接、统计汇总、结果导出,整理成一段可复用脚本。

本文给出的完整源码覆盖了读取数据、清理几何、统一坐标系、空间连接、分组统计和导出结果几个关键环节。实际项目中,你可以在此基础上继续加入字段标准化、批量遍历文件夹、日志记录、异常捕获和结果质检,让GIS数据处理流程更稳定、更可复现。

对GIS初学者来说,最实用的学习路径是:先用ArcGIS理解空间分析逻辑,再用GeoPandas把重复流程自动化。这样既不会脱离GIS原理,也能显著提升日常数据处理效率。