在 R 中 stats::lm 中使用变量参数(点-点-点)

Use of variable arguments (dot-dot-dot) in stats::lm in R

假设我们有一个调用 stats::lm 并将公式和数据框作为参数的函数。可以使用可变参数提供我们想要传递给 stats::lm 的更多参数:

outer_function <- function(formula, data, ...) {
  z <- stats::lm(formula = formula, data = data, ...)
  return(z)
}

现在假设我们要使用此函数并提供一个附加参数 (weights),该参数将传递给 stats::lm

data <- data.frame(replicate(5, rnorm(100)))
weights <- replicate(100, 1)
formula <- X1 ~ X2 + X3

outer_function(formula = formula, data = data, weights = weights)

这会在 stats::lm 中产生以下错误:

Error in eval(extras, data, env) : 
  ..1 used in an incorrect context, no ... to look in

调试对 stats::lm 的调用我看到参数 weights 已正确传递给 stats::lm,但 match.call() 稍后用于函数中的计算,是

stats::lm(formula = formula, data = data, weights = ..1)

这样 weights 就被赋予了 ... 列表的第一个元素,它是空的。

任何人都可以详细说明为什么这种方法会失败吗?特别是,如果 weights 是标量(例如 5),则不会出现问题,而 match.call() 将是

stats::lm(formula = formula, data = data, weights = 5)

目前,我正在为我的函数使用以下解决方案:

outer_function <- function(formula, data, ...) {
  args <- list(formula = formula, data = data, ...)
  z <- do.call(stats::lm, args)
  return(z) 
}

哪个有效,但我仍然想知道是否没有办法绕过 do.call,以防 ... 中的参数是向量或列表。

我想不出像 do.call 这样安全和简洁的解决方法。我 可以 解释发生了什么,调试了 lm 调用。

lm 的正文中,您会找到语句

mf <- eval(mf, parent.frame())

赋值右边,mf是调用

stats::model.frame(formula = formula, data = data, weights = ..1, 
    drop.unused.levels = TRUE)

parent.frame()outer_function调用的框架(也就是outer_function的求值环境)。 eval 正在评估 parent.frame() 中的 mf。由于 S3 调度,最终在 parent.frame() 中评估的是调用

stats::model.frame.default(formula = formula, data = data, weights = ..1, 
    drop.unused.levels = TRUE)

model.frame.default 的正文中,您会找到语句

extras <- eval(extras, data, env)

这个赋值的右边,extras是调用

list(weights = ..1)

指定来自 mf 的参数与 model.frame.default 的正式参数 ... 匹配(在这种情况下只是 weights,因为 model.frame.default 个形式参数,名称为 formuladatadrop.unused.levels); data 是包含您的模拟数据的数据框; env 是您的 全局环境 。 (env 之前在 model.frame.default 的正文中定义为 environment(formula),这确实是您的全局环境,因为那是您定义 formula 的地方。)

eval 正在评估 data 中的 extras 并将 env 作为附件。 此处 会引发错误,因为数据框 data 和您的全局环境 env 不是 ..n 的有效上下文。符号 ..1 仅在以 ... 作为正式参数的函数的框架中有效。

您可能已经从 ?lm 中推断出了问题,其中指出:

All of weights, subset and offset are evaluated in the same way as variables in formula, that is first in data and then in the environment of formula.

weights 被赋予常量值时没有问题(即 而不是 环境中绑定的变量名称和 not 函数调用)在 outer_function 调用中,因为在那种情况下 match.call 不会替换符号 ..n。因此

outer_function(formula = formula, data = data, weights = 5)

有效(好吧,抛出一个 不同的 错误),但是

weights <- 5
outer_function(formula = formula, data = data, weights = weights)

outer_function(formula = formula, data = data, weights = rep(1, 100))

不要。