【问题标题】:scoping/non-standard evaluation issue in glm's formula in a function in RR中函数中glm公式中的范围/非标准评估问题
【发布时间】:2022-01-06 12:24:05
【问题描述】:

我有一个计算表格和模型(以及更多...)的函数:

fun <- function(x, y, formula = y ~ x, data = NULL) {
  out <- list()
  out$tab <- table(x, y)
  out$mod <- glm(formula = formula,
                 family = binomial,
                 data = data)
  out

}

在公式中,我需要使用函数调用中提供的xy(例如x = DF1$xy = DF1$y)以及来自另一个数据帧的变量(例如ab 来自DF2)。它因我的幼稚功能而失败:

fun(x = DF1$x,
    y = DF1$y,
    formula = y ~ x + a + b,
    data = DF2)
# Error in eval(predvars, data, env) : object 'y' not found

如何在函数环境中进行 glm 搜索 xy?我猜这个问题与非标准评估和/或范围界定有关,但我不知道如何解决。

示例数据:

smp <- function(x = c(TRUE, FALSE),
                size = 1e2) {
  sample(x = x,
         size = size,
         replace = TRUE)
  }

DF1 <- data.frame(x = smp(),
                  y = smp())

DF2 <- data.frame(a = smp(x = LETTERS),
                  b = smp(x = LETTERS))

【问题讨论】:

    标签: r function scope glm evaluation


    【解决方案1】:

    @DaveArmstrong 已被接受的答案是正确的。这个答案解释了为什么原始版本的代码有错误。

    @Thomas 在评论中引用文档说

    如果在数据中找不到,变量取自环境(公式),通常是调用 glm 的环境。

    “通常”这个词在这里很关键。确切的规则是附加到公式的环境是第一次计算公式表达式的环境,因为~ 实际上是一个函数。它将评估环境附加到公式对象,当您传递对象时,它会一直存在。

    如果您运行 glm(y ~ x),则无论您调用什么公式都会计算公式,因此这是“典型”情况。

    在您的示例中,您在调用时创建了公式对象

    fun(x = DF1$x,
    y = DF1$y,
    formula = y ~ x + a + b,
    data = DF2)
    

    这意味着全局环境(您进行此调用的位置)已附加到公式中,并且那里没有 y,因此您收到了错误。

    如果您通过调用使用默认的formula = y ~ x

    fun(x = DF1$x,
    y = DF1$y,
    data = DF2)
    

    没有formula 参数,它会起作用,因为默认参数是在使用它们的函数的评估框架中评估的。由于fun() 具有由参数创建的局部变量xy,这很好。

    您还问为什么data = NULL 会在@DaveArmstrong 的函数中工作。他添加了xy 使用

    data$x <- x
    data$y <- y
    

    如果您以data = NULL 开头,则第一行会将其更改为包含x 的列表,第二行添加y 组件,因此您最终会得到包含xy 的列表和这对于glm() 中的data 来说很好。

    【讨论】:

    • 非常感谢您澄清这些要点!我认为仅在需要时才评估承诺(惰性评估)。它不适用于公式吗?
    【解决方案2】:

    为什么不在函数中将xy 添加到data 中?

    fun <- function(x, y, formula = y ~ x, data = NULL) {
      if(length(x) != length(y) | 
         length(x) != nrow(data) | 
         length(y) != nrow(data))stop("x, y and data need to be the same length.\n")
      data$x <- x
      data$y <- y
      out <- list()
      out$tab <- table(x, y)
      out$mod <- glm(formula = formula,
                     family = binomial,
                     data = data)
      out
    }
    
    fun(x = DF1$x,
        y = DF1$y,
        formula = y ~ x + a + b,
        data = DF2)
    # $tab
    # y
    # x       FALSE TRUE
    # FALSE    27   29
    # TRUE     21   23
    # 
    # $mod
    # Call:  glm(formula = formula, family = binomial, data = data)
    # 
    # Coefficients:
    #   (Intercept)        xTRUE           aB           aC           aD           aE           aF           aG           aH           aI           aJ  
    # 3.2761      -1.8197       0.3409     -93.9103      -2.0697      20.6813     -41.5963      -1.1078      18.5921      -1.0857     -36.5442  
    # aK           aL           aM           aN           aO           aP           aQ           aR           aS           aT           aU  
    # -0.5730     -92.5513      -3.0672      22.8989     -53.6200      -0.9450       0.4626      -3.0672       0.3570     -22.8857       1.8867  
    # aV           aW           aX           aY           aZ           bB           bC           bD           bE           bF           bG  
    # 2.5307      19.5447     -90.5693    -134.0656      -2.5943      -1.2333      20.7726     110.6790      17.1022      -0.5279      -1.2537  
    # bH           bI           bJ           bK           bL           bM           bN           bO           bP           bQ           bR  
    # -21.7750     114.0199      20.3766     -42.5031      41.1757     -24.3553      -2.0310     -25.9223      -2.9145      51.2537      70.2707  
    # bS           bT           bU           bV           bW           bX           bY           bZ  
    # -4.7728      -3.7300      -2.0333      -0.3906      -0.5717      -4.0728       0.8155      -4.4021  
    # 
    # Degrees of Freedom: 99 Total (i.e. Null);  48 Residual
    # Null Deviance:        138.5 
    # Residual Deviance: 57.73  AIC: 161.7
    # 
    # Warning message:
    #   glm.fit: fitted probabilities numerically 0 or 1 occurred 
    # 
    
    

    【讨论】:

    • 这也是我要提出的建议。您不想这样做的唯一原因是,如果名为 x 的变量可能已经存在于公式环境或数据框中,并且您希望它优先于作为函数参数的变量。但我不认为 OP 想要那样。
    • 同意,但很难想象与 OP 的帖子一致的情况,在 data 中有一个变量 x,您希望在模型中具有优先权。事实上,我认为在这种特殊情况下,在data 中覆盖现有的x 将是一个功能而不是一个缺陷。不过,总的来说,我认为您对此的谨慎是非常合理的。
    • 简单有效!谢谢!它也适用于 fun(DF1$x, DF1$y),这是自 data = NULL 以来我没想到的。
    • @user2554330 @DaveArmstrong 该解决方案完美运行。但是,你能解释一下为什么我首先得到错误吗?在glm 文档中,数据是“一个可选的数据框、列表或环境(或由as.data.frame 强制转换为数据框的对象),其中包含模型中的变量。如果在data 中找不到,则变量取自environment(formula),通常是调用glm的环境。”(强调我的)因此,为什么xy不是取自函数环境和a和@ 987654340@ 来自data?
    • @user2554330 @DaveArmstrong 为了避免在data 中已经存在名为xy 的变量时出现潜在问题,我添加了stopifnot(! names(x = data) %in% c("x", "y"))
    猜你喜欢
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-08
    • 2019-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多