R中确定系数的计算问题

时间:2013-06-03 20:42:29

标签: r matrix lm

我正在玩R lm()函数,我在计算coefficient of determination R^2时发现了一些奇怪的东西。所以假设我有玩具回归问题:

set.seed(0); N= 12;
x_ <- 1:N; y_ <- 2* x_+ rnorm(N, sd=2); 
lm1_ <- lm( y_ ~ x_ ); S1 <- summary(lm1_); 
lm2_ <- lm( y_ ~ -1 + model.matrix(lm1_) ); S2 <- summary(lm2_); 

所以在这一点上我们实际上设置lm1_和lm2_是一回事。使用相同的因变量,使用相同的模型矩阵,我们应该得到相同的拟合。我们来看看那些:

identical( fitted(lm2_),fitted(lm1_))       
[1] TRUE                                    #SURE!
identical( residuals(lm2_), residuals(lm1_))
[1] TRUE                                    #YEAP!
identical( lm1_$df.residual, lm2_$df.residual)
[1] TRUE                                    #SORTED!
identical( as.numeric(model.matrix(lm2_ )), as.numeric(model.matrix(lm1_ ))) 
[1] TRUE                                    #WHY NOT?
identical( S2$r.squared, S1$r.squared)        
[1] FALSE                                   #HUH?

发生什么事了?让我们使用维基百科文章中的定义来确定决定系数(或如果你愿意,解释的方差百分比):

1 - ((sum( residuals(lm1_)^2))/( sum( (y_ -mean(y_))^2)))
[1] 0.8575376
1 - ((sum( residuals(lm2_)^2))/( sum( (y_ -mean(y_))^2)))
[1] 0.8575376
identical(1 - ((sum( residuals(lm1_)^2))/(sum( (y_ -mean(y_))^2))), S1$r.squared)
[1] TRUE
identical(1 - ((sum( residuals(lm2_)^2))/(sum( (y_ -mean(y_))^2))), S1$r.squared)
[1] TRUE
identical(1 - ((sum( residuals(lm2_)^2))/(sum( (y_ -mean(y_))^2))), S2$r.squared)
[1] FALSE
S2$r.squared
[1] 0.9700402 # ???

现在显然整个技巧都被函数summary.lm()困惑了。 R ^ 2计算为mss/ (rss+ mss),其中rss是残差平方和sum(residuals(lmX_)^2)mss是平均平方和(拟合),取决于是否存在拦截(cp来自summary.lm()):

mss <- if (attr(z$terms, "intercept")) 
            sum((f - mean(f))^2)
        else sum(f^2)

或在我们的案例中:

sum(scale(fitted(lm2_),scale=F)^2) / 
(sum(scale(fitted(lm2_),scale=F)^2)+sum(residuals(lm2_)^2))
[1] 0.8575376
sum(fitted(lm2_)^2) / (sum(fitted(lm2_)^2) + sum(residuals(lm2_)^2))
[1] 0.9700402

很明显,R没有将第二个模型矩阵识别为具有截距。如果在我的情况下有人正在移动模型矩阵,那么这是一个重大问题。我知道明显的解决方法是采用没有截距的模型矩阵,但这并没有减轻这种设计选择有点容易破坏的事实。更一般的定义不会只通过微小的调整来拯救我们这个麻烦吗?是否有一些其他明显的修复我可以使用,我不会遇到这些问题?

如果一个人没有拦截并且正在使用因子变量和交互,那么这个问题会变得非常严重。实际上,R可能会尝试将其中一个级别视为拦截。那么整个局势都是悲惨的:

library(MASS)
lm_oats <- lm( Y ~ -1+  V*N , oats)
S_oats <- summary(lm_oats)
S_oats$r.squared
[1] 0.9640413 # :*D
1-  ( sum( residuals(lm_oats)^2)  / sum( ( oats$Y- mean(oats$Y))^2 )  )
[1] 0.4256653 # :*(

#For the record:
sum((fitted(lm_oats))^2 )/(sum( residuals(lm_oats)^2) +sum((fitted(lm_oats))^2)) 
[1] 0.9640413
sum((scale(scale=F,fitted(lm_oats)))^2 ) /( sum( residuals(lm_oats)^2) 
+sum((scale(scale=F,fitted(lm_oats)))^2 )) 
[1] 0.4256653

这是R的设计特征,以某种方式可以规避吗? (我很感谢我的问题非常开放,感谢所有那些一直在这里的人!)

我使用的是R版3.0.1,Matrix_1.0-12,lattice_0.20-15,MASS_7.3-26。 (Linux 32位)

1 个答案:

答案 0 :(得分:4)

关于R ^ 2的非敏感/不同含义的R邮件列表已经长期讨论了强制通过拦截的模型。 R试图在没有截距的情况下检测何时拟合模型(“语义上”,即通过在模型公式中查找-1+0的存在)并相应地修改R ^ 2计算。如果你做了一些棘手的事情,你就会绕过它的尝试。

更具体地说:

  • summary.lm()测试拟合模型的terms元素是否具有等于1的intercept属性
  • 拟合模型的terms元素是通过调用model.frame()
  • 构建的
  • model.frame()来电terms.formula
  • terms.formula调用一些非常多毛的内部C代码C_termsform,但您可以通过尝试terms(y~x-1) vs terms(y~x+0) vs terms(y~x)看到R只是检查语义上(如上所述......)

底线:如果你想用模拟矩阵做一些适合R的常规约定的东西,你可能应该继续根据你自己喜欢的公式计算R ^ 2;您可以从summary.lm()复制相关代码。如果你这么深,你可能想要使用lm.fit()代替计算效率。