【问题标题】:Behavior of R formulas with respect to the bound environmentR 公式相对于绑定环境的行为
【发布时间】:2015-01-25 22:56:18
【问题描述】:

我想知道 R 如何将环境绑定到公式的一些细节,以及在不同情况下对所述环境的要求,特别是公式中不包含 ~ 是否打开。

假设我有两个像字符串一样的公式,它们通常是通过编程方式创建的,但为简单起见,我只定义为文字:

$ formula_str_no_tilde <- "a + b"
$ formula_str_with_tilde <- "~ a + b"

我打算使用这些从包含 a 和 b 列的数据框中进一步创建模型矩阵。为此,我想将类似公式的字符串转换为合法公式。这是非常奇怪的不同行为的地方:

$ formula_no_tilde <- as.formula(formula_str_no_tilde)
> Error in eval(expr, envir, enclos) : object 'a' not found

同时:

$ formula_with_tilde <- as.formula(formula_str_with_tilde)
no error

查看 as.formula 的文档,有一个 env 参数用于将环境绑定到公式。这个参数默认绑定全局环境,这对错误消息有一定的意义,因为我们在全局环境中没有'a'。不过,它确实使接受第二次调用 as.formula 变得神秘,因为我们处于同样的情况......

由于全球环境是绑定的,人们会理智地怀疑这整个冒险可能被误导了,我们将无法在以后使用我们的 formula_with_tilde 来制作模型矩阵,因为全球环境已经边界。尽管如此:

$ D <- data.frame('a'=c(1, 2, 3), 'b'=c(-1, 0, 1))
$ model.matrix(formula_with_tilde, D)

如您天真希望的那样工作。可能这是model.matrix的一个特性,它为你重新绑定了环境……

所以这是我的问题:

  • 在 R 中没有波浪线的公式是否非法?有没有什么情况是有意义的?
  • 为什么不包含波浪号会引发环境查找错误,而包含它却不会?
  • model.matrix 是否重新绑定与公式关联的环境?

为了完整性:

  • 有没有更好的专业人士开始以编程方式生成和操作公式?

【问题讨论】:

  • 您不妨阅读help(formula)的详细信息部分。信息量很大
  • 波浪号是返回调用对象的前缀或中缀函数。

标签: r


【解决方案1】:

如果你想要一个 unevalated 调用对象(这是波浪号运算符返回的那种 R 语言对象),那么你可以使用call

 call( '+',quote(a),quote(b))
 #a + b

您需要通过用quote 包围来防止解释器的默认操作来评估第二个和第三个参数。您可以将结果用作 `eval 的合法第一个参数并使用数据框作为环境:

dat <- data.frame(a=1:10,b=1:10)
eval( call( '+',quote(a),quote(b)), dat)
# [1]  2  4  6  8 10 12 14 16 18 20

“是公式的波浪号部分”问题的答案:如果我们相信这一点,可能是,是的:

 (~a)[[1]]
#`~`

要从字符串“a+b”构建函数结果,这是一种方法:

 as.call( parse(text="a+b") )
#(a + b)()

“model.matrix 是否重新绑定与公式关联的环境?”

嗯,不完全是,至少我理解这个问题。 model.matrix 从 model.frame 中提取值,这是一个增强的数据框,但它的属性列表中没有附加环境。

看看这些语言对象有什么属性是很有趣的:

> class(bquote(a+b))
[1] "call"
> identical( bquote(a+b), ~a+b)
[1] FALSE

> attributes( ~a+b)
$class
[1] "formula"

$.Environment
<environment: R_GlobalEnv>

> attributes( bquote(a+b))
NULL

在 Globalenv 和 dat 中使用 a=1 和 b=2,如上:

> eval(  (~a+b)[[2]], envir=dat)
 [1]  2  4  6  8 10 12 14 16 18 20
> 
> eval( (~a+b)[[2]] )
[1] 3

附录:Konrad 的评论指出 eval 的参数可以是公式调用或表达式中的任何一个,因此您不需要 as.call,提示我进一步说明 evalevalq可用于产生反映上述差异的结果,仅使用表达式 a+b:

> evalq( a+b, envir=dat)
 [1]  2  4  6  8 10 12 14 16 18 20
> eval( a+b, envir=dat)   # evaluated in current scope despite  envir=dat
[1] 3

【讨论】:

  • quote(a + b) 也会返回相同的调用
  • 我希望人们会知道波浪号是一个返回调用对象的函数,所以我尽可能地保持符号的“功能性”。我从call('+', a,b) 开始,得到~("+", 1, 2),其中'a' 和'b' 在全局环境中分别为1 和2。
  • ~ 返回的内容(formula 对象)和 call('+' …) 返回的内容(language 对象)之间实际上存在差异。特别是,formula 附加了一个环境。
  • as.call 在这里也是完全多余的:is.call((~a+b)[[2]])TRUE
  • @BondedDust:这正是我希望得到的详细答案,谢谢!我将花一些时间来吸收它,并且我可能会有一些后续问题。只是想尽快让您知道,我很欣赏您提供的详细程度。
猜你喜欢
  • 2013-01-24
  • 2012-10-04
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多