PySAL做空间统计?莫兰指数怎么算?
引言:很多同学第一次接触空间统计时,会直接问:PySAL做空间统计?莫兰指数怎么算? 这其实是一个非常典型的 GIS 分析问题:你手里有一份带有空间位置的矢量数据,想判断某个属性值在空间上是“聚集分布”“离散分布”,还是接近随机分布。
本文以 Python GIS 中常用的 PySAL 为例,讲清楚全局莫兰指数 Moran’s I 的计算流程、空间权重矩阵怎么选、结果怎么解释,以及在真实 GIS 项目中最容易踩的坑。适合正在学习空间分析、空间自相关、GeoPandas 和 PySAL 的 GIS 学生与初级工程师。

背景:为什么用 PySAL 做空间统计
背景:在 GIS 中,很多现象并不是独立随机出现的。例如房价高的区域往往靠近其他高房价区域,污染浓度高的监测点周边也可能有较高污染值。这种“相近位置具有相似属性”的现象,就是空间自相关。
PySAL 是 Python Spatial Analysis Library 的缩写,是 Python 生态中非常重要的空间统计工具库。它常与 GeoPandas、libpysal、esda 等库配合使用,用来完成空间权重矩阵、空间自相关、空间回归、聚类检测等分析。
在空间统计入门阶段,最常用的指标之一就是全局莫兰指数 Moran’s I。它回答的问题是:某个属性值在整个研究区范围内是否存在空间聚集趋势。
典型应用场景包括:
- 分析城市房价是否存在高值聚集区。
- 判断县域人口密度是否具有空间集聚特征。
- 检查污染物浓度是否呈现空间相关性。
- 分析疾病发病率、经济指标、夜间灯光值等是否随机分布。
原理:莫兰指数怎么算,结果怎么看
原理:莫兰指数 Moran’s I 衡量的是一个变量与其空间邻近区域变量之间的相关程度。简单理解,就是把每个地理单元的属性值与周边地理单元的属性值进行比较,看高值是否靠近高值、低值是否靠近低值。
全局莫兰指数的常见解释如下:
- Moran’s I 大于 0:正空间自相关,高值靠近高值、低值靠近低值,存在空间聚集。
- Moran’s I 接近 0:空间分布接近随机。
- Moran’s I 小于 0:负空间自相关,高值靠近低值,表现为空间离散或棋盘式分布。
不过,只看 Moran’s I 的数值是不够的。实际分析时还要看 p 值和 z 值,用来判断结果是否具有统计显著性。
- p 值较小:通常说明空间自相关结果更可能不是随机产生的。
- z 值较大或较小:表示结果偏离随机分布的程度更明显。
- p 值不显著:即使 Moran’s I 为正,也不能轻易说存在显著空间聚集。
PySAL 计算莫兰指数时,最关键的前提是构建空间权重矩阵。空间权重矩阵用来定义“谁是谁的邻居”。如果邻居关系定义不合理,莫兰指数的结果也会失真。
步骤:PySAL 计算全局莫兰指数的完整流程
步骤:下面以面数据为例,演示如何使用 GeoPandas 和 PySAL 计算全局莫兰指数。假设你有一份行政区划数据,字段 value 表示需要分析的指标,例如人口密度、房价或污染浓度。
1. 安装需要的 Python 库
建议在独立的 conda 环境中安装,避免和其他 GIS 项目依赖冲突。
conda create -n pysal_env python=3.11
conda activate pysal_env
conda install -c conda-forge geopandas libpysal esda matplotlib
如果你使用 pip,也可以安装:
pip install geopandas libpysal esda matplotlib
2. 读取空间数据
使用 GeoPandas 读取 Shapefile、GeoPackage 或 GeoJSON 都可以。实际项目中更推荐 GeoPackage,因为字段名、编码和多图层管理更稳定。
import geopandas as gpd
gdf = gpd.read_file("data/sample_area.gpkg")
print(gdf.head())
print(gdf.crs)
print(gdf.columns)
这里需要重点检查三件事:
- 数据是否成功读取。
- 几何字段是否有效。
- 用于计算莫兰指数的属性字段是否存在。
3. 清理空值和无效几何
PySAL 做空间统计前,最好先清理空值和无效几何。否则可能出现权重矩阵构建失败、计算结果包含空值等问题。
gdf = gdf[gdf.geometry.notnull()].copy()
gdf = gdf[gdf.is_valid].copy()
gdf = gdf.dropna(subset=["value"]).copy()
gdf["value"] = gdf["value"].astype(float)
如果你的数据中存在自相交、多部件异常或空面,可以先在 QGIS 中使用“修复几何”工具,或在 GeoPandas 中进行几何修复。
4. 构建空间权重矩阵
对于行政区划面数据,最常见的是 Queen 邻接和 Rook 邻接。
- Queen 邻接:只要边界或顶点接触,就算邻居。
- Rook 邻接:必须共享边界才算邻居。
Queen 邻接更宽松,Rook 邻接更严格。对于县区、街道、网格等面数据,Queen 权重是入门分析中较常见的选择。
from libpysal.weights import Queen
w = Queen.from_dataframe(gdf)
w.transform = "r"
print(w.n)
print(w.pct_nonzero)
w.transform = "r" 表示对权重矩阵进行行标准化。这样每个地理单元的邻居权重之和为 1,便于进行空间统计解释。
5. 计算全局莫兰指数 Moran’s I
使用 esda.Moran 可以直接计算全局莫兰指数。
from esda.moran import Moran
y = gdf["value"].values
moran = Moran(y, w)
print("Moran's I:", moran.I)
print("Expected I:", moran.EI)
print("z-score:", moran.z_norm)
print("p-value:", moran.p_norm)
如果想使用置换检验结果,可以查看:
print("Permutation p-value:", moran.p_sim)
print("Permutation z-score:", moran.z_sim)
在实际报告中,通常可以同时报告 Moran’s I、z 值和 p 值。例如:
全局莫兰指数 Moran’s I 为 0.42,p 值小于 0.05,说明该指标在研究区内存在显著的正空间自相关,即高值和低值均表现出一定空间聚集特征。
6. 生成简单的可视化辅助判断
莫兰指数是统计结果,但 GIS 分析不能只看数字。建议同时查看属性分布图,判断高值和低值是否真的呈现空间聚集。
import matplotlib.pyplot as plt
gdf.plot(
column="value",
cmap="OrRd",
legend=True,
edgecolor="white",
linewidth=0.3
)
plt.title("Attribute Distribution")
plt.axis("off")
plt.show()
如果你发现地图上明显存在几个高值连片区域,而 PySAL 莫兰指数也显著为正,那么结果就比较可信。如果地图上没有明显空间结构,但结果却显著,需要进一步检查空间权重矩阵、异常值和数据尺度。
常见坑:PySAL 莫兰指数计算容易出错的地方
常见坑:PySAL 做空间统计并不难,真正难的是前处理和结果解释。下面这些问题在 GIS 项目中非常常见。
1. 坐标系没有检查
对于 Queen 或 Rook 这种邻接权重,坐标系影响相对较小,因为它主要看边界是否相邻。但如果你使用距离权重,例如 KNN 或距离阈值权重,坐标系就非常关键。
如果数据是经纬度坐标系,距离单位是度,不适合直接用于米级距离分析。此时应先投影到合适的投影坐标系。
print(gdf.crs)
# 示例:投影到适合本地研究区的投影坐标系
gdf = gdf.to_crs("EPSG:3857")
注意,EPSG:3857 只是演示用。正式分析中应选择适合研究区的本地投影坐标系,例如高斯克吕格、UTM 或地方坐标系。
2. 孤岛单元导致权重异常
如果某些面要素没有邻居,例如离岛、孤立地块或拓扑断裂,就会产生 island。孤岛会影响空间权重矩阵和统计结果。
print(w.islands)
如果存在孤岛,需要根据业务场景处理:
- 检查是否是拓扑错误导致没有相邻面。
- 如果确实是离岛,可以考虑使用 KNN 权重。
- 在报告中说明孤岛处理方式。
3. 属性字段不是连续数值
莫兰指数适合分析连续型数值变量,例如人口密度、房价、污染浓度、收入水平等。如果字段是类别编码,例如土地利用类型 1、2、3、4,直接计算 Moran’s I 往往没有明确统计含义。
如果要分析类别型空间分布,应考虑其他方法,例如连接数统计、空间聚类或分类变量的邻接关系分析。
4. 样本尺度不同导致结果变化
同一个指标,在省级、县级、街道级、网格级上计算莫兰指数,结果可能完全不同。这属于 GIS 中常见的可变空间单元问题,也就是 MAUP。
因此,报告 PySAL 莫兰指数结果时,一定要说明分析尺度和空间单元类型。
5. 把全局莫兰指数当成热点分析
全局莫兰指数只告诉你整个研究区是否存在总体空间自相关,并不会告诉你热点在哪里。如果你需要识别具体的高值聚集区和低值聚集区,应继续计算局部莫兰指数 Local Moran’s I 或 Getis-Ord Gi*。
方法比较:Queen、Rook、KNN 和距离权重怎么选
方法比较:莫兰指数怎么算,很大程度上取决于空间权重矩阵怎么定义。不同权重适合不同数据类型和研究问题。
| 权重方法 | 适合数据 | 优点 | 注意事项 |
|---|---|---|---|
| Queen 邻接 | 行政区、网格、街区面数据 | 邻居定义宽松,常用于面状单元 | 顶点接触也算邻居,可能增加邻居数量 |
| Rook 邻接 | 规则面、行政边界面 | 只考虑共享边,更严格 | 可能产生更多孤岛或低邻接单元 |
| KNN 权重 | 点数据、孤立面中心点 | 每个要素都有固定数量邻居 | K 值选择会影响结果 |
| 距离阈值权重 | 点数据、距离衰减明显的现象 | 符合距离影响逻辑 | 必须使用合适投影坐标系和距离阈值 |
如果你是第一次用 PySAL 做空间统计,可以按下面的顺序选择:
- 面数据优先尝试 Queen 邻接。
- 如果边界共享关系要求严格,再尝试 Rook 邻接。
- 如果存在较多孤岛,考虑 KNN 权重。
- 如果研究问题明确与距离有关,再使用距离阈值权重。
检查清单:计算前后必须确认的事项
检查清单:为了让 PySAL 莫兰指数结果更可靠,建议在正式分析前后按这个清单逐项检查。
- 数据是否为正确的研究范围,是否存在多余区域。
- 几何是否有效,是否有空几何、自相交或拓扑错误。
- 分析字段是否为连续数值型变量。
- 字段是否存在空值、极端异常值或单位混乱。
- 空间权重矩阵是否符合研究问题。
- 是否存在孤岛单元,是否已经说明处理方式。
- 是否查看了 Moran’s I、p 值和 z 值,而不是只看一个数值。
- 是否配合专题图检查空间分布是否合理。
- 是否说明了空间尺度,例如县级、街道级或网格级。
- 是否避免把全局莫兰指数误解为具体热点位置。
FAQ:PySAL 做空间统计常见问题
FAQ:下面整理几个与 PySAL 莫兰指数计算直接相关的问题。
Q1:PySAL 做空间统计一定要用 GeoPandas 吗?
不一定,但推荐使用 GeoPandas。GeoPandas 可以方便地读取 Shapefile、GeoPackage、GeoJSON 等 GIS 数据,并与 libpysal 的空间权重函数衔接。对于常规 Python GIS 工作流,GeoPandas 加 PySAL 是比较自然的组合。
Q2:莫兰指数怎么算才算显著?
不能只看 Moran’s I 的正负,还要看 p 值和 z 值。一般来说,p 值小于常用显著性水平时,才可以认为结果具有统计显著性。更稳妥的做法是结合置换检验的 p 值、专题图和业务背景一起判断。
Q3:Moran’s I 为正就一定有热点吗?
不一定。全局 Moran’s I 为正,只说明整体上存在正空间自相关。它不能直接指出热点位置。要找具体热点或高值聚集区,需要使用局部莫兰指数、LISA 聚类图或 Getis-Ord Gi* 热点分析。
Q4:为什么我算出来的 PySAL 莫兰指数和别人不一样?
常见原因包括研究范围不同、空间单元尺度不同、字段处理方式不同、空间权重矩阵不同、是否行标准化不同,以及是否处理了孤岛单元。莫兰指数对这些设置比较敏感,所以复现实验时必须记录完整参数。
Q5:点数据可以计算莫兰指数吗?
可以。点数据通常不使用 Queen 或 Rook 邻接,而是使用 KNN 权重或距离阈值权重。需要特别注意坐标系,距离权重应在合适的投影坐标系下计算,不能随意用经纬度坐标直接当作米制距离。
Q6:PySAL 能不能计算局部莫兰指数?
可以。PySAL 的 esda 模块提供了局部 Moran’s I 相关方法。全局莫兰指数用于判断整体空间自相关,局部莫兰指数用于识别局部高高、低低、高低、低高等空间聚类类型。两者适合配合使用。
结论:先定义邻居关系,再解释莫兰指数
结论:使用 PySAL 做空间统计时,莫兰指数怎么算并不只是调用一个函数。完整流程应该包括数据清理、坐标系检查、空间权重矩阵构建、Moran’s I 计算、显著性判断和地图验证。
对于初学者,建议先从面数据的 Queen 邻接权重开始,计算全局莫兰指数,并同时查看 p 值、z 值和专题图。等理解全局空间自相关以后,再进一步学习局部莫兰指数和热点分析。
记住一个原则:莫兰指数回答的是“是否存在整体空间自相关”,不是“热点在哪里”。只要把这个边界分清,PySAL 莫兰指数就是非常实用的 Python GIS 空间统计入门工具。