作为有关R中线性混合模型的问题的前传,并作为初学者/中级统计爱好者的参考,我决定以独立的“问答式”形式发布“手动”计算简单线性回归的系数和预测值。
该示例使用R内置数据集,mtcars
并将其设置为充当自变量的车辆所消耗的每加仑英里数,并根据汽车的重量(连续变量)进行回归,并将汽缸数作为没有相互作用的三个水平(4、6或8)的因子。
编辑:如果您对此问题感兴趣,您肯定会在CV之外的Matthew Drury的这篇帖子中找到详细而令人满意的答案。
作为有关R中线性混合模型的问题的前传,并作为初学者/中级统计爱好者的参考,我决定以独立的“问答式”形式发布“手动”计算简单线性回归的系数和预测值。
该示例使用R内置数据集,mtcars
并将其设置为充当自变量的车辆所消耗的每加仑英里数,并根据汽车的重量(连续变量)进行回归,并将汽缸数作为没有相互作用的三个水平(4、6或8)的因子。
编辑:如果您对此问题感兴趣,您肯定会在CV之外的Matthew Drury的这篇帖子中找到详细而令人满意的答案。
Answers:
注意:我已经在我的网站上发布了该答案的扩展版本。
您是否愿意考虑发布一个与实际R引擎相似的答案?
当然!沿着兔子洞我们去了。
第一层是lm
,暴露给R程序员的接口。您可以通过lm
在R控制台中键入内容来查看其来源。它的大部分(与大多数生产级别代码中的大多数一样)忙于检查输入,设置对象属性和引发错误;但是这条线伸出
lm.fit(x, y, offset = offset, singular.ok = singular.ok,
...)
lm.fit
是另一个R函数,您可以自己调用它。虽然lm
方便地使用公式和数据框,但需要lm.fit
矩阵,因此这是删除的抽象级别之一。检查源代码lm.fit
,进行更繁琐的工作,以及以下非常有趣的行
z <- .Call(C_Cdqrls, x, y, tol, FALSE)
现在我们到了某个地方。 .Call
是R调用C代码的方式。R源中的某个地方有一个C函数C_Cdqrls,我们需要找到它。 在这里。
再次看一下C函数,我们发现大部分是边界检查,错误清除和繁忙的工作。但是这条线是不同的
F77_CALL(dqrls)(REAL(qr), &n, &p, REAL(y), &ny, &rtol,
REAL(coefficients), REAL(residuals), REAL(effects),
&rank, INTEGER(pivot), REAL(qraux), work);
因此,现在我们使用第三种语言,R调用了C,后者正在调用fortran。 这是fortran代码。
第一条评论说明了一切
c dqrfit is a subroutine to compute least squares solutions
c to the system
c
c (1) x * b = y
(有趣的是,看起来该例程的名称在某个时候已更改,但是有人忘记了更新注释)。因此,我们终于可以进行一些线性代数运算,并实际求解方程组。这是fortran真正擅长的事情,这解释了为什么我们经过这么多层次才能到达这里。
注释还解释了代码将要做什么
c on return
c
c x contains the output array from dqrdc2.
c namely the qr decomposition of x stored in
c compact form.
因此,fortran将通过查找分解来解决系统问题。
发生的第一件事,也是最重要的是
call dqrdc2(x,n,n,p,tol,k,qraux,jpvt,work)
这将dqrdc2
在我们的输入矩阵上调用fortran函数x
。这是什么?
c dqrfit uses the linpack routines dqrdc and dqrsl.
所以我们终于把它放到linpack了。Linpack是一个Fortran线性代数库,自70年代以来一直存在。最后,最严重的线性代数找到了行进的方式。在我们的例子中,我们使用函数dqrdc2
c dqrdc2 uses householder transformations to compute the qr
c factorization of an n by p matrix x.
这是完成实际工作的地方。我需要花一整天的时间来弄清楚这段代码在做什么,因为代码级别很低。但总的来说,我们有一个矩阵,我们想将其分解为乘积,其中是一个正交矩阵,是一个上三角矩阵。这是一件很明智的事情,因为一旦有了和,就可以求解线性方程式进行回归
非常简单地。确实
所以整个系统变成
但是是上三角并且与具有相同的秩,因此只要我们的问题很好地被提出,它就是完整的秩,我们也可以只解决简化的系统
但这是很棒的事情。 是上三角,所以这里的最后一个线性方程是,因此求解很简单。然后,您可以逐行上移行,并替换为您已经知道的,每次得到一个简单的线性变量方程式即可求解。因此,一旦有了和,整个过程就会崩溃,即所谓的向后替换,这很容易。您可以在此处更详细地了解此内容,在这里可以找到一个明确的小例子。constant * beta_n = constant
Matthew Drury在同一线程中的答案很好地描述了R中的实际分步计算。在这个答案中,我想逐步证明自己,可以通过简单的示例证明R的结果,这些结果可以通过对列空间的投影的线性代数以及垂直(点积)误差的概念来实现,这在不同的文章中都有说明。 ,由Strang博士在《线性代数及其应用》中作了很好的解释,可在此处轻松访问。
为了估算回归系数,
attach(mtcars)
x1 <- wt
x2 <- cyl; x2[x2==4] <- 1; x2[!x2==1] <-0
x3 <- cyl; x3[x3==6] <- 1; x3[!x3==1] <-0
x4 <- cyl; x4[x4==8] <- 1; x4[!x4==1] <-0
X <- cbind(x1, x2, x3, x4)
colnames(X) <-c('wt','4cyl', '6cyl', '8cyl')
head(X)
wt 4cyl 6cyl 8cyl
[1,] 2.620 0 1 0
[2,] 2.875 0 1 0
[3,] 2.320 1 0 0
[4,] 3.215 0 1 0
[5,] 3.440 0 0 1
[6,] 3.460 0 1 0
lm
X_tr_X_inv <- solve(t(X) %*% X)
Proj_M <- X_tr_X_inv %*% t(X)
Proj_M %*% mpg
[,1]
wt -3.205613
4cyl 33.990794
6cyl 29.735212
8cyl 27.919934
与:相同coef(lm(mpg ~ wt + as.factor(cyl)-1))
。
HAT <- X %*% X_tr_X_inv %*% t(X)
y_hat <- HAT %*% mpg
cyl <- as.factor(cyl); OLS <- lm(mpg ~ wt + cyl); predict(OLS)
:
y_hat <- as.numeric(y_hat)
predicted <- as.numeric(predict(OLS))
all.equal(y_hat,predicted)
[1] TRUE
beta = solve(t(X) %*% X, t(X) %*% y)
实际上比更加准确solve(t(X) %*% X) %*% t(X) %*% y
。