Python空间分析如何用于城市研究? python空间计量模型实操与GIS数据处理技巧(含:代码包)

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

《Python空间分析如何用于城市研究? python空间计量模型实操与GIS数据处理技巧(含:代码包)》这篇文章面向正在做城市研究、空间数据分析或GIS课程作业的读者,重点解决一个具体问题:如何把城市矢量数据、统计指标和空间权重矩阵组织起来,并用Python完成基础空间分析与空间计量模型建模。

引言:Python空间分析在城市研究中到底解决什么问题

在城市研究中,我们经常会遇到这类问题:房价是否存在空间集聚?人口密度是否受到邻近区县影响?公共服务设施可达性是否在空间上不均衡?这些问题只看普通表格统计往往不够,因为城市现象通常具有明显的空间相关性。

Python空间分析的价值在于,它可以把GIS数据处理、空间关系构建、探索性空间数据分析和空间计量模型串成一个可复现流程。相比只在桌面GIS软件中点工具,Python更适合批量处理、多城市对比、论文复现和自动化制图。

本文会围绕一个典型城市研究流程展开:读取城市行政区矢量数据,整理指标字段,检查坐标系,构建空间权重矩阵,计算空间自相关,并进一步演示Python空间计量模型实操的基本思路。

Python空间分析用于城市研究的Python空间计量模型实操流程
Python空间分析在城市研究中的典型流程:数据清洗、空间权重、空间自相关和空间计量模型。

背景:城市研究为什么不能只用普通回归

普通线性回归默认样本之间相互独立。但在城市研究中,这个假设经常不成立。例如相邻街道的房价会相互影响,相邻区县的产业结构可能相似,交通可达性也会沿空间网络扩散。

如果忽略空间相关性,可能出现三个问题:

  • 系数估计偏误:如果因变量本身存在空间溢出,普通回归可能低估或高估解释变量的影响。
  • 显著性判断不可靠:空间聚集会使样本信息并非完全独立,标准误可能被低估。
  • 结论缺少空间解释:普通回归只能告诉你变量相关,却很难说明邻近地区是否产生影响。

因此,城市研究中的Python空间分析通常不仅包括缓冲区、叠加、空间连接等GIS处理,还会进一步进入空间自相关分析空间计量模型

原理:从GIS数据到空间计量模型的关键概念

1. 空间单元

空间单元是模型中的样本对象,可以是城市、区县、街道、街区、网格或小区地块。做Python空间分析前,必须先明确分析单元,因为不同空间单元会影响结果解释。

例如,以区县为单元分析房价和人口密度,得到的是区县尺度结论;以1公里网格为单元,得到的则是更细粒度的空间格局。尺度变化可能导致结果不同,这在GIS中通常称为可变空间单元问题。

2. 坐标系与投影

城市研究经常涉及面积、距离、缓冲区和邻接关系。如果数据仍是经纬度坐标系,直接计算距离或面积容易出错。建议在正式分析前,将数据投影到适合研究区的投影坐标系。

常见做法是:

  • 全国尺度可使用等面积投影或适合全国分析的投影方案。
  • 城市尺度可使用当地高斯克吕格投影、UTM分带或地方坐标系。
  • Web地图展示可以使用WGS84或Web Mercator,但不建议直接用于精确面积和距离分析。

3. 空间权重矩阵

空间权重矩阵是Python空间计量模型实操中最关键的一步。它描述“谁和谁是邻居”,以及邻居之间的影响强度。

常见空间权重包括:

  • Queen邻接:只要边界或顶点接触,就认为相邻。
  • Rook邻接:只有共享边界才认为相邻。
  • K近邻:每个空间单元连接最近的K个邻居。
  • 距离阈值:一定距离范围内的单元被视为邻居。

选择哪种权重矩阵,取决于城市问题本身。行政区扩散效应常用邻接权重,交通或服务设施影响更适合距离权重或网络距离权重。

4. 空间自相关

空间自相关用于判断相近地区的属性值是否相似。常见指标是Moran’s I。若Moran’s I显著为正,说明高值靠近高值、低值靠近低值;若显著为负,说明高低值交错分布。

在进入空间计量模型前,建议先做空间自相关检验。它可以帮助你判断是否有必要使用空间滞后模型、空间误差模型或其他空间回归方法。

步骤:Python空间计量模型实操与GIS数据处理流程

步骤1:准备Python环境

建议使用conda创建独立环境,避免GeoPandas、PySAL、Shapely、Fiona等库版本冲突。

conda create -n city_spatial python=3.10
conda activate city_spatial
conda install -c conda-forge geopandas libpysal esda spreg mapclassify matplotlib pandas numpy

如果你使用Jupyter Notebook,可以继续安装:

conda install -c conda-forge jupyterlab

推荐代码包目录结构如下:

city-spatial-analysis/
├── data/
│   ├── city_boundary.shp
│   └── city_indicators.csv
├── output/
│   ├── moran_result.csv
│   └── model_result.txt
├── notebooks/
│   └── 01_city_spatial_model.ipynb
└── scripts/
    └── run_spatial_model.py

步骤2:读取城市边界和指标数据

假设你有一个城市行政区边界文件和一个CSV指标表。边界文件中有区县编码字段,指标表中也有同名编码字段,用于关联。

import geopandas as gpd
import pandas as pd

boundary = gpd.read_file("data/city_boundary.shp")
indicator = pd.read_csv("data/city_indicators.csv")

print(boundary.head())
print(indicator.head())
print(boundary.crs)

如果中文字段或中文路径读取异常,优先检查文件编码、字段名是否被截断,以及Shapefile是否包含完整的shp、shx、dbf、prj文件。

步骤3:检查并统一关联字段

很多Python空间分析失败并不是模型问题,而是字段类型不一致。例如边界数据中的行政区代码是整数,CSV表中的行政区代码是字符串,合并后就会出现大量空值。

boundary["adcode"] = boundary["adcode"].astype(str)
indicator["adcode"] = indicator["adcode"].astype(str)

gdf = boundary.merge(indicator, on="adcode", how="left")

print(gdf[["adcode", "name", "y", "x1", "x2"]].isna().sum())

这里假设因变量为y,解释变量为x1x2。实际研究中,y可以是房价、人口密度、就业密度、夜间灯光强度、设施可达性等指标。

步骤4:投影到适合城市尺度分析的坐标系

如果后续要计算距离或面积,建议不要直接使用经纬度坐标。下面示例将数据投影到Web Mercator,仅用于演示。正式研究时应根据城市所在区域选择更合适的投影。

if gdf.crs is None:
    raise ValueError("数据缺少坐标系,请先确认原始坐标系并设置CRS。")

gdf_projected = gdf.to_crs(epsg=3857)

如果只是做行政区邻接权重,坐标系影响相对较小;如果使用距离阈值或K近邻权重,投影坐标系就非常重要。

步骤5:清理缺失值和异常值

空间计量模型通常不能直接处理缺失值。建议先明确缺失原因,而不是简单删除。

model_cols = ["y", "x1", "x2", "geometry"]
gdf_model = gdf_projected[model_cols].dropna().copy()

print("原始样本数:", len(gdf_projected))
print("建模样本数:", len(gdf_model))

如果删除样本后出现空间单元不连续、孤岛区域过多,空间权重矩阵可能出现问题。此时应回到数据源检查指标缺失是否集中在某些片区。

步骤6:构建空间权重矩阵

下面使用Queen邻接权重。它适合区县、街道等面状行政单元的邻接关系分析。

from libpysal.weights import Queen

w = Queen.from_dataframe(gdf_model)
w.transform = "r"

print("空间单元数量:", w.n)
print("孤岛单元:", w.islands)

w.transform = "r"表示行标准化,即每个空间单元的邻居权重之和为1。这是很多空间计量模型中的常用设置。

如果出现孤岛单元,可以考虑:

  • 检查面数据是否存在拓扑缝隙。
  • 改用K近邻权重。
  • 合并过小或独立的空间单元。
  • 确认研究区边界是否裁剪过度。

步骤7:计算Moran’s I空间自相关

在做Python空间计量模型实操前,先检验因变量是否存在空间自相关。

from esda.moran import Moran

y = gdf_model["y"].values
moran = Moran(y, w)

print("Moran's I:", moran.I)
print("p-value:", moran.p_sim)

如果Moran’s I显著为正,说明因变量存在空间集聚。例如高房价区靠近高房价区,低房价区靠近低房价区。这时继续使用空间计量模型就有更明确的依据。

步骤8:建立普通OLS模型作为基准

不要一开始就直接上空间模型。建议先建立普通OLS模型作为基准,再与空间模型结果比较。

import numpy as np
from spreg import OLS

y_arr = gdf_model[["y"]].values
x_arr = gdf_model[["x1", "x2"]].values

ols_model = OLS(
    y_arr,
    x_arr,
    name_y="y",
    name_x=["x1", "x2"]
)

print(ols_model.summary)

OLS模型可以帮助你初步判断变量方向、显著性和拟合情况。如果OLS残差仍存在明显空间自相关,则说明空间结构没有被普通解释变量完全吸收。

步骤9:建立空间滞后模型

空间滞后模型常用于解释“邻近地区的因变量会影响本地区因变量”的情况。例如相邻区县房价上涨可能带动本区房价变化。

from spreg import ML_Lag

lag_model = ML_Lag(
    y_arr,
    x_arr,
    w=w,
    name_y="y",
    name_x=["x1", "x2"],
    name_w="queen"
)

print(lag_model.summary)

在结果中,需要重点关注空间滞后项的系数。如果该项显著,说明因变量存在空间溢出效应。城市研究中,这常见于房价、产业集聚、人口流动、公共服务可达性等问题。

步骤10:建立空间误差模型

空间误差模型适用于“遗漏变量或误差项存在空间相关”的情况。例如某些未观测的区位因素、政策环境或历史因素在空间上连续分布。

from spreg import ML_Error

error_model = ML_Error(
    y_arr,
    x_arr,
    w=w,
    name_y="y",
    name_x=["x1", "x2"],
    name_w="queen"
)

print(error_model.summary)

空间误差模型不一定表示邻近地区的因变量直接影响本地,而是说明模型中未解释的部分具有空间结构。解释论文结果时要注意这一区别。

步骤11:导出结果和地图

为了让Python空间分析结果可以进入论文、报告或GIS制图流程,建议将模型样本和关键字段导出。

gdf_model.to_file("output/model_sample.gpkg", layer="sample", driver="GPKG")

with open("output/model_result.txt", "w", encoding="utf-8") as f:
    f.write(str(ols_model.summary))
    f.write("nn")
    f.write(str(lag_model.summary))
    f.write("nn")
    f.write(str(error_model.summary))

GeoPackage格式比Shapefile更适合保存中文字段、长字段名和多图层数据。对于后续在QGIS或ArcGIS Pro中制图,推荐优先使用GeoPackage。

常见坑:Python空间分析城市研究中最容易出错的地方

1. 经纬度坐标直接算距离和面积

经纬度单位是度,不是米。直接用经纬度计算城市尺度距离、面积和缓冲区,结果很容易不准确。做GIS数据处理时,应先确认坐标系,再决定是否投影。

2. 行政区代码合并失败

CSV表和矢量边界合并失败,最常见原因是字段类型不一致、前导零丢失、字段名不同或编码不统一。合并后一定要检查缺失值数量。

3. 空间权重矩阵随意选择

Queen权重、Rook权重、K近邻权重和距离权重代表不同空间关系。权重矩阵不是技术细节,而是研究假设的一部分。论文或报告中需要说明选择理由。

4. 只看模型显著性,不看空间含义

空间计量模型不是为了让结果“更显著”。它的重点是解释空间依赖、空间溢出或空间误差结构。变量显著不等于空间机制成立,还需要结合城市背景解释。

5. 忽略孤岛单元

如果某些空间单元没有邻居,空间权重矩阵会出现孤岛。孤岛可能来自真实地理隔离,也可能来自拓扑错误。建模前应检查w.islands

6. 数据尺度混用

例如把街道级人口、区县级GDP和网格级设施指标直接混合建模,容易产生尺度错配。不同空间尺度数据必须先通过空间连接、面积加权、人口加权或汇总规则统一到同一分析单元。

方法比较:桌面GIS、GeoPandas和PySAL怎么选

方法 适合任务 优点 限制
QGIS或ArcGIS Pro 数据检查、投影转换、可视化制图、空间叠加 操作直观,适合快速检查空间数据 批量复现和模型扩展不如代码灵活
GeoPandas 矢量读取、字段处理、空间连接、投影转换 与Pandas结合紧密,适合GIS数据处理自动化 超大数据性能有限,需要配合PostGIS或Dask
PySAL 空间权重、Moran’s I、空间计量模型 空间统计和空间计量能力强,适合城市研究论文 需要理解空间权重和模型假设
PostGIS 大规模空间数据存储、空间查询、批量预处理 适合百万级以上要素和多人协作 模型分析通常还需要导入Python或R

实际项目中,推荐组合使用:先用QGIS或ArcGIS Pro检查数据,再用GeoPandas清洗和转换,使用PySAL完成空间自相关与空间计量模型,最后把结果导回GIS软件制图。

检查清单:跑模型前先确认这些问题

  • 研究问题是否明确:是解释空间集聚、空间溢出,还是控制空间误差?
  • 分析单元是否统一:所有变量是否已经汇总到同一空间尺度?
  • 坐标系是否正确:距离、面积和邻近关系是否依赖投影坐标?
  • 字段是否合并成功:行政区代码、名称和指标表是否一一对应?
  • 缺失值是否处理:删除样本是否改变研究区空间结构?
  • 空间权重是否合理:Queen、Rook、K近邻或距离权重是否符合研究机制?
  • 是否检查Moran’s I:因变量或OLS残差是否存在空间自相关?
  • 是否比较基准模型:是否同时报告OLS和空间模型结果?
  • 结果是否可复现:代码、数据路径、输出文件和环境依赖是否清晰?

FAQ:Python空间分析与城市研究常见问题

Q1:Python空间分析适合哪些城市研究题目?

适合研究房价空间分异、人口密度分布、公共服务设施可达性、交通影响、产业集聚、生态环境质量、城市更新、土地利用变化等问题。只要研究对象有明确空间位置,并且可能存在邻近影响,就可以考虑使用Python空间分析。

Q2:Python空间计量模型实操一定要先会高级数学吗?

不一定要先掌握全部推导,但必须理解基本概念,包括空间权重矩阵、空间自相关、空间滞后、空间误差和模型解释边界。对于GIS读者来说,先从可复现流程入手,再逐步补充理论会更有效。

Q3:Queen权重和K近邻权重哪个更适合城市研究?

如果研究单元是连续行政区,Queen权重常用于表达相邻地区影响。如果研究单元分布不规则、存在孤岛或点状样本较多,K近邻权重更稳定。选择权重时应服务于研究机制,而不是只看模型结果好不好。

Q4:为什么我的Moran’s I不显著?

可能原因包括变量本身没有明显空间集聚、空间权重矩阵不合适、分析尺度过粗或过细、异常值影响结果、样本数量太少。建议先制图查看空间分布,再尝试合理的权重矩阵敏感性分析。

Q5:空间滞后模型和空间误差模型怎么解释?

空间滞后模型强调邻近地区因变量对本地区的影响,常用于分析空间溢出。空间误差模型强调未观测因素或误差项的空间相关,常用于控制遗漏变量造成的空间结构。两者的理论含义不同,不能只根据显著性随意选择。

Q6:是否可以直接用Excel表做空间计量模型?

不建议只用Excel表。空间计量模型需要空间边界、坐标或邻接关系。Excel可以保存属性指标,但必须与GIS矢量数据关联,才能构建空间权重矩阵并进行Python空间分析。

Q7:代码包应该包含哪些内容才方便复现?

建议至少包含环境说明、原始数据说明、数据清洗脚本、模型运行脚本、输出结果目录和README文件。如果数据不能公开,也应提供字段结构说明和示例数据,方便他人理解你的GIS数据处理流程。

结论:把Python空间分析变成可复现的城市研究流程

Python空间分析用于城市研究,并不是简单调用一个模型函数,而是一套完整流程:明确研究问题,整理GIS数据,统一空间单元,检查坐标系,构建空间权重,检验空间自相关,再选择合适的Python空间计量模型。

对GIS读者来说,最重要的是把空间概念和城市机制结合起来。模型结果只有放回真实城市背景中解释,才有研究价值。建议初学者先用一个城市、一个指标、一个清晰问题完成全流程,再逐步扩展到多城市、多年份或面板空间计量分析。

如果你正在写课程论文、毕业论文或城市空间分析报告,可以先按本文的检查清单整理数据和代码包。只要数据结构清楚、权重矩阵合理、模型解释克制,Python空间计量模型实操就能成为城市研究中非常可靠的GIS分析工具。