如何在R中使用公式来排除主效应但保留相互作用

wol*_*oor 6 regression r linear-regression lm categorical-data

我不想要主效应,因为它与更精细的因子固定效果共线,所以有这些是烦人的NA.

在这个例子中:

lm(y ~ x * z)
Run Code Online (Sandbox Code Playgroud)

我想要x(数字)和z(因素)的相互作用,但不是主要的效果z.

李哲源*_*李哲源 9

介绍

R文件?formula说:

'*'运算符表示因子交叉:'a*b'被解释为'a + b + a:b

因此,通过执行以下操作之一,听起来主要影响是直截了当的:

a + a:b  ## main effect on `b` is dropped
b + a:b  ## main effect on `a` is dropped
a:b      ## main effects on both `a` and `b` are dropped
Run Code Online (Sandbox Code Playgroud)

真的吗?不不不(太简单,太天真).实际上它取决于变量类ab.

  • 如果它们都不是因素,或者只有一个是因素,那么这是正确的;
  • 如果它们都是因素,没有.当存在交互(高阶效应)时,您永远不会丢弃主效果(低阶效果).

这种行为是由于一个叫做魔术的函数model.matrix.default,它从公式构造一个设计矩阵.数值变量仅包含在列中,但因子变量自动编码为多个虚拟列.正是这种虚拟重新编码是一种魔力.通常认为我们可以启用或禁用对比来控制它,但不是真的.即使在这个最简单的例子中,我们也失去了对比度的控制.问题是model.matrix.default在进行虚拟编码时有自己的规则,并且它对指定模型公式的方式非常敏感.正是由于这个原因,当存在两个因素之间的相互作用时,我们不能放弃主效应.


数字和因子之间的相互作用

从您的问题来看,x是数字,z是一个因素.您可以指定具有交互的模型,但不能指定zby的主效应

y ~ x + x:z
Run Code Online (Sandbox Code Playgroud)

既然x是数字,那就相当于do

y ~ x:z
Run Code Online (Sandbox Code Playgroud)

这里唯一的区别是参数化(或model.matrix.default虚拟编码如何).考虑一个小例子:

set.seed(0)
y <- rnorm(10)
x <- rnorm(10)
z <- gl(2, 5, labels = letters[1:2])

fit1 <- lm(y ~ x + x:z)
#Coefficients:
#(Intercept)            x         x:zb  
#     0.1989      -0.1627      -0.5456  

fit2 <- lm(y ~ x:z)
#Coefficients:
#(Intercept)         x:za         x:zb  
#     0.1989      -0.1627      -0.7082 
Run Code Online (Sandbox Code Playgroud)

从系数的名称我们看到,在第一个规范中,z对比所以它的第一级"a"不是虚拟编码,而在第二个规范中,z没有对比,并且两个级别"a"和"b"都是虚拟编码.鉴于两个规范都以三个系数结束,它们实际上是等价的(从数学上讲,两种情况下的设计矩阵具有相同的列空间),您可以通过比较它们的拟合值来验证这一点:

all.equal(fit1$fitted, fit2$fitted)
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)

那么为什么z在第一种情况下进行对比呢?因为否则我们有两个虚拟列x:z,并且这两列的总和只是x,与x公式中的现有模型术语别名.事实上,在这种情况下即使你要求你不想要对比,model.matrix.default也不会服从:

model.matrix.default(y ~ x + x:z,
      contrast.arg = list(z = contr.treatment(nlevels(z), contrasts = FALSE)))
#   (Intercept)          x       x:zb
#1            1  0.7635935  0.0000000
#2            1 -0.7990092  0.0000000
#3            1 -1.1476570  0.0000000
#4            1 -0.2894616  0.0000000
#5            1 -0.2992151  0.0000000
#6            1 -0.4115108 -0.4115108
#7            1  0.2522234  0.2522234
#8            1 -0.8919211 -0.8919211
#9            1  0.4356833  0.4356833
#10           1 -1.2375384 -1.2375384
Run Code Online (Sandbox Code Playgroud)

那么为什么在第二种情况下z没有对比呢?因为如果是,我们在构建交互时会失去级别"a"的效果.即使你需要对比,model.matrix.default也会忽略你:

model.matrix.default(y ~ x:z,
      contrast.arg = list(z = contr.treatment(nlevels(z), contrasts = TRUE)))
#   (Intercept)       x:za       x:zb
#1            1  0.7635935  0.0000000
#2            1 -0.7990092  0.0000000
#3            1 -1.1476570  0.0000000
#4            1 -0.2894616  0.0000000
#5            1 -0.2992151  0.0000000
#6            1  0.0000000 -0.4115108
#7            1  0.0000000  0.2522234
#8            1  0.0000000 -0.8919211
#9            1  0.0000000  0.4356833
#10           1  0.0000000 -1.2375384
Run Code Online (Sandbox Code Playgroud)

哦,很棒model.matrix.default.它能够做出正确的决定!


两个因素之间的相互作

让我重申一下:当存在交互时,没有办法放弃主效应.

我不会在这里提供额外的例子,因为我有一个在为什么我得到NA系数以及如何lm降低参考水平以进行交互.请参阅那里的"交互对比"部分.简而言之,以下所有规格都给出了相同的模型(它们具有相同的拟合值):

~ year:treatment
~ year:treatment + 0
~ year + year:treatment
~ treatment + year:treatment
~ year + treatment + year:treatment
~ year * treatment
Run Code Online (Sandbox Code Playgroud)

特别是,第1规格导致NA系数.

因此,一旦RHS ~包含一个year:treatment,你永远不会要求model.matrix.default放弃主要效果.

在制作ANOVA表时,对这种行为缺乏经验的人会感到惊讶.


通过传递 model.matrix.default

有些人认为model.matrix.default很烦人,因为它似乎在伪编码中没有一致的方式.他们认为"一致的方式"是始终放弃第一个因素水平.好吧,没问题,您可以model.matrix.default通过手动执行虚拟编码来绕过,并将生成的虚拟矩阵作为变量提供给lm,等等.

但是,你仍然需要model.matrix.default的帮助可以轻松地为做虚拟编码一个(是的,只有一个)因子变量.例如,对于z前一个示例中的变量,其完整的虚拟编码如下,您可以保留其全部或部分列以进行回归.

Z <- model.matrix.default(~ z + 0)  ## no contrasts (as there is no intercept)
#   za zb
#1   1  0
#2   1  0
#3   1  0
#4   1  0
#5   1  0
#6   0  1
#7   0  1
#8   0  1
#9   0  1
#10  0  1
#attr(,"assign")
#[1] 1 1
#attr(,"contrasts")
#attr(,"contrasts")$z
#[1] "contr.treatment"
Run Code Online (Sandbox Code Playgroud)

回到我们简单的例子,如果我们不想为对比zy ~ x + x:z,我们可以做

Z2 <- Z[, 1:2]  ## use "[" to remove attributes of `Z`
lm(y ~ x + x:Z2)
#Coefficients:
#(Intercept)            x       x:Z2za       x:Z2zb  
#     0.1989      -0.7082       0.5456           NA
Run Code Online (Sandbox Code Playgroud)

毫不奇怪,我们看到了NA(因为colSums(Z2)有别名x).如果我们想强制执行对比y ~ x:z,我们可以执行以下任一操作:

Z1 <- Z[, 1]
lm(y ~ x:Z1)
#Coefficients:
#(Intercept)         x:Z1  
#    0.34728     -0.06571

Z1 <- Z[, 2]
lm(y ~ x:Z1)
#Coefficients:
#(Intercept)         x:Z1  
#     0.2318      -0.6860  
Run Code Online (Sandbox Code Playgroud)

后一种情况可能是contefranz试图做的事情.

但是,我并不真的推荐这种黑客攻击.当您将模型公式传递给lm等时,model.matrix.default正试图为您提供最明智的构造.而且,实际上我们想用拟合模型进行预测.如果你做了假编码自己,你会提供时也很难newdatapredict.