scoping/non-standard R 函数中 glm 公式的评估问题

scoping/non-standard evaluation issue in glm's formula in a function in R

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

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)以及来自另一个数据框的变量(例如,来自 DF2ab)。它因我的天真功能而失败:

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?我想这个问题与非标准评估 and/or 范围界定有关,但我不知道如何解决它。

示例数据:

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

为什么不把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 
# 

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

@Thomas 在评论中引用文档说

If not found in data, the variables are taken from environment(formula), typically the environment from which glm is called.

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

如果您 运行 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 没问题。