R线性回归公式中的大写字母"I"代表什么意思?

43

我一直无法找到这个问题的答案,主要是因为使用单独字母(例如“I”)进行任何谷歌搜索都会导致问题。

在这样的模型中,“I”有什么作用?

data(rock)
lm(area~I(peri - mean(peri)), data = rock)

鉴于以下内容无法正常工作:

lm(area ~ (peri - mean(peri)), data = rock)

而且这个确实有效:

rock$peri - mean(rock$peri)

任何关于如何自己进行研究的关键词都将非常有帮助。


5
R语言有很好的文档。请阅读help("I") - Roland
2
是的,谢谢,我看到了。但这并没有完全回答为什么在线性模型内部需要特殊处理而在外部不需要的问题。如果答案是“这就是R的工作方式”,那我想也算是一个答案。 - Nancy
2
在函数公式中,它被用于抑制诸如“+”、“-”、“*”和“^”等运算符的解释,使它们作为算术运算符使用。这非常明确。甚至还有一个有用的链接指向“formula”的文档。 - Roland
5
回到最初的问题:请参考《R语言简介》中的第11.1节(随R软件一同安装,在帮助菜单下)给出了一些提示。它基本上提供了一个记忆方法,即I()=绝缘。这可能会有所帮助。而且我同意I()的文档确实有点简略。 - Stephan Kolassa
2
@Nancy,这与元素类别无关,而是与公式中“-”具有特殊含义有关。括号之所以存在是因为I是一个函数,所以你需要它们,就像你在mean()上需要它们一样。此外(但这个效果是次要的),它还可以视觉上指示哪些内容受到公式解析代码的保护。 - Gavin Simpson
显示剩余8条评论
3个回答

71

I 隔离保护I( ... )内部的内容,防止R公式解析代码扫视。它允许标准的R运算符像在公式外部使用时一样工作,而不被视为特殊的公式运算符。

例如:

y ~ x + x^2

would, to R, mean "give me:

  1. x = x的主效应,以及
  2. x^2 = x的主效应和二阶交互作用",

而不是意图的x加上x平方:

> model.frame( y ~ x + x^2, data = data.frame(x = rnorm(5), y = rnorm(5)))
           y           x
1 -1.4355144 -1.85374045
2  0.3620872 -0.07794607
3 -1.7590868  0.96856634
4 -0.3245440  0.18492596
5 -0.6515630 -1.37994358
这是因为在公式中,符号^是一个特殊的运算符,具体描述可参考?formula。由于主效应已经包含在公式中的x项中,所以仅在模型框架中包括x,而没有与x^2项进行交叉以得到二阶交互作用。
要获得通常的运算符,您需要使用I()将调用从公式代码中隔离出来:
> model.frame( y ~ x + I(x^2), data = data.frame(x = rnorm(5), y = rnorm(5)))
            y          x       I(x^2)
1 -0.02881534  1.0865514 1.180593....
2  0.23252515 -0.7625449 0.581474....
3 -0.30120868 -0.8286625 0.686681....
4 -0.67761458  0.8344739 0.696346....
5  0.65522764 -0.9676520 0.936350....

(最后一列是正确的,只是看起来很奇怪,因为它属于 AsIs 类。)

在您的例子中,- 在公式中使用时会表示从模型中移除一个术语,而您想要-具有其通常的二元运算符意义,即减法

> model.frame( y ~ x - mean(x), data = data.frame(x = rnorm(5), y = rnorm(5)))
Error in model.frame.default(y ~ x - mean(x), data = data.frame(x = rnorm(5),  : 
  variable lengths differ (found for 'mean(x)')

出现错误是因为 mean(x) 是长度为1的向量,而且 model.frame() 正确地告诉你它的长度与其他变量不匹配。解决方法是使用 I()

> model.frame( y ~ I(x - mean(x)), data = data.frame(x = rnorm(5), y = rnorm(5)))
           y I(x - mean(x))
1  1.1727063   1.142200....
2 -1.4798270   -0.66914....
3 -0.4303878   -0.28716....
4 -1.0516386   0.542774....
5  1.5225863   -0.72865....
因此,如果您想使用在公式中具有特殊意义的运算符,但需要其“非公式”含义,则需要用 I( ) 将运算元素包裹起来。
阅读 ?formula 以了解更多关于特殊运算符的信息,以及 ?I 以获得有关该函数本身及其在数据框中的另一个主要用途的详细信息(如果您感兴趣,这也是 AsIs 的来源)。

1
非常好的答案,我尝试使用X:X而不是X^2,但它仍然没有起作用,你知道为什么吗? - Jason Goal
你原本期望 I(X:X) 做什么?我猜它会尝试应用序列操作符,就像 seq(from = X, to = X, by = 1L) 一样。但这对我来说毫无意义。 - Gavin Simpson
那么,在公式中的 X: Y 是指 XY 之间的交互项吗? - Jason Goal
是的,X:Y(不用I()包裹)表示XY之间的交互作用。这就是重点;:^和其他一些运算符在公式中有不同的用途/解释。如果您想要通常的非公式解释,您需要将其包装在I()中。我认为X:X不会起作用,因为它并不是字面意义上的X * X,因为这对于因子变量无效。:表示交互作用。 - Gavin Simpson

2

从文档中可以看到:

函数I有两种主要用途。

  • 在函数data.frame中,通过在调用data.frame时将对象包含在I()中来保护对象,可以防止将字符向量转换为因子并删除名称,并确保将矩阵插入为单个列。I还可用于保护要添加到数据帧或通过as.data.frame转换为数据帧的对象。

针对这一点:

df1 <- data.frame(stringi = I("dog"))
df2 <- data.frame(stringi = "dog")

str(df1)
str(df2)
  • 在函数公式中,它被用来抑制像“+”、“-”、“*”和“^”这样的运算符被解释为公式运算符,因此它们被用作算术运算符。这被terms.formula解释为一个符号。

针对这一点:

lm(mpg ~ disp + drat, mtcars)
lm(mpg ~ I(disp + drat), mtcars)

第二行。“创建一个新的预测器”,它是disp + drat的字面意义之和。


0
谢谢大家。不过我还是有点困惑。关于formula的文档中写道:

"^ 运算符表示交叉到指定的程度。例如 (a+b+c)^2 等同于 (a+b+c)*(a+b+c),进一步展开为一个包含 a、b 和 c 的主效应以及它们的二阶交互作用的公式。"

所以我认为这段代码应该是:
lm(Y ~ X^2)

会给出三个系数:(1)截距,(2)一阶项的系数X,以及(3)二次项的系数X^2。但它并没有。
X1 <- runif(100)
X1_2 <- X1^2

set.seed(61)
Y1 <- 5*X1 + -4.5*X1^2 + rnorm(100,0,.05)

以上代码输出如下:
> lm(Y1 ~ X1^2)
> 
> Call: lm(formula = Y1 ~ X1^2)
> 
> Coefficients: (Intercept)           X1  
>      1.0486       0.0343

这与没有二次项是一样的。
> > lm(Y1 ~ X1)
> 
> Call: lm(formula = Y1 ~ X1)
> 
> Coefficients: (Intercept)           X1  
>      1.0486       0.0343

当我使用函数I时,我得到了这个:
> > lm(Y1 ~ I(X1^2))
> 
> Call: lm(formula = Y1 ~ I(X1^2))
> 
> Coefficients: (Intercept)      I(X1^2)  
>      1.0602      -0.1145  ,

只捕捉数据生成过程的下降曲线。
看起来要同时获得主要项和交互项,将两个项明确地放入模型中:
> > lm(Y1 ~ X1 + I(X1^2))
> 
> Call: lm(formula = Y1 ~ X1 + I(X1^2))
> 
> Coefficients: (Intercept)           X1      I(X1^2)     -0.009738    
> 5.021233    -4.518124

这似乎是不必要的繁琐,也不像文档所承诺的那样,虽然我肯定是误解了它。(也许poly函数对于所有这些来说是最好的选择。)
如果我有点迟钝,对不起,现在已经很晚了,我陷入了一个愚蠢的兔子洞...

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