在公式中定义一个中缀运算符

12

我试图创建一个更简洁的版本这个解决方案,其中涉及指定公式的右侧,形式为d1 + d1:d2

鉴于在公式上下文中*是完整交互的简洁代替(即d1 * d2给出d1 + d2 + d1:d2),我的方法是尝试定义另一种运算符,例如使用中缀方法,如我在其他应用程序中所习惯的那样,a la: %+:%

"%+:%" <- function(d1,d2) d1 + d2 + d1:d2

然而,这种可预测的失败是因为我对评估没有仔细处理;让我们举一个例子来说明我的进展:

set.seed(1029)
v1 <- runif(1000)
v2 <- runif(1000)
y <- .8*(v1 < .3) + .2 * (v2 > .25 & v2 < .8) - 
  .4 * (v2 > .8) + .1 * (v1 > .3 & v2 > .8)
希望这个例子可以清楚地说明,为什么简单地写出这两个术语可能是不可取的。
y ~ cut(v2, breaks = c(0, .25, .8, 1)) +
  cut(v2, breaks = c(0, .25, .8, 1)):I(v1 < .3)

一个接近我所期望的输出的解决方案是将整个公式定义为函数:

plus.times <- function(outvar, d1, d2){
  as.formula(paste0(quote(outvar), "~", quote(d1),
                    "+", quote(d1), ":", quote(d2)))
}

当传递给lm时,这会给出预期的系数,但是名称更难以直接解释(特别是在真实数据中,我们会注意为d1d2提供描述性名称,与这个通用示例相反):

out1 <- lm(y ~ cut(v2, breaks = c(0, .25, .8, 1)) +
             cut(v2, breaks = c(0, .25, .8, 1)):I(v1 < .3))
out2 <- lm(plus.times(y, cut(v2, breaks = c(0, .25, .8, 1)), I(v1 < .3)))
any(out1$coefficients != out2$coefficients)
# [1] FALSE
names(out2$coefficients)
# [1] "(Intercept)"         "d1(0.25,0.8]"        "d1(0.8,1]"           "d1(0,0.25]:d2TRUE"  
# [5] "d1(0.25,0.8]:d2TRUE" "d1(0.8,1]:d2TRUE"

所以这不是最理想的。

有没有办法定义调整代码,使我提到的中缀运算符按预期工作?修改plus.times的形式,使变量不被重命名如何?

我一直在尝试探索(?formula, ?"~", ?":", getAnywhere(formula.default), 这个回答等),但还没有看到R在公式中遇到*时如何解释它,以便我可以进行所需的微调。


它们在c中的stats:::model.frame.default中被解释。https://github.com/wch/r-source/blob/ed66b715221d2720f5b334470335635bada520b1/src/library/stats/src/model.c#L888 - rawr
@rawr 谢谢。我不知道 C 代码在做什么 - 我看到他们“定义”了 formula 可以理解的每个符号,但是他们似乎只使用了 tildeSymbol。这是否意味着我不能得到自己的中缀表达式而不降至 C 并定义类似于此处所做的 plusColonSymbol - MichaelChirico
1
@HeatherTurner的回答对我来说似乎完全正确。如果你真的想开始尝试扩展公式,我建议首先(1)查看model.frame()结果的terms组件,然后(2)查看这里的代码... - Ben Bolker
1个回答

11

在这种情况下,您不需要定义新的运算符:在公式中,d1/d2会展开为d1 + d1:d2。换句话说,d1/d2指定d2嵌套在d1中。继续您的示例:

out3 <- lm(y ~ cut(v2,breaks=c(0,.25,.8,1))/I(v1 < .3))
all.equal(coef(out1), coef(out3))
# [1] TRUE

进一步评论

影响因素可能是交叉的或嵌套的。对于两个因素,如果能够观察到这两个因素每个水平的所有组合,则它们是交叉的,例如性别和治疗、温度和pH等。如果一个因素嵌套在另一个因素中,则该因素的每个水平只能在另一个因素的某个水平内被观察到,例如城镇和乡村、工作人员和店铺等。

这些关系反映在模型的参数化中。对于交叉因素,我们使用 d1*d2d1 + d2 + d1:d2,以给出每个因素的主效应和交互作用。对于嵌套因素,我们使用 d1/d2d1 + d1:d2,以给出形式为 1 + d2 的单独子模型,对于每个 d1 水平。

嵌套的概念不仅限于因素,例如我们可以使用 sex/x 对男性和女性分别进行线性回归拟合 x

在公式中,%in% 等同于 :,但它可以用于强调数据/模型的嵌套或分层结构。例如,a + b %in% aa + a:b 是相同的,但读作 "a加上在a内部的b" 可以更好地描述正在拟合的模型。即便如此,使用 / 也具有简化模型公式并强调结构的优势。


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