在R中,stats::lm中使用可变参数(点-点-点)的方法。

3
假设我们有一个函数,它调用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的方法。


1
这回答解决了你的问题吗?省略号问题:将...传递给lm。看起来你的解决方案比那里提出的更整洁,所以不确定你是否会找到更好的解决方案。 - caldwellst
感谢@caldwellst的贡献。您分享的帖子基本上涉及到了同样的问题,并提出了与我的解决方案类似的解决方法。因此,我能够通过do.call解决问题,但我想了解(1)我的初始解决方案出了什么问题,以及(2)是否可能不使用do.call来解决问题。 - user2986671
你不想使用 do.call 有什么特别的原因吗? - Mikael Jagan
1
我认为这不是一个重复的问题。原帖明确询问了为什么会出现错误的技术细节。在我看来,这与另一个问题(“我该如何修复它?”)非常不同,后者并没有引发太多关于潜在问题的讨论。 - Mikael Jagan
1个回答

1

我想不出来一个像 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 的评估环境)。 evalparent.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则是您的全局环境。(envmodel.frame.default的主体中早先被定义为environment(formula),这确实是您的全局环境,因为那是您定义formula的地方。)

eval在将extrasdata一起以env作为封闭空间进行评估时引发了错误。此处,因为数据框data和全局环境env不是..n的有效上下文。符号..1仅在具有...作为形式参数的函数的框架中有效。

您可能已经从?lm中推断出问题所在,它指出:

所有的weightssubsetoffset都以与formula中的变量相同的方式进行评估,即首先在data中,然后在formula的环境中。

当在outer_function调用中给weights赋一个常量值(即不是绑定在环境中的变量名或函数调用的名称)时,就没有问题,因为在这种情况下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))

不要。

非常感谢@mikael-jagan的这些澄清,我想我会选择do.call。 - user2986671

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