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
ols <- function(data, formula, method="QR") {
mf <- model.frame(formula, data, na.action=na.exclude)
Y <- model.response(mf, type="double")
X <- model.matrix(formula, mf)
if (method == "matrix") b <- solve(crossprod(X)) %*% crossprod(X, Y)
if (method == "QR") {
QR <- qr(X)
Q <- qr.Q(QR)
R <- qr.R(QR)
b <- backsolve(R, crossprod(Q,y))
rownames(b) <- colnames(X)
}
return(b)
}Testing the function:
set.seed(42)
N <- 1000
X1 <- rnorm(N)
X2 <- rnorm(N)
y = 1.5 + 2*X1 + 3*X2 + rnorm(N)
df <- data.frame(
y, X1, X2
)
f <- y ~ X1 + X2
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