【问题标题】:Any pitfalls to using programmatically constructed formulas?使用以编程方式构造的公式有什么陷阱吗?
【发布时间】:2013-06-30 23:18:30
【问题描述】:

我想遍历一个很长的潜在解释变量向量, 依次回归每个响应变量。而不是粘贴在一起 模型公式,我正在考虑使用reformulate()as demonstrated here.

下面的函数fun() 似乎可以完成这项工作,适合所需的模型。但请注意, 它在其调用元素中记录构造公式对象的名称 而不是它的价值

## (1) Function using programmatically constructed formula
fun <- function(XX) {
    ff <- reformulate(response="mpg", termlabels=XX)
    lm(ff, data=mtcars)
}
fun(XX=c("cyl", "disp"))
# 
# Call:
# lm(formula = ff, data = mtcars)                 <<<--- Note recorded call
# 
# Coefficients:
# (Intercept)          cyl         disp  
#    34.66099     -1.58728     -0.02058  

## (2) Result of directly specified formula (just for purposes of comparison)
lm(mpg ~ cyl + disp, data=mtcars)
# 
# Call:
# lm(formula = mpg ~ cyl + disp, data = mtcars)   <<<--- Note recorded call
# 
# Coefficients:
# (Intercept)          cyl         disp  
#    34.66099     -1.58728     -0.02058  

我的问题:这有什么危险吗?这能变成一个 例如,如果我想稍后申请 updatepredict 或 模型拟合对象的其他功能,(可能来自其他环境)?

一个稍微尴尬的替代方案,但确实可以记录 呼叫权是使用eval(substitute())。这在某种程度上是一个更安全的结构吗?

fun2 <- function(XX) {
    ff <- reformulate(response="mpg", termlabels=XX)
    eval(substitute(lm(FF, data=mtcars), list(FF=ff)))
}
fun2(XX=c("cyl", "disp"))$call
## lm(formula = mpg ~ cyl + disp, data = mtcars)

【问题讨论】:

  • 我认为使用update 会小心处理此问题,请参阅[stackoverflow.com/questions/13690184/…。您通常可以通过尝试使用data.table 来找出这些陷阱——请参阅 [stackoverflow.com/questions/15096811/…
  • 这工作:do.call("lm", list(ff, quote(mtcars)))
  • @G.Grothendieck -- 谢谢。这看起来不错,而且看起来它不应该在路上造成任何问题。 (另外,感谢 mnel 提供的有趣链接。)
  • 还值得看看add1.lm 是如何工作的(使用. ~ .+update.formula 和ti 来记住(或注意)reformulate 只是粘贴方法的包装器。

标签: r scoping


【解决方案1】:

我总是犹豫是否没有情况下涉及 R 环境和作用域的东西可能会咬人,但是......经过更多探索,我上面的第一个用法确实看起来安全

事实证明,打印出来的电话有点红鲱鱼。

实际上被其他函数使用的公式(以及由formula()as.formula()提取的公式)是存储在fit对象的terms元素中的公式,并且it 得到正确的实际公式。 (terms 元素包含一个类"terms" 的对象,它只是一个带有一堆附加属性的"formula"。)

要查看我的问题中的所有建议和关联的 cmets 存储相同的 "formula" 对象(直到关联的环境),请运行以下命令。

## First the three approaches in my post
formula(fun(XX=c("cyl", "disp")))
# mpg ~ cyl + disp
# <environment: 0x026d2b7c>

formula(lm(mpg ~ cyl + disp, data=mtcars))
# mpg ~ cyl + disp

formula(fun2(XX=c("cyl", "disp"))$call)
# mpg ~ cyl + disp
# <environment: 0x02c4ce2c>

## Then Gabor Grothendieck's idea
XX = c("cyl", "disp")
ff <- reformulate(response="mpg", termlabels=XX)
formula(do.call("lm", list(ff, quote(mtcars))))  
## mpg ~ cyl + disp

要确认 formula() 确实是从 fit 对象的 terms 元素派生其输出,请查看 stats:::formula.lmstats:::formula.terms

【讨论】:

  • 不错的探索。由于其中一些相同的问题,我以前使用过do.call 方法。很高兴知道这些函数不依赖于存储的公式/调用。但是,我确实相信 update 确实如此,所以请注意这种用法。
  • @Aaron -- 谢谢。您指的是与我的问题之后的第一条评论中的 mnel 相同的问题吗?如果是这样,我是否正确理解该陷阱同样适用于上述 任何 调用? (即使用do.call() 发起对lm() 的调用并不能帮助您避免update() 的范围问题,对吧?)
  • 经过进一步探索,看起来update 也很安全。我确定不是,但也许我一直都错了。但是,有些函数确实依赖于正确的函数,例如,anova.lme;见stackoverflow.com/q/7666807/210673
  • @Aaron -- 不错的收获。通过将我指向那个问题,我认为您帮助我找到并修复了导致 OP 调用 anova() 失败的 nlme 代码位。 (See my comment here 了解更多详情。)
猜你喜欢
  • 2011-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多