使用rlang向glm()传递权重

3

我希望通过使用rlang,将weights传递给glm()函数,而无需使用eval(substitute())do.call()方法,并使其易于理解。

这描述了一个更复杂的基础函数。

# Toy data
mydata = dplyr::tibble(outcome = c(0,0,0,0,0,0,0,0,1,1,1,1,1,1),
                                group = c(0,1,0,1,0,1,0,1,0,1,0,1,0,1),
                                wgts = c(1,1,1,1,1,1,1,1,1,1,1,1,1,1)
)

# This works
glm(outcome ~ group, data = mydata)                             

# This works
glm(outcome ~ group, data = mydata, weights = wgts)                             

library(rlang)
# Function not passing weights
myglm <- function(.data, y, x){
    glm(expr(!! enexpr(y) ~ !! enexpr(x)), data = .data)
}

# This works
myglm(mydata, outcome, group)

# Function passing weights
myglm2 <- function(.data, y, x, weights){
    glm(expr(!! enexpr(y) ~ !! enexpr(x)), `weights = !! enexpr(weights)`, data = .data)
}

# This doesn't work
myglm2(mydata, outcome, group, wgts)

(Ticks are to highlight)。

我知道这里的weights参数是错误的,我尝试了许多不同的方法,但都没有成功。实际函数将传递给purrr:map()purrr:invoke()版本,这就是为什么我想避免简单的do.call()。非常感谢您的想法。

1个回答

3
问题在于glm()可以识别提供给其weights参数的表达式,但不支持准引用,因为它使用基本的quote()/substitute()/eval()机制,而不是rlang这会导致嵌套表达式算术的问题
解决方法之一是组合整个glm表达式,然后对其进行评估。您可以使用...来提供可选参数。
myglm2 <- function( .data, y, x, weights, ... ) {
  myglm <- expr( glm(!!enexpr(y) ~ !!enexpr(x), data=.data, 
                      weights = !!enexpr(weights), ...) )
  eval(myglm)
}

myglm2(mydata, outcome, group)
# Call:  glm(formula = outcome ~ group, data = .data)

myglm2(mydata, outcome, group, wgts)
# Call:  glm(formula = outcome ~ group, data = .data, weights = wgts)

myglm2(mydata, outcome, group, wgts, subset=7:10)
# Call:  glm(formula = outcome ~ group, data = .data, weights = wgts, 
#     subset = ..1)
# While masked as ..1, the 7:10 is nevertheless correctly passed to glm()

为了遵循@lionel的建议,您可以将表达式的组合/求值封装到一个独立的函数中:
value <- function( e ) {eval(enexpr(e), caller_env())}

myglm2 <- function( .data, y, x, weights, ... ) {
  value( glm(!!enexpr(y) ~ !!enexpr(x), data=.data, 
              weights = !!enexpr(weights), ...) )
}

1
我会将 expr + eval 的内容提取到一个函数中(例如 value <- function(expr) eval(enexpr(expr)))。然后你可以使用 value(glm(!!enexpr(y) ~ !!enexpr(x)),这样会更好一些。不需要使用 eval_tidy(),因为 glm() 无法很好地处理 quosures。 - Lionel Henry
1
感谢您的建议,@lionel。function(e) {eval(enexpr(e))} 给了我一个 cannot coerce class ‘"rlang_fake_data_pronoun"’ to a data.frame 的错误。我通过使用 eval_tidy(enquo(e)) 来解决这个问题。 - Artem Sokolov
1
嗯... 这个错误非常令人惊讶,也许你打错了字?顺便说一下,直接传递这些点,不要使用!!!enexprs(...)。将捕获到的点拼接到裸表达式中比拼接到quo引用中更糟糕,因为你会失去正确的环境。如果直接传递这些点,看起来更整洁,而且更健壮和准确。 - Lionel Henry
2
哎呀,你说得对,我忘记转发调用者环境了:s。正确的函数是value <- function(e) eval(enexpr(e), caller_env())。至于enexprs(),这种方式传递点仍然不是一个好主意,在许多情况下,你会得到一个错误的函数。我会在myglm2()中添加一个weights参数,并在glm()调用中引用/取消引用它。然后正常传递点。 - Lionel Henry
3
谢谢,@lionel。我已使用你的所有建议更新了答案。 - Artem Sokolov
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接