空间计量方法这几年越来越常见,做区域经济、城市规划、环境健康、房地产估价的朋友,基本绕不开地理加权回归(GWR),但真跑起来很多人会发现一个别扭的前提:传统GWR让所有自变量共享同一个带宽(bandwidth),而现实中变量几乎不可能作用在同一空间尺度上。MGWR(多尺度地理加权回归)正是为打破这一限制而生的,它给每个自变量分配独立的最优带宽,通过后向拟合迭代校准,让数据自己告诉我们每个变量的作用尺度。这篇文章我从环境准备、数据清洗、建模执行到结果解读,完整走一遍Python实现MGWR的实战全流程,并把我调试过程中踩过的坑一并讲透。适合有一定Python和回归基础、正在准备空间统计论文或需要做空间异质性分析的从业者,如果是完全的新手,建议先把Pandas和基础回归模型跑顺了再回来看。
1. 为什么传统GWR不“适配”你的数据:从单带宽到多带宽的转变
1.1 传统GWR的隐藏前提:所有变量共享一个带宽
GWR的基本思想不复杂:在普通线性回归的框架上,对每一个空间位置赋予一个权重矩阵,离这个位置近的样本点在回归估计中权重更大,远的权重小甚至为零。这个“距离衰减”的尺度由一个参数控制,就是带宽。带宽越小,模型越“局部”,系数随空间的变化也就越剧烈;带宽越大,模型越接近全局回归。
问题在于,传统GWR默认一个模型只有一个带宽。也就是说,所有解释变量都按同一个衰减尺度来加权。这个假设在数学上很好处理,但在实际空间过程中几乎站不住脚。我举个例子:你在研究城市房价,解释变量有“到CBD的距离”和“小区绿化率”。到CBD的距离对房价的影响往往在市中心周围几公里内变化明显,是强局域过程;而绿化率的影响可能在整个城市尺度上都相对稳定,接近全局过程。这两个变量本质上是两把不同的“尺子”在发挥作用,强行用一个带宽去统一度量,结果就是:要么为了拟合局部变量把带宽调小,导致全局变量的系数被噪声牵着走;要么为了全局变量把带宽调大,导致局部变量的空间异质性被平滑掉了。
从统计角度说,GWR的单一带宽相当于对模型施加了一个隐含约束:所有变量的空间非平稳性强度相同。这个约束通常不成立,所以GWR的系数估计是有偏的,局部系数图上的很多“规律”实际上是带宽妥协的产物,而非真实空间过程的反映。
1.2 MGWR的改进逻辑:每个变量配一把尺子
MGWR的核心改动就是去掉这个约束。它的模型形式可以写成:
yᵢ = βᵢ₀ + Σₖ β_bwk,ᵢ · xᵢₖ + εᵢ
这里的下标bwk代表第k个解释变量拥有自己的带宽。通俗说,MGWR是“变量级别的GWR”,每个解释变量单独做一次局部回归,用各自最优的带宽来权衡邻域范围。
校准算法上,MGWR不再是一个带宽搜索,而是一个迭代过程。它用后向拟合(backfitting)的思路:先假设一组初始带宽,然后逐个变量迭代更新,每更新一个变量时把其他变量当前已有的拟合结果扣除,只对残差部分做局部回归,再优化该变量自己的带宽,反复循环直到收敛。这也是MGWR比GWR计算量大的主要原因——它内部嵌套了多轮GWR式的带宽寻优。
在实际效果上,MGWR带来的好处不只是拟合优度提升。更关键的是,它可以回答“每个变量到底在多大空间尺度上起作用”这个问题。带宽数值本身承载了极强的空间尺度语义,这一点是传统GWR完全给不了的。对科研论文来说,这是很好的分析素材;在业务分析里,尺度信息也能帮助判断干预措施应该在哪一级空间单元(社区、街道、全区)落地。
1.3 多尺度结果的常见形态
带宽结果出来以后,一般会出现三种形态:
- 部分变量带宽接近样本总量n:说明该变量的作用范围是全区域性的,空间异质性很弱,可以视为全局平稳变量。
- 部分变量带宽中等,比如占样本量的20%-50%:说明存在中等尺度的空间分层规律。
- 部分变量带宽很小,只覆盖几个近邻点:说明该变量高度局域化,只在很小的地理范围内产生影响。
这其实是把“隐性尺度”显性化。我见过不少增补分析,把带宽结果的语义直接写成“变量X的影响主要存在于Y米左右的邻域范围”,审稿人和业务方都很买账。这种判断要从原始距离单位下的带宽反算实际距离,后面第5节会详细说。
2. Python环境搭建与数据规范化处理
2.1 环境安装与版本兼容(mgwr库的坑)
MGWR的Python实现主要靠mgwr这个库。安装很简单:
pip install mgwr geopandas libpysal但有几个版本坑值得提前打预防针:mgwr依赖spglm和spreg,在安装时经常出现依赖冲突,尤其是跟numpy、pandas的版本来回拉扯。我的建议是用一个独立的虚拟环境,不要直接装到系统Python里。我习惯用conda先建一个干净的环境:
conda create -n mgwr_env python=3.9 conda activate mgwr_env pip install mgwr geopandas libpysal matplotlib为什么选Python 3.9而不是最新的3.12?因为mgwr的更新节奏不算快,在老版本Python上兼容性表现最稳。你要是用最新版Python,大概率会遇到某个依赖包编译失败的报错。这不是说3.12不能用,只是没必要在环境问题上浪费调试时间。
导入库的时候,很多教程里写的是:
from mgwr.gwr import GWR from mgwr.sel_bw import Sel_BW from mgwr.mgwr import MGWR from mgwr.sel_bw import Sel_BW_MGWR以mgwr 2.1.2左右的版本为准,这套导入是没问题的。但注意不同小版本的API可能有微调,如果你发现MGWR导入报错,查一下mgwr.__version__,再去找对应版本的文档,别硬凭记忆写代码。
2.2 数据格式与坐标系处理:最容易翻车的一步
MGWR需要三个核心输入:坐标数组coords、因变量向量y、自变量矩阵X。读取数据的标准姿势是用geopandas读Shapefile或GeoJSON:
import geopandas as gpd gdf = gpd.read_file("house_data.shp")然后提取坐标。这里有一个我见过无数人踩的坑:直接把经纬度(WGS84)作为坐标传给模型。mgwr内部用的是欧氏距离,经纬度是度、分、秒这种角度单位,用它算出来的距离毫无物理意义,带宽结果也没法解读。正确的做法是先把数据投影到合适的平面坐标系(投影坐标系CRS),再取坐标。
gdf = gdf.to_crs("EPSG:3857") # Web墨卡托,单位是米 coords = list(zip(gdf.geometry.x, gdf.geometry.y))研究区范围和投影选择可以参考这张表:
| 研究区场景 | 推荐坐标系 | 说明 |
|---|---|---|
| 全国范围 | 适合的等距/等积投影 | 避免高纬度变形过大 |
| 单一城市或县域 | 本地UTM带或地方坐标系 | 距离精度高,直接以米为单位 |
| 街道/社区尺度 | UTM或城市独立坐标系 | 允许较小带宽的精细解读 |
| 跨多个投影带的大区域 | 慎用单一投影 | 需做敏感性分析交代边界条件 |
而且要说明的是,如果你处理的是面数据(比如社区、街道这类多边形),直接用geometry.x和geometry.y拿到的是几何对象的代表性坐标点,并不是真正的质心。严谨的做法是先算质心再取坐标:
gdf["centroid"] = gdf.geometry.centroid coords = list(zip(gdf.centroid.x, gdf.centroid.y))坐标系这一步别偷懒。投影选得好不好,直接影响带宽的实际距离可比性和最终系数图的平滑程度。研究区范围小、局部差异明显的,优先选该地区的UTM带或者地方坐标系,而不是全球通用墨卡托——后者在高纬度地区距离变形大。
3. 数据清洗、共线性诊断与空间权重矩阵构建
3.1 数据结构化与坐标提取
拿到原始数据后,先别急着丢进模型。MGWR不像sklearn里的模型那样对数据格式宽容,它要求:
- 所有变量必须是数值型,字符串变量要先做编码或哑变量处理。
- 不允许有缺失值,任何一个NaN都会让模型直接报错或者输出无意义的系数。
- 样本之间的单位尽量统一,混杂多种单位会让带宽搜索的过程极其不稳定。
这些都是常规要求,但有一个容易忽略的点:数据中如果有重复坐标点,也就是多个样本落在完全相同的空间位置上,空间权重矩阵计算时容易出现权重异常。碰到这种情况,我一般先做一次去重或者合并。
我处理空间数据时的标准动作是先看一下数据概况:
print(gdf.shape) print(gdf.isnull().sum()) print(gdf.geometry.is_valid.sum(), "个有效几何")然后构造建模用的数据矩阵:
X = gdf[["distance_cbd", "green_rate", "school_num"]].values.astype(float) y = gdf["price"].values.astype(float) coords = list(zip(gdf.centroid.x, gdf.centroid.y))astype(float)这一下值得保留。因为很多从Excel或者CSV读进来的数值字段,实际上是object类型,直接传给mgwr会报类型错误,先统一转成float最省事。
3.2 多重共线性诊断:VIF和相关系数矩阵怎么用
MGWR虽然对每个变量分别做局部回归,但它本质上仍然是线性回归框架,多重共线性依然是硬伤。更麻烦的是,MGWR在迭代校准的时候会反复对自变量矩阵做运算,共线性严重时带宽搜索经常抖动甚至不收敛——最直接的体感就是:每次运行结果不一样,迭代到一半报矩阵奇异。
我通常在建模前做两层检查:一是相关系数矩阵,二是VIF(方差膨胀因子)。相关系数矩阵适合快速扫雷,VIF适合定量判断。VIF大于10已经是红色警戒,大于5就要开始警惕。
import pandas as pd from statsmodels.stats.outliers_influence import variance_inflation_factor df_vif = X_df.copy() df_vif["intercept"] = 1 vif = pd.Series( [variance_inflation_factor(df_vif.values, i) for i in range(df_vif.shape[1])], index=df_vif.columns, ) print(vif)需要注意的是,VIF的阈值只是经验值,不绝对。但如果你的研究目的中包含“解读每个变量的独立影响”,共线性高的变量一定会在MGWR系数图上表现为两条“阴阳对称”的带状模式——一个变量系数偏高,另一个变量系数偏低,看着像有规律,其实只是共线性在空间上的表达。对这种情况,我的经验是优先考虑剔除业务解释力较弱的一方,或者做变量整合(比如PCA降维后再建模,但代价是损失可解释性)。
3.3 空间权重矩阵的构建逻辑
MGWR不用你手动传空间权重矩阵,带宽搜索过程会自动生成。但理解它的构建逻辑对调参非常重要。空间权重矩阵的核心是两点:核函数和带宽。
核函数决定距离衰减的形状。常用的有高斯核(gaussian)和双平方核(bisquare)。高斯核在带宽范围内权重平滑递减但不会降到0,所有样本都参与估计,只是权重极小的样本影响可以忽略;双平方核在带宽范围内递减、带宽外直接为0,相当于有一个明确的截断距离。实际项目中我一般默认用双平方核,因为空间过程通常存在一个实际的影响阈值,截断在业务解释上更清晰。如果样本分布特别稀疏、样本量小,高斯核更稳妥。
带宽则决定“多近算近”。你可以手动指定,也可以让模型搜索。手动指定的场景很少,主要用在模型对比——比如你强制让MGWR所有变量都用同一个带宽,就跑成了GWR。搜索方式上,MGWR默认用的是黄金分割搜索,迭代次数用GCV(广义交叉验证)或AICc来评估。
这里有一个我自己的习惯:先跑一遍GWR的带宽搜索,把结果作为MGWR带宽的初始参考。因为MGWR的后向拟合对初始带宽比较敏感,一个合理的初始值能明显减少迭代次数、提高收敛稳定性。这一步在代码里不算额外负担,十几行的事,但收益很明显。
4. MGWR建模核心流程:从带宽搜索到模型拟合
4.1 模型初始化与关键参数(核函数、搜索方法)
数据准备完之后,建模代码的核心框架如下:
from mgwr.gwr import GWR from mgwr.sel_bw import Sel_BW from mgwr.mgwr import MGWR from mgwr.sel_bw import Sel_BW_MGWR # 先跑一个GWR作为MGWR的初始带宽参考 gwr_selector = Sel_BW(coords, y, X, kernel='bisquare') gwr_bw = gwr_selector.search() # MGWR带宽搜索 mgwr_selector = Sel_BW_MGWR(coords, y, X, kernel='bisquare', gwr_bw=gwr_bw) mgwr_bws = mgwr_selector.search() # 拟合MGWR mgwr_model = MGWR(coords, y, X, kernel='bisquare', selector=mgwr_selector).fit()这里有几个参数值得拆开说。
kernel:我上面提过,一般用bisquare。如果你的数据点特别稀疏,用gaussian更不容易在某些带宽下出现局部样本不足的问题。
search:Sel_BW默认用golden_section,对MGWR来说,由于要反复迭代,默认设置已经够用。只有在某些变量死活不收敛的时候,我才会把search='interval'或者增大搜索步数。
max_iter:MGWR后向拟合的最大迭代次数,默认值通常是200。如果模型跑满了迭代次数还没收敛,结果里会有警告。这时候不要简单粗暴地把max_iter加到2000,先回头检查数据质量和共线性,90%的问题出在输入上。
gwr_bw:这个参数非常关键,它给MGWR的每个带宽搜索提供了一个初始锚点。我第一次跑的时候不懂,直接空着没传,模型也能跑,但迭代次数多了不少,收敛也不稳定。后来养成习惯,先跑GWR拿初始带宽,再传给MGWR。
4.2 带宽优化与模型拟合代码实操
实际运行中,还有一件事容易被忽略:X和y的量纲差距过大时,带宽搜索的过程会异常敏感。比如房价数量级是百万,而你某个变量的数量级是0.01,矩阵运算过程中数值尺度差异会在迭代时被放大。我的习惯是建模前先做标准化(z-score),建模后再做逆变换,把系数还原到原始尺度。
from sklearn.preprocessing import StandardScaler scaler_y = StandardScaler() scaler_X = StandardScaler() y_scaled = scaler_y.fit_transform(y.reshape(-1, 1)).ravel() X_scaled = scaler_X.fit_transform(X)然后用标准化后的数据跑MGWR。跑完以后,带宽结果不受标准化影响,因为带宽用的是空间距离而不是变量数值尺度;但系数矩阵需要重标定到原始单位才能解释。由于MGWR的系数是逐点的,不能用简单的均值反变换,要逐点做:
# 对每个点、每个变量,用标准差的比值还原系数 coef_original = mgwr_model.params[:, 1:] * (scaler_y.scale_ / scaler_X.scale_)第0列是截距项,还原时要考虑均值的偏移:
intercept_original = ( scaler_y.mean_ + mgwr_model.params[:, 0] * scaler_y.scale_ - (coef_original * scaler_X.mean_).sum(axis=1) )这段代码我踩过不只一次坑。如果不还原,画出来的系数图数值上是“标准化后的系数”,业务上解读成“房价每上涨100万对应XX”完全对不上。
4.3 模型结果提取与GWR对比评价
拟合完成后,最关心的几个输出是:
mgwr_model.bw:每个变量的最优带宽数组。mgwr_model.params:每个样本点的局部系数估计,形状是(n, k+1)。mgwr_model.summary():包含模型AICc、R²、调整R²和各变量带宽的汇总表。mgwr_model.predy和mgwr_model.resid_response:拟合值和残差。
对比GWR和MGWR,我建议不只是看R²提高了多少。R²提升是次要的,更重要的是带宽结构的合理性。在GWR里只有一个带宽,假设是80;在MGWR里,可能截距带宽是300(接近全局)、地铁距离带宽是35(强局域)、绿化率带宽是180(中等尺度)。这个结构本身就是模型对数据空间过程的一个“体检报告”。
模型的summary输出要重点关注两个统计量:AICc和调整R²。AICc用于模型比较,同一样本下,AICc越低说明模型在拟合优度和复杂度之间平衡得越好。MGWR的AICc通常低于GWR,因为虽然每个变量多了一个带宽参数,但解释能力的提升足以覆盖这个代价。调整R²则是防止你只看R²被自由度欺骗,MGWR的变量多了、拟合值自然更好,但调整R²才体现真实增益。
实际分析里,我还会把MGWR和GWR的局部系数做相关性对比。如果两个模型的系数图长得完全一样,说明数据里空间过程的尺度差异其实不明显,MGWR只是“形式上多尺度”,并没有带来实质性新信息。这种情况在业务报告中跟人解释时最好先坦白承认。
5. 结果解读与空间可视化:让模型输出可解释
5.1 带宽的尺度语义:如何解读“多尺度”
拿到带宽数组后,第一个动作是把它们还原为实际距离。带宽值的单位和你传给模型的坐标单位一致。如果你用的是投影坐标,单位是米,那么带宽值就是米。比如样本数是300,某个变量带宽是25,说明该变量只受约25个近邻样本影响的区域尺度;如果带宽是280,基本上就是全局尺度,和OLS的结果没有本质差别。
换算成实际距离还有一个更直观的方式:带宽对应的搜索半径。如果研究区域是一个市区,坐标单位是米,带宽40意味着局部邻域大约就是几十个街区的范围。你在报告里可以直接写“该变量的影响主要存在于半径约X公里的范围内”,这就是尺度语义。
这里要特别提醒:不同变量带宽之间的比值关系,比绝对数值更有分析价值。比如A变量带宽是B变量的两倍,说明A的空间作用尺度是B的约两倍。而在GWR里根本没有这个维度可以比较,你只能比较系数大小,无法比较作用尺度。这也是MGWR最值得强调的增量信息。
实际解读时,我一般把变量分成三类来讨论:
| 带宽类型 | 典型占比 | 解读方向 |
|---|---|---|
| 全局型 | 接近样本量n | 系数空间变化不大,讨论平均效应 |
| 中等尺度 | n的20%-50% | 存在空间分层,与区域结构相关 |
| 局域型 | 小于n的20% | 强异质性,适合空间靶向分析 |
5.2 局部系数分布图与标准化处理
MGWR输出的核心可视化是局部系数地图。做法不复杂:把系数矩阵和原始gdf合并,然后用geopandas直接出图。
gdf["coef_dist"] = coef_original[:, 0] # 第一个解释变量的系数 fig, ax = plt.subplots(figsize=(10, 8)) gdf.plot(column="coef_dist", cmap="RdYlBu", legend=True, ax=ax) ax.set_title("Distance to CBD Coefficient (MGWR)") plt.axis("off") plt.show()几个出图细节值得提:
- 颜色映射用
RdYlBu或viridis都行,但注意系数的正负方向要能直观区分。我更倾向RdYlBu这类发散配色,0值附近是浅色。 - 如果系数分布极其不均匀,个别点特别高,图面会被一两个极值“拉爆”。这时用分位数分级(quantile breaks)而不是等间隔分级,能让空间模式看得更清楚。
- 多变量系数图建议做成一张图多个子图,统一图例范围,方便横向比较。如果各自图例范围差别很大,视觉上会误导判断。
还有一个细节:系数达到统计显著的区域才值得讨论。MGWR本身不直接输出局部显著性,实际中常见做法是计算局部系数的t值或使用蒙特卡洛模拟做显著性检验。这个步骤虽然麻烦,但如果你要在论文里写“系数在XX区域显著为正”,它几乎是必须的。一个偷懒但可以接受的变通方案是:结合实际业务逻辑加敏感性分析,只对业务上可解释的区域展开讨论,对噪音区域明确说明是统计不稳定区域。
5.3 模型拟合优度与残差空间自相关检验
模型拟合优度除了summary里的R²和AICc,残差的空间自相关检验也是不可少的环节。普通回归假设残差独立,空间数据里,残差如果仍有显著的空间聚集,说明模型没有完全提取空间结构,缺失了某些关键变量或模型形式不正确。
用libpysal可以快速计算残差的Moran's I:
from libpysal.weights import Queen from esda.moran import Moran w = Queen.from_dataframe(gdf) moran_resid = Moran(mgwr_model.resid_response, w) print(moran_resid.I, moran_resid.p_sim)如果p值不显著,说明残差的空间自相关基本被模型吸收,这是一个很好的信号。如果显著,无非两种解释:一是遗漏了重要的空间变量(比如相邻区域的溢出效应),二是带宽选择过度平滑,把本该局域化的信息压平了。我处理过的案例中,后面这种情况反而更常见,处理办法通常是调整核函数或对部分局域变量放宽带宽约束。
另外,别忘了把预测值和实际值的关系散点图也画出来看一眼。R²再高,如果散点图右上角出现系统性欠拟合或过拟合的弧线,说明模型存在非线性结构,这时候机器学习方法可能比MGWR更合适——这也是一种有价值的结论。
6. 实测中遇到的典型问题与排查思路
6.1 模型不收敛:别硬调迭代次数,先检查输入
最常见的现象就是模型warning“达到最大迭代次数仍未收敛”。新手第一反应是调大max_iter,但我调试多次的经验是:如果100-200次迭代还不收敛,多半不是迭代次数不够,而是数据或参数有问题。
优先排查清单:
- 数据是否标准化:没标准化的情况下,某些变量尺度特别大(比如人口密度有几百上千,绿化率只有零点几),带宽迭代时震荡剧烈。
- 是否存在近似重复的坐标点:权重矩阵在重复点处计算异常,导致估计值跳动。
- 是否共线性过高:某个变量几乎能被其他变量线性表出时,局部回归的矩阵求解本身就不稳定。
- 是否用了含缺失值或无穷值的数据:这个看似低级,但我真见过因为一个
inf没清理,模型反复报奇异矩阵的案例。
按这个顺序排查,比盲目加大max_iter有效得多。还有一个细节:如果你用的是双平方核,在带宽很小、局部样本很少的情况下,局部矩阵会出现秩亏,这种情况要考虑换高斯核或增大初始带宽。
6.2 系数炸裂:共线性、异常值、标准化三连问
系数图出来之后,偶尔会出现某几个点系数异常高(比如房价系数是平均值的几十倍),这种“炸裂”现象通常有三个来源。
第一个是共线性。当两个变量高度相关时,MGWR在每个点上估计的系数会出现“跷跷板效应”,一个点在这个变量上系数极高,另一个变量系数就极低。诊断办法还是回头算VIF。
第二个是异常值。空间数据里经常混入一些数据录入错误或者测量异常点,比如某栋楼单价多打了一个零。MGWR的局部回归对异常值非常敏感,特别是在带宽小的时候,异常值会带着周围几个点一起“偏航”。处理方式是在建模前画一下因变量和自变量的箱线图,把明显离群的样本剔除或复核。
第三个是标准化不一致。我见过有人对X做了标准化但忘了对y做,或者建模时用标准化数据、输出系数时忘了还原。标准化不一致时系数分布范围完全失真。所以我在代码里固定一个流程:先标准化,建模,最后统一做逆变换。逆变换那一步在写完代码后要留一行检查,把还原后的系数均值跟OLS还原后的系数对比一下,量级如果差太多基本就是还原公式写错了。
6.3 投影坐标与距离单位引发的“神秘”结果
这个是所有坑里最容易让人迷惑的。如果有人用经纬度跑完模型,然后发现带宽结果是几千几百,但研究区域明明只有几百公里——恭喜你,你用的是角度单位。经纬度是球面坐标,直接作为平面坐标用,距离计算完全是错的,带宽数值没有“米”的概念。
解决办法前面已经说了,建模前统一投影。但还有一个细节很多人没注意:不同投影在区域不同位置的变形不一致。如果研究区横跨几个度带,用单一的投影坐标可能存在一定距离扭曲,严谨的处理是按研究区范围选择投影或者用本地坐标系统。如果担心投影选择的主观性,可以用两个不同投影跑一遍做敏感性对比,带宽差异如果太大,说明结果对投影敏感,要在论文里交代这个边界条件。
6.4 大样本下的性能优化方案
样本量上了几千甚至几万,MGWR的运算会明显变慢。原因很简单:每个变量带宽搜索要反复做局部回归,每个点都要解一个加权最小二乘问题,复杂度是O(n²)量级。
常见的优化手段有三个:一是精简变量,变量数量从6个减到4个,运算时间可能减半还多;二是先用GWR的带宽搜索找初始值,减少MGWR的迭代轮次,这个在4.1节提过;三是如果纯属演示或探索阶段,可以随机抽样一部分点先跑,确认模型稳定后再全量跑最终版本。
如果计算资源确实有限,还有一个技巧:把核函数的搜索范围适当收窄。比如你已知某个变量不可能特别局域化,可以用带宽边界参数限制搜索区间,让算法少试几个带宽值。这个在代码里不是所有版本的mgwr都开放,但如果你的库版本支持,值得用起来。
另外,如果你经常跑这类模型,建议把数据预处理、建模、可视化封装成一套函数,别每次重新拼接。我自己的经验是,MGWR这种模型调试一次时间成本很高,封装成函数后,后续换数据、调参数都只需改几行,省下的时间远比当初写函数的时间多。
最后聊一点个人体会。MGWR给我的最大感触不是它R²比GWR高了多少,也不是系数图更“好看”了,而是它提供了一种看待空间数据的新方式——从“这个地方关系强、那个地方关系弱”升级到“这个变量的尺度小、那个变量的尺度大”。这种尺度视角在空间分析里非常稀缺。
我也要提醒一句:MGWR不是万能的,它仍然是线性模型,解决不了非线性关系,也替代不了机理模型。当样本量、变量质量和共线性都没有把控好时,多尺度结果的可靠性要打问号。但只要你把数据准备这一步做扎实,MGWR在空间异质性分析中的价值是实打实的。希望这篇实战笔记能让你少踩几个我踩过的坑,把更多精力花在解读空间规律本身。