☰
GWR模型+克里金法预测空气质量指数:空间异质性建模与R实践
2026/9/27 1:02:02 网站建设 项目流程

简介:这是一份面向空间统计与地理信息系统学习者的课程论文资源,基于地理加权回归(GWR)与克里金插值法,系统展示空气质量指数预测的完整研究流程。内容涵盖GWR模型原理与校准过程、回归系数显著性分析、预测结果评估,以及克里金插值生成空间分布图等关键环节,并附有数据处理与可视化方法说明。文档结构完整,从问题背景到模型原理、案例验证均有清晰论述。资源包共1个文件,为docx格式的论文文档,大小1.08MB,便于直接阅读和参考。已有1256人学习浏览,适合正在学习空间分析、地理信息系统的学生或研究人员借鉴。附录提供地理加权回归预测、克里格插值预测及地图可视化三部分Python源代码,可帮助读者复现实验、理解模型实现细节,并迁移到其他区域或污染物预测场景。

1. 应用GWR模型和克里金法预测空气质量指数:从空间异质性说起

拿到"应用GWR模型和克里金法对空气质量指数进行预测"这个标题,很多人的第一反应是:这不就是把两个空间统计方法串起来吗?实际做下来你会发现,真正的价值不在"用没用这两种方法",而在"为什么非要用它们"。空气质量指数(AQI)监测站点是离散的,但污染扩散是连续的,而且受地形、气象、工业布局影响,同一个城市不同片区的AQI变化规律根本不是一个模型能描述的。GWR模型解决的是"回归关系随地理位置变化"的问题,克里金法解决的是"残差里仍然有空间自相关"的问题,两者组合起来,相当于先把能解释的规律拿掉,再把剩下的空间结构补回来。这篇笔记适合正在做环境监测数据分析、想用站点数据生成连续污染分布图,或者被"预测精度上不去"困扰的工程师。我会直接给你一套能复现的R语言流程,以及参数怎么调、坑在哪。

2. GWR模型与克里金法:为什么组合比单一模型更能打

2.1 GWR模型的核心假设:回归系数不是唯一的

普通线性回归(OLS)假设因变量和自变量的关系在整个研究区域内是稳定的,一个回归系数管所有地方。这个假设在空气质量问题上几乎必然失实:某回归元(比如工业烟尘排放)在A区对AQI的影响可能是正向的,到了B区因为扩散条件好,影响也许是负的或者不显著。GWR模型的核心突破是允许每个空间位置都有自己的局部回归系数,公式写作:

[ y_i = \beta_0(u_i,v_i) + \sum_k \beta_k(u_i,v_i)x_{ik} + \varepsilon_i ]

其中((u_i,v_i))是第(i)个监测点的坐标,(\beta_k)随位置变化。关键在于估计局部系数时,距离该点越近的样本权重越大,权重函数通常用高斯核或bisquare核。这个思路本质上是对"空间异质性"的显式建模。实际使用中要注意,GWR并不是一个黑匣子,它的结果非常依赖带宽(bandwidth)的选择,带宽过小模型方差大,带宽过大就退化成全局回归。后面我会专门讲带宽怎么选。

2.2 克里金法:空间自相关怎么被量化

克里金法不关心自变量,它直接对空间变量本身建模,核心工具是变异函数(variogram)。它描述的是"两个点的值差异的方差"随距离变化的规律,一般写作(\gamma(h)),(h)是两点间的距离。常见的理论模型有球状、指数、高斯等,拟合时你要给出初始的块金值(nugget)、基台值(sill)和变程(range)。克里金的优势是给出预测值的同时还能给出预测方差——这对环境监管特别重要,因为你要知道哪里的预测是可靠的。但克里金有一个隐含前提:变量的空间自相关是平稳的,也就是变异函数在整个区域内一致。空气质量数据往往不满足这个条件,特别是当趋势项很强时,直接对AQI做克里金会得到"光滑但离谱"的插值面。这也是为什么要把GWR和克里金组合起来的原因。

2.3 组合策略:先回归后插值(regression-kriging)与残差克里金

最常见的组合方式叫回归克里金(regression-kriging),步骤是:先用GWR模型拟合AQI与气象、排放等自变量的关系,得到每个站点上的拟合值和残差;然后对残差做克里金插值;最后把GWR的预测面(需要逐像元计算系数)和残差插值面叠加。这样做的好处是双重的——GWR捕捉了由驱动因子导致的确定性空间变化,克里金捕捉了剩余的随机空间结构。值得注意的是,如果残差的空间自相关很弱,克里金不会带来明显提升,这时候不要硬上。我一般会在拟合完GWR后先绘制残差的变异函数,看一眼变程是不是明显大于站点平均间距,再做决定。另外,GWR的系数面本身也是可以可视化的产物,它告诉你哪个变量的影响在空间上如何伸缩,这对解释污染成因比单纯预测更有价值。

3. 数据准备与预处理:AQI数据要过哪些关卡

3.1 数据字段与坐标系统

你手头的AQI数据大概率长这样:站点编号、时间戳、AQI值、经度、纬度,可能还有PM2.5、PM10、NO2等分项浓度。GWR和克里金都需要空间坐标,而且必须是投影坐标(投影坐标用米为单位,距离才有意义)。经纬度是度,直接用会导致距离计算扭曲,尤其是高纬度地区。我建议用UTM投影,或者根据研究区选择地方坐标系。R中可以用sf::st_transform()转换。数据格式上,推荐用CSV加独立坐标字段,不要用带格式的Excel,省得读进来一堆麻烦。

下面是一段数据读取和投影转换的示例:

library(sf) # 读取CSV,包含站点经纬度和AQI值 aqi_df <- read.csv("aqi_stations.csv", stringsAsFactors = FALSE) # 转成sf对象,先声明原始坐标系为WGS84经纬度 aqi_sf <- st_as_sf(aqi_df, coords = c("lon", "lat"), crs = 4326) # 投影到UTM zone 50N(中国东部常用),单位变为米 aqi_sf_proj <- st_transform(aqi_sf, crs = 32650) # 提取投影坐标,后面gwr和克里金都用 coords <- st_coordinates(aqi_sf_proj) aqi_df$x <- coords[, "X"] aqi_df$y <- coords[, "Y"]

这里的关键是crs参数不能猜。你从公开数据源拿到的经纬度通常是WGS84(EPSG:4326),但也有可能是GCJ-02加密后的坐标,如果是后者,距离计算会引入系统性偏差。拿到数据后先拿一个已知地标的经纬度验证一下,别到跑完模型才发现坐标对不上。投影转换后,检查x、y的范围是否在你的研究区内,比如UTM 50N的x坐标应该在300000到900000之间,如果出现负值或明显离谱,说明坐标字段顺序搞反了。

3.2 缺失值与异常值处理

AQI站点数据常见的缺失情况有两种:单个时间点缺失和某个站点连续多天缺失。对GWR和克里金建模来说,你只需要一个"研究时段内每个站点有一个代表性值"的数据集,所以先要做时间聚合。我一般用日平均值,然后取一个污染季或全年的均值。如果某站点缺失率超过30%,我建议直接剔除该站点,不要试图插补,因为插补出来的值本身带有空间结构,会污染后续的残差分析。另外AQI值理论上在0-500之间,超过500就是爆表,这类异常值要单独处理——如果研究时段内有严重沙尘事件,极端值会导致变异函数被拉坏。解决方案是加虚拟变量标记污染事件,或者在建模时做对数变换。对数变换对GWR和克里金都友好,因为这两个方法都假设误差分布接近正态。

3.3 变量筛选与共线性检查

GWR模型可以容纳多个自变量,但变量之间如果有强共线性,局部估计会非常不稳定。这是因为每个位置只用了带宽内的子样本,样本量小了,共线性的破坏力更大。我的做法是先算所有候选变量的相关系数矩阵,把|r|>0.7的变量组里保留一个。更严格的检查是计算方差膨胀因子(VIF),R里可以用car::vif()。注意GWR的局部权重会导致局部VIF偏高,所以别只看全局VIF。筛选变量时,优先保留那些有明确物理意义的:风速、湿度、温度、人口密度、路网密度、工业用地比例,而不是一股脑把所有能拿到的变量塞进去。变量个数建议控制在5个以内,否则带宽内样本量不够用。

4. 用R在本地跑通GWR+克里金的最小命令

4.1 安装与加载相关包

主流实现是R的spgwr包(GWR)和gstat包(克里金)。注意spgwr开发较早,对sf对象的支持不友好,需要把数据转回SpatialPointsDataFrame。另一个选择是GWmodel包,功能更新但语法略复杂。我下面的代码兼容性优先,用spgwr加sp。安装时如果遇到编译问题,多半是系统缺少GDAL,Windows用户建议直接装预编译版。

install.packages(c("spgwr", "gstat", "sp", "sf")) library(spgwr) library(gstat) library(sp) library(sf)

4.2 构建空间数据框

spgwr需要SpatialPointsDataFrame,我们先把前面处理好的aqi_df转成这个格式。注意coords矩阵的列名必须是x和y(或coords.x1、coords.x2),否则后续函数可能报错。

spdf <- SpatialPointsDataFrame( coords = cbind(aqi_df$x, aqi_df$y), data = aqi_df ) # 打印一下确认投影坐标范围 summary(spdf@coords)

这里有一个血泪教训:spgwr的带宽优化函数gwr.sel()默认使用AICc准则,它对样本量敏感。如果你只有几十个站点,AICc会倾向选择非常大的带宽,让GWR退化为全局回归。在这种情况下,可以考虑用交叉验证法选择带宽,后面会讲到。

4.3 运行GWR模型并提取残差

先做全局OLS,用于对照和设置GWR初始参数。然后调用gwr(),并指定带宽。带宽的获取方式有两种:手动指定或用gwr.sel()自动搜索。我这里演示先用自动搜索,然后手动指定一个更稳健的值:

# 先跑一个OLS作为对照 lm_global <- lm(AQI ~ PM2.5 + wind + humidity, data = spdf@data) summary(lm_global) # 自动搜索最优带宽(AICc准则) bw_aicc <- gwr.sel(AQI ~ PM2.5 + wind + humidity, data = spdf, method = "aicc", gweight = gwr.Gauss) print(bw_aicc) # 用搜索到的带宽跑GWR gwr_res <- gwr(AQI ~ PM2.5 + wind + humidity, data = spdf, bandwidth = bw_aicc, gweight = gwr.Gauss, hatmatrix = TRUE) # 提取拟合值和残差 spdf$gwr_fitted <- gwr_res$SDF$fitted.values spdf$gwr_resid <- gwr_res$SDF$residual

gwr.Gauss是高斯核函数,gweight参数决定权重形状。带宽的单位是米,如果你的站点平均间距是5公里,带宽至少应该大于5000米,否则局部样本太少。hatmatrix=TRUE是为了后面计算预测点的杠杆值,调试时很关键。

4.4 对残差做克里金插值

在插值之前,先拟合变异函数。gstat的variogram()需要传入一个公式,左边是残差值,右边用~1表示没有趋势项。然后你选择理论模型进行拟合。这里有几个常见的选择:球状、指数、高斯。我一般先绘制经验变异函数,肉眼看趋势再定模型。

# 构建gstat对象并计算经验变异函数 vgm_data <- gstat(id = "resid", formula = gwr_resid ~ 1, data = spdf) vgm_exp <- variogram(vgm_data, cutoff = 30000, width = 3000) plot(vgm_exp) # 拟合指数模型 vgm_fit <- fit.variogram(vgm_exp, vgm("Exp", range = 15000, nugget = 0, sill = var(spdf$gwr_resid))) plot(vgm_exp, vgm_fit)

cutoff和width决定变异函数的距离范围与分组间隔。cutoff取最大站间距的60%左右比较合适,太大会导致远端样本稀少、变异函数抖动厉害。如果你看到经验变异函数的基台持续上升没有平台,说明残差可能还有趋势项,此时不要直接做克里金,应该考虑在GWR中加入更多自变量,或者改用泛克里金(Universal Kriging)对残差拟合趋势。

拟合完变异函数后,生成待插值网格。网格分辨率取决于你的预测目标——省级尺度用1公里,城市尺度用500米或更细。网格的点数不能太密,否则克里金运算慢得让人怀疑人生。我通常用expand.grid生成规则网格,然后剔除研究区外的点。

# 生成网格,范围根据站点坐标的包围盒扩展10% x_range <- range(spdf@coords[, 1]) y_range <- range(spdf@coords[, 2]) x_grid <- seq(x_range[1] - 1000, x_range[2] + 1000, by = 1000) y_grid <- seq(y_range[1] - 1000, y_range[2] + 1000, by = 1000) grid_df <- expand.grid(x = x_grid, y = y_grid) coordinates(grid_df) <- ~ x + y grid_df@proj4string <- spdf@proj4string # 克里金插值 krige_res <- krige(formula = gwr_resid ~ 1, locations = spdf, newdata = grid_df, model = vgm_fit)

krige()的第一个参数是公式,locations是已知点数据,newdata是预测位置。model必须使用fit.variogram的结果,不能直接放一个字符串。输出的var1.pred和var1.var分别是预测均值和预测方差,后者可以用于绘制置信区间。

4.5 叠加预测结果与精度评估

最后一步是把GWR的预测面和残差克里金面叠加。GWR预测面需要你在网格点上逐点计算局部系数,这一步不能直接用predict.gwr(spgwr这个函数对网格支持不好)。常见做法是:把网格点视为"位置",用GWR的局部系数套用到网格点的自变量值上。但实际中网格点上的自变量值(比如PM2.5的网格)可能要来自另一个插值或卫星反演产品。这里我用一个简化方案:把GWR的拟合值做一个薄板样条插值当作趋势面,然后叠加克里金残差。虽然严格来说这不是完整的回归克里金,但工程上足够用,精度差异不大。

# 对GWR拟合值做样条插值(作为趋势面) library(fields) tps_fit <- Tps(spdf@coords, spdf$gwr_fitted) grid_tps <- predict(tps_fit, grid_df@coords) grid_df$pred_gwr <- grid_tps # 叠加克里金残差 grid_df$pred_aqi <- grid_df$pred_gwr + krige_res$var1.pred # 留出部分站点做验证 # 假设the_data$fold已经分配了训练/测试 train_idx <- which(spdf$fold == "train") test_idx <- which(spdf$fold == "test") # 用训练集重新建模,测试集算RMSE

评估精度的指标用RMSE和R²,还有空间自相关指标Moran's I。如果测试集残差的Moran's I显著,说明预测面仍然有空间结构没捕捉到。我一般还会画一张"预测值 vs 观测值"散点图,看是否存在系统性低估值——AQI高值区经常被低估,这是所有空间插值方法的通病。

5. 避坑指南:空间预测中常见的5个坑

5.1 站点数量太少导致GWR系数剧烈抖动

现象:GWR输出的局部系数在空间上呈现明显的椒盐状,相邻两个站点的系数差异巨大,完全不符合物理规律。

原因:带宽内有效样本量不足。如果站点总数小于50,最小组有效样本可能只有十几个,回归系数自然不稳定。这属于模型的"过拟合空间噪声"。

解决:一是增大带宽,用交叉验证而不是AICc来选择带宽;二是减少自变量个数,只保留2-3个最强解释变量;三是改用局部加权平均替代全模型GWR。我自己的经验是,站点少于30个时,别碰GWR,直接用克里金加外部漂移变量(KED)会更稳。

5.2 变异函数拟合失败:经验变异函数一团乱麻

现象:variogram()画出来的点云完全非线性,怎么拟合都不收敛,或者拟合的变程只有几百米,比站点间距还小。

原因:残差中存在强离群点,或者残差本身空间自相关性极弱。另一个常见原因是坐标投影没做,经纬度当作米来用,距离尺度崩溃。

解决:先对残差做箱线图检查,剔除超过3倍四分位距的离群点;再确认坐标已投影。如果残差变程确实小于平均站间距,说明GWR已经提取掉了几乎所有空间结构,此时克里金的贡献有限,直接报告GWR结果即可,不要硬插值。

5.3 预测网格太密导致克里金内存爆炸

现象:网格分辨率设为100米,研究区有100公里×100公里,生成了一亿个点,krige()跑了一小时还没结束,最后报内存不足。

原因:克里金的协方差矩阵计算复杂度是O(n²),网格点一多,内存和CPU都扛不住。

解决:把网格分辨率放宽到1公里或更粗,或者分块插值。gstat支持maxdist参数限制最大距离,只使用变程内相邻的点,能大幅减少计算量。另外可以先用rasterize把克里金结果转成栅格,而不是保留全量点数据。

5.4 时间维度的误用:把不同月份的AQI混在一起建模

现象:模型整体R²很高,但残差在时间上呈现明显波动,春夏季低估,秋冬季高估。

原因:AQI的驱动因子有季节性(采暖、气象条件),不同季节的回归关系不同。GWR只处理空间维度,不处理时间维度,把一年数据混在一起等于掩盖了时序特征。

解决:要么按季节分别建模,用"季节+站点"作为观测单元;要么在GWR中加入月份虚拟变量或季节交互项。更高级的做法是时空加权回归(GTWR),但实现复杂度高,入门阶段建议按季节切片分别建模。

5.5 忽略预测方差导致的决策风险

现象:最终预测图很平滑,但实际监测站周边30公里外几乎没有任何数据,预测方差极大,可出图时没人看方差。

原因:克里金可以给出var1.var,但很多人只画均值面,不画方差面。这在环境执法场景里很危险——你不能把一个方差爆表的位置当作精确预测值来用。

解决:强制要求输出方差图,并在文档中标注高方差区域不可用于决策。如果方差面出现"牛眼"结构,说明局部站点密度过低,建议增加监测站点或者缩小预测区域。

6. 让预测更稳的两个进阶技巧:带宽选择与交叉验证

带宽是GWR模型里唯一的、也是最重要的调节参数。gwr.sel()默认支持两种自动选择方式:AICc和交叉验证(CV)。AICc倾向于选择较小的带宽以获得更好的拟合优度,但容易过拟合。交叉验证则通过逐一删点预测来评估预测误差,更诚实。我的习惯是:先用AICc跑一遍,看带宽对应的局部样本量;再用CV跑一遍,比较两者的RMSE。如果CV带宽比AICc带宽大很多,说明你的站点分布不均匀——密集区被过度拟合,稀疏区被欠拟合。这时我会手动设定带宽为站点平均间距的1.5-2倍,再微调。

具体实现时,gwr.sel()的method参数可以切换:

bw_cv <- gwr.sel(AQI ~ PM2.5 + wind + humidity, data = spdf, method = "cv", gweight = gwr.Gauss) print(bw_cv)

如果两个带宽的预测精度差异小于5%,优先选择更大的带宽,因为它的系数面更平滑,解释性更好。我踩过的最深的坑是拿着AICc带宽直接出图,结果系数面出现了环状伪影,后来发现是带宽太小、局部回归对站点位置过于敏感。换成交叉验证带宽后,伪影消失了。

另一个容易被忽视的细节是核函数的选择。gwr.Gauss(高斯核)对所有站点都有非零权重,即使距离很远也有微小影响;gwr.bisquare(双平方核)在带宽外权重直接归零,计算效率更高。当你的站点数量超过500时,用双平方核能明显加速。但双平方核要注意变程的设置,如果带宽设置不当,边缘区域会出现权重阶跃,导致预测面不连续。所以我个人在小样本场景下偏爱高斯核。

最后说说模型验证的纪律。我在做这类空间预测时,从来不用全部站点评价精度。正确做法是空间交叉验证——把研究区划分成若干空间块,一次留一块做测试,训练和测试之间保持空间距离。这比随机预留更贴近真实使用场景,因为随机抽出的测试点往往和训练点紧挨着,预测误差被低估。用gstat的krige.cv()可以快速做克里金的逐点交叉验证,GWR的交叉验证则要自己写循环,逻辑不复杂,但一定要按空间块切分。

空间统计这个方向,方法越高级,对数据质量的要求越高。GWR和克里金组合起来确实能提升AQI预测的精度,但它不是万能药。如果你的站点数据稀疏且分布不均,不如老老实实用全局回归加样条插值。做决策时,多看一眼预测方差图,比多看十个R²都强。这些是我几年来用空间模型做环境预测攒下的教训,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询