Ordinary Least Squares
- we want to solve something of the form \[ \boldsymbol{Y} = \boldsymbol{X}\boldsymbol{\beta} + \boldsymbol{\epsilon} \]
Solutions
- OLS is typically learned as
\[ (\boldsymbol{X}'\boldsymbol{X})^{-1}\boldsymbol{X}'\boldsymbol{Y} \] - but in practice, this is computationally inefficient - floating point algebra, matrix inversion, etc etc - instead, we use QR decomposition
QR Decomposition
\[ \boldsymbol{X} = \boldsymbol{QR} \] - since R is an upper triangular matrix, we can set
\[ \boldsymbol{Y} = \boldsymbol{QR}\boldsymbol{\beta} \] - meaning that:
\[ \boldsymbol{Q}^{-1}\boldsymbol{Y} = \boldsymbol{R}\boldsymbol{\beta} \] - by definition of the QR decomposition: \[ \boldsymbol{Q}^{-1}\boldsymbol{Y} = \boldsymbol{Q}'\boldsymbol{Y}\] - we can therefore exploit the triangular structur of \(\boldsymbol{R}\) to backsolve the equation: \[ \boldsymbol{Q}'\boldsymbol{Y} = \boldsymbol{R}\boldsymbol{\beta} \]
Implementation
<- function(data, formula, method="QR") {
ols
<- model.frame(formula, data, na.action=na.exclude)
mf <- model.response(mf, type="double")
Y <- model.matrix(formula, mf)
X
if (method == "matrix") b <- solve(crossprod(X)) %*% crossprod(X, Y)
if (method == "QR") {
<- qr(X)
QR <- qr.Q(QR)
Q <- qr.R(QR)
R <- backsolve(R, crossprod(Q,y))
b rownames(b) <- colnames(X)
}
return(b)
}
Testing the function:
set.seed(42)
<- 1000
N <- rnorm(N)
X1 <- rnorm(N)
X2 = 1.5 + 2*X1 + 3*X2 + rnorm(N)
y
<- data.frame(
df
y, X1, X2
)
<- y ~ X1 + X2
f
coef(lm(f, df))
## (Intercept) X1 X2
## 1.496133 1.978201 2.981712
ols(df, f)
## [,1]
## (Intercept) 1.496133
## X1 1.978201
## X2 2.981712
ols(df, f, "matrix")
## [,1]
## (Intercept) 1.496133
## X1 1.978201
## X2 2.981712