【问题标题】:Passing weights to glm() using rlang使用 rlang 将权重传递给 glm()
【发布时间】:2019-01-26 22:30:48
【问题描述】:

我想通过函数将weights 传递给glm(),而不必使用eval(substitute())do.call() 方法,而是使用rlang

这描述了一个更复杂的底层函数。

# 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)

(标记要突出显示)。

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

【问题讨论】:

    标签: r glm rlang tidyeval


    【解决方案1】:

    问题在于glm() 可以识别提供给其weights 参数的表达式,但不支持准引用,因为它使用基本quote() / substitute() / eval() 机制而不是@ 987654327@。 This causes problems for nested expression arithmetic.

    绕过它的一种方法是组合整个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), ...) )
    }
    

    【讨论】:

    • 我会在函数中提取 expr + eval 的东西(例如value &lt;- function(expr) eval(enexpr(expr)))。然后你可以做value(glm(!!enexpr(y) ~ !!enexpr(x)) 更好一点。不需要eval_tidy(),因为glm() 不能很好地处理quosures。
    • 感谢@lionel 的建议。 function(e) {eval(enexpr(e))} 给了我cannot coerce class ‘"rlang_fake_data_pronoun"’ to a data.frame 错误。我可以改用eval_tidy(enquo(e)) 来解决它。
    • 嗯...这个错误非常令人惊讶,也许你打错了?对了,直接传点就行了,不要用!!!enexprs(...)。将捕获的点拼接到裸表达式而不是 quosures 尤其糟糕,因为您会丢失正确的环境。如果你直接通过点,它看起来更干净,更健壮和准确。
    • 哎呀,你完全正确,我忘了转发调用者 env :s。正确的函数是value &lt;- function(e) eval(enexpr(e), caller_env())。至于enexprs(),以这种方式传递点仍然是一个坏主意,在很多情况下你会得到一个损坏的函数。我会在myglm2() 中添加一个weights 参数,并在glm() 调用中引用/取消引用它。然后正常传递点。
    • 谢谢,@lionel。我根据您的所有建议更新了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-05
    • 2019-02-27
    • 2016-08-27
    • 2015-09-26
    • 2012-06-07
    • 1970-01-01
    相关资源
    最近更新 更多