带因子分组张量平滑的mgcv GAM绘图、预测与交叉验证问题
GAM模型预测、绘图与交叉验证问题解答
1. 基础概念确认
1.1 type="link"的含义
type="link"返回的就是对数链接尺度的线性预测值,包含所有模型项(截距、分类主效应、平滑项、offset)的加总结果,还未经过链接函数的逆变换。
2. plot.gam输出与自定义预测结果差异的原因
差异主要来自三个核心逻辑的不同:
- 输出内容不同:
plot.gam默认绘制的是单个平滑项的边际独立效应,会自动将其他模型项(截距、size_bin主效应、其他平滑项、offset)的贡献归零,仅展示当前选中的te张量项的效应值;而你自定义预测时默认是所有模型项的总贡献,且手动排除了海流项,两者的数值范围本身就会有巨大差异。 - 网格过滤规则不同:
plot.gam的too.far=0.05参数会自动剔除距离原始观测点过远的网格点,避免外推误差;如果你自定义预测时没有做对应的过滤,会包含大量无观测支撑的外推区域,视觉效果差异明显。 - 因子by项的处理不同:
plot.gam会自动将by因子的其他水平设为参考水平,仅展示当前水平下的平滑项效应,自定义预测时如果没有严格按每个size_bin单独生成预测值、单独绘图,也会出现错位。
3. 分组三向交互项的最优预测绘图方案
按以下步骤操作可以实现结果对齐、符合研究需求的绘图:
- 构建标准化预测网格
- 覆盖研究范围内的经纬度、年份的合理取值,每个
size_bin(small/med/large)都完整重复一遍所有时空网格点 - 如果要绘制单位调查 effort的丰度,将
plots_sampled统一设为1,此时offset(log(plots_sampled))=0,不需要额外处理offset - 海流变量
vo、uo不需要手动赋值,后续通过exclude参数剔除其贡献即可
- 覆盖研究范围内的经纬度、年份的合理取值,每个
- 调用
predict做精准预测- 首先运行
names(count_te_model.xy.vo.I$smooth)获取所有平滑项的准确名称,避免exclude参数写错失效 - 如果你要绘制总丰度的时空趋势(剔除海流影响):调用
predict(模型, 新数据=预测网格, type="response", exclude=c("海流相关平滑项的完整名称")),此时返回的就是原始计数尺度的、单位调查 effort下的丰度预测值 - 如果你要绘制和
plot.gam对齐的边际平滑效应:调用predict(模型, 新数据=预测网格, type="terms"),提取对应时空交互平滑项的列,再加上对应size_bin的主效应和截距即可
- 首先运行
- 匹配
plot.gam的过滤规则
调用mgcv::exclude.too.far函数,将预测网格中距离原始观测经纬度点过远的点的预测值设为NA,参数和plot.gam的too.far保持一致即可。 - 分面绘图
按size_bin分面、按年份切片绘制经纬度热力图,或者制作动图展示时间维度的变化趋势。
4. 链接尺度预测值的反变换方法
你使用family(model)$linkinv进行反变换的做法完全正确,比直接用exp()更规范:
- 对于当前的log链接负二项模型,
family(model)$linkinv和exp()的计算结果完全一致 - 该方法的通用性更强,如果你后续更换其他链接函数(比如cloglog、sqrt等),不需要修改代码逻辑,不会出现适配错误
5. 交叉验证代码的优化建议
你当前的交叉验证代码存在数据泄露问题,需要调整:
- 交叉验证必须仅用训练集数据拟合模型,不能用到测试集的任何信息:你现在的代码是先在全量数据集上拟合了模型,再拆分训练测试,会高估模型的外推效果
- 计算r²时,建议给计数加一个极小值避免0值报错:将
cor(log(count_est),log(count))改为cor(log(count_est + 1e-6), log(count + 1e-6)),避免出现无穷值影响结果
内容的提问来源于stack exchange,提问作者erica_sn
相关产品推荐
相关产品推荐

