通过BIOMOD2进行白芷未来气候适宜性预测——承接前文的后续投影实践
上一篇文章已经完成了白芷在当前气候条件下的物种分布建模、评估与集成预测。真正有价值的下一步,不只是看“现在哪里适合”,而是继续回答“未来哪些区域还适合、适合性会如何变化”这个问题。这个步骤通常被称为未来情景投影,也是物种分布模型在气候变化研究中的核心应用之一。
这篇文章就接着前文,介绍如何在已经保存好 BIOMOD2 建模对象的前提下,直接开展后续预测。整个流程的重点很简单:先准备未来气候环境变量,再把它们与训练阶段的变量顺序和名称对齐,最后调用 BIOMOD2 的投影与集成预测接口,输出未来情景下的白芷适宜性分布图。
一、前提条件
这一部分默认你已经完成了当前气候下的建模,并保存了 biomod_objects.rds,其中至少包含:
1 2 3 4
| objs <- readRDS("outputs/Angelica_dahurica/biomod_objects.rds") biomod_model_out <- objs$biomod_model_out biomod_em <- objs$biomod_em
|
换句话说,后续预测不需要重新训练模型,只需要复用已经筛选和集成好的模型对象。这样做的好处很明显:
- 节省大量计算时间;
- 保证未来投影与当前模型完全一致;
- 便于批量切换不同 SSP 情景和未来时间段。
二、准备未来气候数据
未来预测最容易出错的地方,不是模型本身,而是环境变量的准备。未来栅格文件必须满足两个条件:
- 变量种类与当前建模阶段一致;
- 变量名称和顺序与训练时严格一致。
通常建议把未来环境变量单独放进一个目录,例如:
1 2 3 4 5 6
| future_env/ └── ssp126_2041-2060/ ├── bio1.tif ├── bio2.tif ├── bio3.tif └── ...
|
如果你后面还要预测 SSP245、SSP585,或者 2021-2040、2061-2080 等时间段,可以继续按同样结构扩展目录。这样脚本只需要改一个路径,就能切换情景。
三、读取已保存对象
未来预测脚本的第一步,是直接读取前文保存的模型对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| setwd("/data2/xty/zqf/gzk")
library(terra) library(sp) library(dismo) library(ggplot2) library(viridis) library(biomod2) library(R.utils) library(randomForest) library(gbm) library(mgcv) library(stats)
output_dir <- file.path(getwd(), "outputs", "Angelica_dahurica")
objs_path <- file.path(output_dir, "biomod_objects.rds")
if (!file.exists(objs_path)) { stop("未找到 biomod_objects.rds,请先运行当前气候脚本到保存对象步骤。") }
objs <- readRDS(objs_path) biomod_model_out <- objs$biomod_model_out biomod_em <- objs$biomod_em
|
这段代码的意义在于:当前模型和集成模型已经被固定下来,后续只做投影,不再重新拟合。
四、加载未来环境变量
未来栅格建议使用 terra 统一处理。它比传统的 raster 更适合大栅格数据,内存占用也更友好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| future_env_dir <- file.path(getwd(), "future_env", "ssp126_2041-2060")
if (!dir.exists(future_env_dir)) { stop(paste("未来环境目录不存在:", future_env_dir)) }
future_files <- list.files(future_env_dir, pattern = "\\.tif$", full.names = TRUE)
if (length(future_files) == 0) { stop("未来环境目录中未找到 .tif 文件") }
future_stack <- terra::rast(future_files)
|
如果未来文件本身没有正确命名,可以再补一层处理:
1 2 3 4 5 6 7 8
| train_var_names <- biomod_model_out@expl.var.names
if (is.null(names(future_stack)) || any(names(future_stack) == "")) { names(future_stack) <- tools::file_path_sans_ext(basename(future_files)) }
|
这一段看起来简单,但很关键。BIOMOD2 在投影时并不是只看“文件里有多少层”,而是看这些层是否能与训练时的变量一一对应。名称对不上,后面就会报错;顺序不对,结果也可能失真。
五、检查变量一致性
投影前最好先做一次显式校验:
1 2 3 4 5 6 7 8 9
| missing_vars <- setdiff(train_var_names, names(future_stack))
if (length(missing_vars) > 0) { stop(paste("未来环境缺少变量:", paste(missing_vars, collapse = ", "))) }
future_stack <- future_stack[[train_var_names]]
|
这里的逻辑非常直接:
setdiff() 用来找出训练阶段有、未来数据里没有的变量;future_stack[[train_var_names]] 用来按训练时的变量顺序重新排列。
只要这一步做对,后续投影就会稳很多。
六、开展未来投影
接下来就进入核心步骤:单模型投影和集成预测。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| proj_name <- "future_SSP126_2041-2060"
biomod_proj_future <- BIOMOD_Projection( bm.mod = biomod_model_out, new.env = future_stack, proj.name = proj_name, models.chosen = "all", metric.binary = "TSS", nb.cpu = 1 )
gc()
biomod_em_proj_future <- BIOMOD_EnsembleForecasting( bm.em = biomod_em, bm.proj = biomod_proj_future, proj.name = proj_name, models.chosen = "all", metric.binary = "TSS", nb.cpu = 1 )
|
这一步的含义可以这样理解:
BIOMOD_Projection() 先把训练好的单个模型投到未来环境中;BIOMOD_EnsembleForecasting() 再基于集成模型生成最终的综合结果。
如果你前面在集成建模阶段使用了 EMmean、EMcv 和 EMwmean,这里输出的也会是对应的一套集成投影结果。
这一步对于计算资源的要求可能比较高,尤其是当未来环境变量较多、模型数量较多时。建议在服务器上运行,并且根据实际情况调整 nb.cpu 参数以加速计算。
七、导出结果文件
完成预测后,建议把结果统一写到单独的输出目录里,避免和当前气候结果混在一起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| output_path <- file.path(output_dir, paste0("proj_", proj_name))
dir.create(output_path, recursive = TRUE, showWarnings = FALSE)
unpacked_val <- terra::unwrap(biomod_em_proj_future@proj.out@val)
writeRaster( unpacked_val, filename = file.path(output_path, "Angelica_SSP126_2041-2060_ensemble.tif"), overwrite = TRUE )
if (nlyr(unpacked_val) >= 1) { writeRaster(unpacked_val[[1]], file.path(output_path, "Angelica_SSP126_2041-2060_EMmean.tif"), overwrite = TRUE) }
if (nlyr(unpacked_val) >= 2) { writeRaster(unpacked_val[[2]], file.path(output_path, "Angelica_SSP126_2041-2060_EMcv.tif"), overwrite = TRUE) }
if (nlyr(unpacked_val) >= 3) { writeRaster(unpacked_val[[3]], file.path(output_path, "Angelica_SSP126_2041-2060_EMwmean.tif"), overwrite = TRUE) }
message("未来气候预测完成,结果已保存至: ", output_path)
|
导出后,你会得到一个总的集成结果文件,以及若干拆分后的子结果。这样后续无论是做制图、阈值分类,还是和当前气候结果做差值分析,都会方便很多。
八、可选的自动提醒
如果你经常在本地或服务器上跑长时间任务,可以顺手加一个 Bark 推送,脚本跑完后直接收到通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| bark_base_url <- "https://api.day.app/你自己设备的BarkKey/"
bark_title <- "BIOMOD2"
bark_body <- paste0("未来气候预测完成,结果已保存至: ", output_path)
bark_url <- paste0( bark_base_url, utils::URLencode(bark_title, reserved = TRUE), "/", utils::URLencode(bark_body, reserved = TRUE) )
try({ system(paste("curl -fsS", shQuote(bark_url)), ignore.stdout = TRUE, ignore.stderr = TRUE) }, silent = TRUE)
|
这部分不是必须,但对于长时间批量投影很实用。
九、后续可以怎么扩展
如果你想把这套流程继续做得更完整,下一步通常有三种扩展方向:
- 批量跑多个 SSP 情景,比如 SSP126、SSP245、SSP585;
- 比较不同时间段的变化,比如 2031-2050、2041-2060、2061-2080;
- 把未来结果和当前结果做差值分析,输出适宜区扩张、收缩和迁移方向。
其中第三步往往最有论文价值,因为它不仅能告诉你“未来哪里适合”,还可以进一步解释“适宜区是增加了还是减少了”。
十、总结
白芷的未来适宜性预测,本质上是把前文训练好的 BIOMOD2 模型,迁移到气候变化情景下重新评估其空间适宜度。只要确保模型对象已保存、未来环境变量与训练变量完全一致,再用 BIOMOD_Projection() 和 BIOMOD_EnsembleForecasting() 依次完成单模型和集成投影,就能得到相对可靠的未来分布结果。
AIGC声明
本文内容使用AI根据源代码生成,可能存在不准确或不完整的地方,请读者自行核实相关信息。