speedglm在自定义R函数中调用时出现对象未找到错误的排查
speedglm封装到自定义函数后权重参数找不到的问题及解决方法
问题现象
直接调用speedglm拟合带权重的GLM时运行正常,但将其封装到自定义函数后,会出现「找不到对象'w'」或「找不到对象'd'」的错误:
library(speedglm) # 直接调用正常运行 m1 <- speedglm(wt ~ cyl, data = mtcars, weights = mtcars$wt) # 封装后报错 train_glm <- function(f, d, w) { speedglm(formula = f, data = d, weights = w) } m <- train_glm(wt ~ cyl, d = mtcars, w = mtcars$wt) #> Error in eval(extras, data, env) : object 'w' not found
更特殊的是:
- 不带权重的封装函数能正常运行
- 函数内用
d$wt作为权重参数仍报错,但用全局环境的数据集名称(如mtcars2$wt)则正常
原因分析
speedglm对weights这类额外参数采用了延迟评估机制:它会将weights参数作为表达式,在**公式的原始环境(通常是全局环境)**中求值,而非自定义函数的局部环境。
当你在自定义函数里传递w或d$wt时,这些变量仅存在于函数的局部作用域中,speedglm去全局环境查找自然找不到,因此报错;而使用全局环境的mtcars2$wt时,全局环境能找到该变量,所以运行正常。
解决方案
针对这个问题,有三种简单有效的修复方式:
方法1:指定评估环境为当前函数环境
直接在调用speedglm时,通过env参数将评估环境设置为自定义函数的局部环境,这样speedglm就会在函数作用域内查找权重变量:
train_glm <- function(f, d, w) { speedglm(formula = f, data = d, weights = w, env = environment()) } # 验证运行 m <- train_glm(wt ~ cyl, d = mtcars, w = mtcars$wt) summary(m)
方法2:用do.call传递权重向量
do.call会直接将权重向量的值传递给speedglm,而非传递表达式,避免延迟评估的问题:
train_glm <- function(f, d, w) { do.call(speedglm, list(formula = f, data = d, weights = w)) } # 验证运行 m <- train_glm(wt ~ cyl, d = mtcars, w = mtcars$wt) summary(m)
方法3:将权重加入数据集后用列名引用
把权重列添加到传入的数据集里,然后用列名作为weights参数,这样speedglm会从data参数中读取权重,无需额外查找环境:
train_glm <- function(f, d, w_col) { # 临时添加权重列到数据集 d$.weights <- d[[w_col]] # 用列名指定权重 model <- speedglm(formula = f, data = d, weights = .weights) # 清理临时列(可选) d$.weights <- NULL return(model) } # 验证运行 m <- train_glm(wt ~ cyl, d = mtcars, w_col = "wt") summary(m)
验证结果
以上三种方法都能成功拟合模型,且结果与直接调用speedglm的结果完全一致。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

