为何do.call无法定位列表中存储的函数?
问题原因与解决方案
这个问题的核心在于**do.call对字符串参数的处理逻辑**,以及字符串"LM"和"model$LM"在R中的本质差异:
为什么"LM"能正常运行?
当你传入字符串"LM"给map2中的.x时,do.call(.x, ...)会把这个字符串当作函数名,在当前环境中查找同名的函数对象——而你之前已经把lm赋值给了LM,所以R能找到这个函数,自然可以正常调用。
为什么"model$LM"会报错?
而当你传入字符串"model$LM"时,do.call同样会把整个字符串当作一个完整的函数名去查找,但你的环境里根本不存在一个叫model$LM的函数(model$LM是从列表model中提取元素的表达式,不是一个函数的名字)。这就是报错提示could not find function "model$LM"的原因。
简单来说:do.call不会解析字符串形式的表达式,它只会把字符串当作单纯的对象名去查找。
解决方案
要解决这个问题,你需要先把字符串形式的表达式"model$LM"解析成对应的函数对象,再传给do.call。这里有两种常用的方式:
方式1:在map2中先解析表达式
修改mutate中res的定义,先用eval(parse(text = .x))把字符串解析成实际的函数对象:
specr:::setup_specs(y = "y", x = "x", model = "model$LM" ) %>% dplyr::mutate(formula = pmap(., specr:::create_formula) ) %>% tidyr::unnest(formula) %>% dplyr::mutate(res = map2(.data$model, formula, ~do.call(eval(parse(text = .x)), list(data = df, formula = .y))))
注意:
parse(text = ...)虽然能解决问题,但如果字符串来自不可信来源,可能存在安全风险,不过在你的调试场景中是完全可行的。
方式2:提前提取函数对象,避免传入复杂字符串
更稳妥的方式是,不要给setup_specs传入字符串"model$LM",而是直接传入函数对象本身:
# 直接传入函数对象 specr:::setup_specs(y = "y", x = "x", model = list(model$LM) ) %>% dplyr::mutate(formula = pmap(., specr:::create_formula) ) %>% tidyr::unnest(formula) %>% dplyr::mutate(res = map2(.data$model, formula, ~do.call(.x, list(data = df, formula = .y))))
这种方式跳过了字符串解析的步骤,直接传递函数对象,更安全也更高效。
内容的提问来源于stack exchange,提问作者GiulioGCantone
相关产品推荐
相关产品推荐

