2
$\begingroup$

Consider the code, which contains regression using lm of two categorical and one continuous variables without interaction using data from the correct model:

set.seed(12345)

n <- 100
X1 <- as.factor(sample(c("a", "b", "c"), n, replace = TRUE))
X2 <- as.factor(sample(c("d", "e"), n, replace = TRUE))
X3 <- rnorm(n, mean = 0, sd = 1)

effeta <- 1
effetb <- 2
effetc <- 3
effetd <- 4
effete <- 5

Y <- 10 + 3*X3 + effeta*(X1 == "a") + effetb*(X1 == "b") + 
        effetc*(X1 == "c") + effetd*(X2 == "d") + effete*(X2 == "e") + 
        rnorm(n, mean = 0, sd = 1)

model <- lm(Y ~ X1 + X2 + X3)
print(summary(model))

The output is :

Call:
lm(formula = Y ~ X1 + X2 + X3)

Residuals:
     Min       1Q   Median       3Q      Max
-2.74679 -0.60571  0.06521  0.67873  1.85143

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 15.06741    0.20151  74.773  < 2e-16 ***
X1b          0.95913    0.23994   3.997 0.000127 ***
X1c          1.96010    0.23637   8.292 7.26e-13 ***
X2e          1.08521    0.18887   5.746 1.10e-07 ***
X3           3.03324    0.09537  31.804  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9327 on 95 degrees of freedom
Multiple R-squared:  0.9194,    Adjusted R-squared:  0.916
F-statistic: 270.8 on 4 and 95 DF,  p-value: < 2.2e-16

The objective is to understand MATHEMATICALLY what exactly is going on and then to reproduce it in R. More specifically :

  1. One can say that X3 coefficient is correctly estimated. Sources online indicate that lm will take one level as reference, set its effect to zero. In a generic situation, how to predict in advance which variable will be taken as reference? In alphabetical order perhaps? Once the effects are estimated, how do we see that they are correct ?
  2. Although there is no reference, I would believe that R writes the categorical observations using dummy variables to combine everything in a matrix like mentionned in Searle and then do multiple linear regression. How do I write down such a matrix in this case?

Let us write $x_3 \in \mathbb{R}^n$ in place of x3. Then :

\begin{equation*} x_3 = \begin{bmatrix} x_{1,3} \\ x_{2,3} \\ \vdots \\ x_{n,3} \end{bmatrix} \end{equation*}

To do it blindly for the categorical variables, one writes :

\begin{equation*} x_1 = \begin{bmatrix} x_{1,1a} && x_{1,1b} && x_{1,1c} \\ x_{2,1a} && x_{2,1b} && x_{2,1c} \\ \vdots && \vdots && \vdots\\ x_{n,1a} && x_{n,1b} && x_{n,1c} \\ \end{bmatrix}; x_2 = \begin{bmatrix} x_{1,1d} && x_{1,1e} \\ x_{2,1d} && x_{2,1e}\\ \vdots && \vdots \\ x_{n,1d} && x_{n,1e} \\ \end{bmatrix} \end{equation*}

All entries in the two matrices are $0$ or $1$. And then put :

\begin{equation*} X = \begin{bmatrix} 1 &&x_{1,1a} && x_{1,1b} && x_{1,1c} && x_{1,1d} && x_{1,1e} && x_{1,3} \\ 1 && x_{2,1a} && x_{2,1b} && x_{2,1c} && x_{2,1d} && x_{2,1e} &&x_{2,3} \\\\ \vdots && \vdots && \vdots && \vdots && \vdots && \vdots && \vdots \\ 1 && x_{n,1a} && x_{n,1b} && x_{n,1c} && x_{n,1d} && x_{n,1e} && x_{n,3} \end{bmatrix} \end{equation*}

So that we can do regression lm(Y~X). But this is obviously not what happens. How do I take away some columns to get what actually happens?

$\endgroup$
1
  • 2
    $\begingroup$ If you drop the second and fifth column in $X$, you get the design matrix that R passes to the internal QR solver. Check with model.matrix(model). By default, factors are dummy encoded, dropping the "first" level (as being linearly dependent). $\endgroup$
    – Michael M
    Commented Jul 18 at 20:06

2 Answers 2

3
$\begingroup$

There actually is a reference given, Statistical Models in S by Chambers and Hastie.

R does not necessarily use dummy variables. The contrasts option, or contrasts set with the function C describe how factors are coded. The factory default is contr.treatment, which does give dummy variables for all levels except the first. Here's the coding for a four-level variable. Note that it has three columns.

> contr.treatment(4)
  2 3 4
1 0 0 0
2 1 0 0
3 0 1 0
4 0 0 1

There are other options

> contr.sum(4)
  [,1] [,2] [,3]
1    1    0    0
2    0    1    0
3    0    0    1
4   -1   -1   -1
> contr.helmert(4)
  [,1] [,2] [,3]
1   -1   -1   -1
2    1   -1   -1
3    0    2   -1
4    0    0    3
> contr.SAS(4)
  1 2 3
1 1 0 0
2 0 1 0
3 0 0 1
4 0 0 0
> contr.poly(4)
             .L   .Q         .C
[1,] -0.6708204  0.5 -0.2236068
[2,] -0.2236068 -0.5  0.6708204
[3,]  0.2236068 -0.5 -0.6708204
[4,]  0.6708204  0.5  0.2236068

This gives blocks of columns of the design matrix, which just get pasted together sideways. If you have interactions things get a bit more complicated: you multiply each of the columns for one variable by each of the columns for the other variable (easy) and then decide which columns need to be dropped (annoying).

In your example, if you do model.matrix(model) you get the model matrix. Here's the top of it

> head(model.matrix(model))
  (Intercept) X1b X1c X2e          X3
1           1   1   0   0  1.39160685
2           1   0   1   0 -0.30383538
3           1   1   0   1  1.59350961
4           1   1   0   1  0.78706366
5           1   0   0   1  0.45555145
6           1   0   1   1  0.08665505

and, importantly, here is the bottom of it

99            1   1   0   1 -0.49347541
100           1   0   1   1  0.16951075
attr(,"assign")
[1] 0 1 1 2 3
attr(,"contrasts")
attr(,"contrasts")$X1
[1] "contr.treatment"

attr(,"contrasts")$X2
[1] "contr.treatment"

The assign attribute tells you which formula terms each column came from. The first column is the intercept, the next two are X1, then one for X2 and one for X3. It also tells you that the two factor variables did use treatment 'contrasts'.

R gets this information by processing the model formula with terms

> terms(model)
Y ~ X1 + X2 + X3
attr(,"variables")
list(Y, X1, X2, X3)
attr(,"factors")
   X1 X2 X3
Y   0  0  0
X1  1  0  0
X2  0  1  0
X3  0  0  1
attr(,"term.labels")
[1] "X1" "X2" "X3"
attr(,"order")
[1] 1 1 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
attr(,"predvars")
list(Y, X1, X2, X3)
attr(,"dataClasses")
        Y        X1        X2        X3 
"numeric"  "factor"  "factor" "numeric" 

The "term.labels" attribute specifies the terms in the model. model.matrix goes through and makes a set of columns for each term using the specified contrasts and accounting appropriately for interactions.

In this case there aren't any interactions, so you get 1 for the intercept, you get coding by

> contr.treatment(3)
  2 3
1 0 0
2 1 0
3 0 1

for X1, you get coding by

> contr.treatment(2)
  2
1 0
2 1

for X2, and X3 just gets put in as a column on its own.

$\endgroup$
2
  • $\begingroup$ Thank you very much! This is extremely helpful. Just one more thing though. Do you mind answering the first question in the post as well? Namely, given lm approximation of effects, how do you determine mathematically what the effects are in the original format ? $\endgroup$
    – 温泽海
    Commented Jul 19 at 14:30
  • 1
    $\begingroup$ The contrasts matrix specifies which contrasts are estimated -- eg, if you're using treatment contrasts, they are differences between the each other level of the factor and the first level. The ordering is the ordering of the factor levels. If you didn't tell R what ordering to use it will be alphabetic in your current locale $\endgroup$ Commented Jul 21 at 21:40
0
$\begingroup$

Here is an introductory course about linear regression, starting with simple linear regression and then moving on to multiple linear regression (menu on the right side). It explains the basics, also mathematical, in more detail: https://online.stat.psu.edu/stat501/

In principle, linear regression tries to create a line through all given data points, where every data point is as close to the line as possible.

Mathematically, the $\beta$-vector of the equation \begin{align*} Y &= X \beta + \epsilon \\ &= \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \beta_3 x_3 + \epsilon \end{align*} should minimize the sum of squared residuals RSS (residuals = distance between $Y$ and $\hat{Y}$, where $Y$ are the original Y-values and $\hat{Y}$ the fitted values (the results of the equation $\hat{Y} = X\beta$ where $\beta$ is replaced by the estimated $\hat{\beta}$)). Plotting all data points with the estimated $\hat{Y}$ results in a straight line closest to ALL original points in total (not just optimized for 1 point).

This minimization problem is also called Ordinary-Least-Squares (OLS), a derivation of the equation that finally needs to be solved, can be found here for example: How to derive the least square estimator for multiple linear regression?

The final equation then is: $\beta = (X^\top X)^{-1}X^\top Y$

X <- model.matrix(model)
head(X)

X is, as pointed out earlier, the model matrix. It is constructed in such a way, that the first column contains a vector of $1$'s followed by the covariate column vectors. In case of categorial variables, for all levels except the reference category, a column vector is created. Using dummy coding for categorial variables $X1$, $X2$ either a $1$ or $0$ is made depending on the level (a, b, c) / (d, e). If all column vectors of a variable are set to 0 within an observation, the value of the variable for this specific observation equals the reference category. Each row contains 1 observation, thus X has 100 rows in this example.

Solving the equation from above with matrix multiplication gets you all $\beta$ estimates at once:

solve(t(X)%*%X)%*%t(X)%*%Y  

There are several possibilities how OLS estimates can be derived. This post explains what the function lm() uses:
Algorithm for minimization of sum of squares in regression packages

One way to calculate the estimates "by hand" in detail can be found here (as an example for $\beta_0, \beta_1, \beta_2)$:
http://qed.econ.queensu.ca/pub/faculty/abbott/econ351/351note12.pdf

Adapted to your example this means:

\begin{equation} \begin{array}{c} \mathbf{X} = \begin{pmatrix} 1 & X_{1b,1} & X_{1c,1} & X_{2e,1} & X_{3,1} \\ 1 & X_{1b,2} & X_{1c,2} & X_{2e,2} & X_{3,2} \\ \vdots & \vdots & \vdots & \vdots & \vdots \\ 1 & X_{1b,n} & X_{1c,n} & X_{2e,n} & X_{3,n} \\ \end{pmatrix} \end{array} \quad \begin{array}{c} \mathbf{y} = \begin{pmatrix} y_1 \\ y_2 \\ \vdots \\ y_n \\ \end{pmatrix} \end{array} \\ \end{equation}

\begin{equation} \mathbf{X}^\top \mathbf{X} = \begin{pmatrix} n & \sum_{i=1}^{n} X_{1b,i} & \sum_{i=1}^{n} X_{1c,i} & \sum_{i=1}^{n} X_{2e,i} & \sum_{i=1}^{n} X_{3,i} \\[10pt] \sum_{i=1}^{n} X_{1b,i} & \sum_{i=1}^{n} X_{1b,i}^2 & \sum_{i=1}^{n} X_{1b,i} X_{1c,i} & \sum_{i=1}^{n} X_{1b,i} X_{2e,i} & \sum_{i=1}^{n} X_{1b,i} X_{3,i} \\[10pt] \sum_{i=1}^{n} X_{1c,i} & \sum_{i=1}^{n} X_{1c,i} X_{1b,i} & \sum_{i=1}^{n} X_{1c,i}^2 & \sum_{i=1}^{n} X_{1c,i} X_{2e,i} & \sum_{i=1}^{n} X_{1c,i} X_{3,i} \\[10pt] \sum_{i=1}^{n} X_{2e,i} & \sum_{i=1}^{n} X_{2e,i} X_{1b,i} & \sum_{i=1}^{n} X_{2e,i} X_{1c,i} & \sum_{i=1}^{n} X_{2e,i}^2 & \sum_{i=1}^{n} X_{2e,i} X_{3,i} \\[10pt] \sum_{i=1}^{n} X_{3,i} & \sum_{i=1}^{n} X_{3,i} X_{1b,i} & \sum_{i=1}^{n} X_{3,i} X_{1c,i} & \sum_{i=1}^{n} X_{3,i} X_{2e,i} & \sum_{i=1}^{n} X_{3,i}^2 \\ \end{pmatrix} \\ \end{equation}

\begin{equation} \mathbf{X}^\top \mathbf{y} = \begin{pmatrix} \sum_{i=1}^{n} y_i \\ \sum_{i=1}^{n} X_{1b,i} y_i \\ \sum_{i=1}^{n} X_{1c,i} y_i \\ \sum_{i=1}^{n} X_{2e,i} y_i \\ \sum_{i=1}^{n} X_{3,i} y_i \\ \end{pmatrix} \end{equation}

Solving now for each single $\beta_i$ results in:

\begin{equation} % For β0 \beta_0 = \frac{ \left( \sum_{i=1}^{n} y_i \right) - \left( \beta_{1b} \sum_{i=1}^{n} X_{1b,i} + \beta_{1c} \sum_{i=1}^{n} X_{1c,i} + \beta_{2e} \sum_{i=1}^{n} X_{2e,i} + \beta_3 \sum_{i=1}^{n} X_{3,i} \right) }{n} \end{equation}

\begin{equation} % For β1b \beta_{1b} = \frac{ \left( \sum_{i=1}^{n} X_{1b,i} y_i \right) - \left( \beta_0 \sum_{i=1}^{n} X_{1b,i} + \beta_{1c} \sum_{i=1}^{n} X_{1b,i} X_{1c,i} + \beta_{2e} \sum_{i=1}^{n} X_{1b,i} X_{2e,i} + \beta_3 \sum_{i=1}^{n} X_{1b,i} X_{3,i} \right) }{\sum_{i=1}^{n} X_{1b,i}^2} \end{equation}

\begin{equation} % For β1c \beta_{1c} = \frac{ \left( \sum_{i=1}^{n} X_{1c,i} y_i \right) - \left( \beta_0 \sum_{i=1}^{n} X_{1c,i} + \beta_{1b} \sum_{i=1}^{n} X_{1c,i} X_{1b,i} + \beta_{2e} \sum_{i=1}^{n} X_{1c,i} X_{2e,i} + \beta_3 \sum_{i=1}^{n} X_{1c,i} X_{3,i} \right) }{\sum_{i=1}^{n} X_{1c,i}^2} \end{equation}

\begin{equation} % For β2e \beta_{2e} = \frac{ \left( \sum_{i=1}^{n} X_{2e,i} y_i \right) - \left( \beta_0 \sum_{i=1}^{n} X_{2e,i} + \beta_{1b} \sum_{i=1}^{n} X_{2e,i} X_{1b,i} + \beta_{1c} \sum_{i=1}^{n} X_{2e,i} X_{1c,i} + \beta_3 \sum_{i=1}^{n} X_{2e,i} X_{3,i} \right) }{\sum_{i=1}^{n} X_{2e,i}^2} \end{equation}

\begin{equation} % For β3 \beta_3 = \frac{ \left( \sum_{i=1}^{n} X_{3,i} y_i \right) - \left( \beta_0 \sum_{i=1}^{n} X_{3,i} + \beta_{1b} \sum_{i=1}^{n} X_{3,i} X_{1b,i} + \beta_{1c} \sum_{i=1}^{n} X_{3,i} X_{1c,i} + \beta_{2e} \sum_{i=1}^{n} X_{3,i} X_{2e,i} \right) }{\sum_{i=1}^{n} X_{3,i}^2} \end{equation}

Here is the R code:

### preparation

X1.b <- X[,2]
X1.c <- X[,3]
X2.e <- X[,4]
X3. <- X[,5]

n <- nrow(X)

b0 <- coef(model)[1]
b1b <- coef(model)[2]
b1c <- coef(model)[3]
b2e <- coef(model)[4]
b3 <- coef(model)[5]

sum.X1b <- sum(X1.b)
sum.X1c <- sum(X1.c)
sum.X2e <- sum(X2.e)
sum.X3 <- sum(X3.)
sum.Y <- sum(Y)

sum.X1b.sqare <- sum(X1.b^2)
sum.X1c.square <- sum(X1.c^2)
sum.X2e.sqare <- sum(X2.e^2)
sum.X3.sqare <- sum(X3.^2)

sum.X1b.Y <- sum(X1.b*Y)
sum.X1c.Y <- sum(X1.c*Y)
sum.X2e.Y <- sum(X2.e*Y)
sum.X3.Y <- sum(X3*Y)

sum.X1b.X1c <- sum(X1.b*X1.c)
sum.X1b.X2e <- sum(X1.b*X2.e)
sum.X1b.X3 <- sum(X1.b*X3.)

sum.X1c.X2e <- sum(X1.c*X2.e)
sum.X1c.X3 <- sum(X1.c*X3.)

sum.X2e.X3 <- sum(X2.e*X3.)

### Solving equations

beta.0 <- unname(sum.Y - (b1b*sum.X1b + b1c*sum.X1c + b2e*sum.X2e + b3*sum.X3))/n
beta.0

beta.1b <- unname((sum.X1b.Y - (b0*sum.X1b + b1c*sum.X1b.X1c + b2e*sum.X1b.X2e + b3*sum.X1b.X3))/sum.X1b.sqare)
beta.1b

beta.1c <- unname((sum.X1c.Y - (b0*sum.X1c + b1b*sum.X1b.X1c + b2e*sum.X1c.X2e + b3*sum.X1c.X3))/sum.X1c.square)
beta.1c

beta.2e <- unname((sum.X2e.Y - (b0*sum.X2e + b1b*sum.X1b.X2e + b1c*sum.X1c.X2e + b3*sum.X2e.X3))/sum.X2e.sqare)
beta.2e

beta.3 <- unname((sum.X3.Y - (b0*sum.X3 + b1b*sum.X1b.X3 + b1c*sum.X1c.X3 + b2e*sum.X2e.X3))/sum.X3.sqare)
beta.3

2 Add-Ons to the great previous post from Thomas:

(1) The reference category can also be changed, it doesn't have to be necessarily the first level of your variables.

For example setting level "b" in X1 as reference category - new $\beta$ estimate for X1a instead of X1b:

X1 <- relevel(X1,ref=2)
model2 <- lm(Y ~ X1 + X2 + X3)
summary(model2)

More options can be found in this post:
https://stackoverflow.com/questions/3872070/how-to-force-r-to-use-a-specified-factor-level-as-reference-in-a-regression

(2) The following code provides a quick overview how the categorial variables are coded. Just pick all columns of categorial variables. It shows the "unique" rows of the model matrix:

unique(model.matrix(model)[,1:4])  
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.