# Calculate, by hand, fitted values of a regression interaction from a regression output

I am working with an interaction model similar to this one below:

set.seed(1993)

moderating <- sample(c("Yes", "No"),100, replace = T)
x <- sample(c("Yes", "No"), 100, replace = T)
y <- sample(1:100, 100, replace = T)

df <- data.frame(y, x, moderating)

Results <- lm(y ~ x*moderating)
summary(Results)

Call:
lm(formula = y ~ x * moderating)

Residuals:
Min      1Q  Median      3Q     Max
-57.857 -29.067   3.043  22.960  59.043

Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept)         52.4000     6.1639   8.501 2.44e-13 ***
xYes                 8.4571     9.1227   0.927    0.356
moderatingYes      -11.4435     8.9045  -1.285    0.202
xYes:moderatingYes  -0.1233    12.4563  -0.010    0.992
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 30.82 on 96 degrees of freedom
Multiple R-squared:  0.04685,   Adjusted R-squared:  0.01707
F-statistic: 1.573 on 3 and 96 DF,  p-value: 0.2009



I'm learning how to calculate the fitted value of a interaction from a regression table. In the example, the base category (or omitted category) is x= No and moderating = No.

Thus far, I know how to calculate the following fitted values:

#Calulate Fitted Value From a Regression Interaction by hand
#Omitted Variable = X_no.M_no

X_no.M_no <- 52.4000
X_yes.M_no <- 52.4000 + 8.4571
X_no.M_yes <- 52.4000 + -11.4435
X_yes.M_yes #<- ?



I just do not understand how the final category, X_yes.M_yes, is calculated. My initial thoughts were X_yes.M_yes <- 52.4000 + -0.1233, (the intercept plus the interaction term) but that is incorrect. I know its incorrect because, using the predict function, the fitted value of X_yes.M_yes = 49.29032, not 52.2767 as 52.4000 + -0.1233 is equal to.

How do I calculate, by hand, the predicted value of the X_yes.M_yes category?

Here are the predicted values as generated from the predict function in R

#Validated Here Using the Predict Function:
newdat <- NULL
for(m in na.omit(unique(df$$moderating))){ for(i in na.omit(unique(df$$x))){
moderating <- m
x <- i

newdat<- rbind(newdat, data.frame(x, moderating))

}
}

Prediction.1 <- cbind(newdat, predict(Results, newdat, se.fit = TRUE))
Prediction.1



In models with interaction terms, I think it's always instructive to write down the regression model you are working with. Let's denote your "X" values a $$X$$ and what you are calling your "moderator" as $$M$$. In this case, the model is written as:

$$\hat{Y} = \hat{\beta_0}+X\hat{\beta_1}+M\hat{\beta_2}+XM\hat{\beta_{3}}$$

From your results in R, this becomes:

$$\hat{Y} = 52.4000+X8.4571-M11.4435-XM0.1233$$

Now, you have to understand how R is coding your categorical/binary Yes/No Values in your variables $$X$$ and $$M$$. By default, R will code your $$X$$ values as follows (in lexicographical order):

$$\begin{eqnarray*} X & = & \begin{cases} 1 & \text{if X is Yes}\\ 0 & \text{if X is No} \end{cases} \end{eqnarray*}$$

and

R will similary code your $$M$$ values as: $$\begin{eqnarray*} M & = & \begin{cases} 1 & \text{if Moderator is Yes}\\ 0 & \text{if Moderator is No} \end{cases} \end{eqnarray*}$$

Then, as you correctly identified, if $$X$$ is Yes, and $$M$$ is No, the regression equation above becomes:

$$\begin{eqnarray*}\hat{Y} & = & 52.4000+(1)8.4571-(0)11.4435-(1)(0)0.1233 \\ & =& 52.4000+8.4571-(0)-0\\ & =& 52.4000+8.4571 \end{eqnarray*}$$

Now, in the case where Both $$X$$ is Yes, and $$M$$ is Yes, the coded values of both $$X$$ and $$M$$ are equal to 1 and the regression equation becomes:

$$\begin{eqnarray*}\hat{Y} & = & 52.4000+(1)8.4571-(1)11.4435-(1)(1)0.1233 \\ & =& 52.4000+8.4571-11.4435-0.1233\\\end{eqnarray*}$$

and this last term is what you are looking for where you wrote

X_yes.M_yes #<- ?


NOTE:

One thing to note as @Roland mentioned, everything I've written above assumes you are using the default coding in R for binary variables. By default R is coding your "Yes" values as 1 and your "No" values as 0 as I previously mentioned (0 is used for No in this case because it's the first level of the factor variable in lexicographical order). However, there are alternative coding schemes that may be used (e.g. Yes = 1 and No = -1). But from your R code and output, I can tell you are in fact using the binary 0/1 coding as I provided in my answer. You can verify that the 0/1 coding scheme is being used in your R session by issuing the following command:

model.matrix(Results)


This displays the "design matrix" or "model matrix" and displays the codings "behind" each of your categories:

   (Intercept) xYes moderatingYes xYes:moderatingYes
1             1    1             1                  1
2             1    0             1                  0
3             1    0             1                  0
4             1    1             1                  1
5             1    0             1                  0
6             1    0             1                  0
7             1    1             1                  1
.
.
.

• You should mention that your answer explains the default treatment contrasts and that "No" corresponds to 0 because it is the first level of the factor variable. Aug 21, 2020 at 6:04
• I mentioned R's coding and based on the output the OP provided, that's what he has set up -- the default is 0/1 coding. But for other readers who may not know any better, I'll add this. I think it's a good idea. Aug 21, 2020 at 6:12
• Great. But I don't know an R function design.matrix. It's model.matrix. Aug 21, 2020 at 6:15
• Ahh. Thanks. I went from memory -- which isn't great these days! ;-) Or I mixed up my SAS and R code. Will change. Aug 21, 2020 at 6:18