R中通过purrr::map传ARIMA/ETS字符串调用fabletools失效问题
失效原因
- R本身完全支持把字符串形式的函数名做动态调用,不存在语法层面的阻碍
- 你写的
fable::x(Trips)是静态硬编码写法:R的::操作符是编译阶段就解析的,会直接去fable命名空间找字面叫x的函数,根本不会读取你存在变量x里的字符串值,所以必然运行报错
解决方案
要实现动态传模型名做批量拟合,核心是在运行期把字符串转成可调用的函数实体,以下两种写法完全适配fable生态的接口要求:
- 用
get()从指定包的命名空间取出函数实体:比如要取fable包里的ARIMA函数,就写get("ARIMA", envir = asNamespace("fable")),拿到函数后直接传参model_fn(Trips)即可 - 用
rlang::call2()动态构造函数调用表达式,再通过!!!展开传入model()接口,这种写法和fabletools对表达式的解析逻辑完全兼容
修正后的可运行代码
library(tidyverse) library(fable) library(fabletools) library(tsibble) tourism <- tsibble::tourism my.model.names <- c("ARIMA", "ETS") # 逐次循环拟合的实现 fit_results <- map2( .x = my.model.names, .y = my.model.names, .f = function(model_name, result_name){ # 从fable包命名空间动态取对应模型函数 model_fun <- get(model_name, envir = asNamespace("fable")) # 正常拟合流程 tourism %>% dplyr::filter(Region == "Adelaide") %>% fabletools::model( !!result_name := model_fun(Trips) ) } )
更高效的批量拟合写法
不需要逐次循环调用model(),fabletools本身支持一次传入多个模型表达式直接批量拟合,性能和可维护性更好:
# 动态构造所有模型的调用表达式 model_calls <- my.model.names %>% set_names() %>% map(~ rlang::call2(.x, quote(Trips))) # 一次性传入model接口完成批量拟合 full_fit <- tourism %>% dplyr::filter(Region == "Adelaide") %>% fabletools::model(!!!model_calls)
注意事项
- 不要硬写
包名::变量名()的动态调用结构:::是静态解析的语法,不会读取运行期的变量值,所有动态取包内函数的操作都要通过get(envir = asNamespace(包名))实现 - 不推荐用
assign()把结果直接写到全局环境:会污染全局工作区,通过map()的返回值承接结果更稳定 - 动态构造表达式的时候,用
!! :=的命名写法可以自动匹配列表里的模型名作为拟合结果的列名,不需要手动调整结果名称
内容的提问来源于stack exchange,提问作者Christian Moczkowski
相关产品推荐
相关产品推荐

