21  Introduction to Mixed Models

Exercises in R

22 Introduction

Mixed effects models are powerful statistical tools that allow us to analyze data with hierarchical or grouped structures. In these exercises, we will explore the use of mixed models in R.

23 Setup

You will be using the lme4 package for fitting mixed models and you need to install this package. In the examples we will use tidyverse for data manipulation and ggplot2 for visualization, but if you prefer other alternatives, that is of course fine.

library(lme4)
library(tidyverse)
library(ggplot2)

24 Random intercept and slope

In these exercises we cover both models with random intercept and random slope using the lme4 package in R.

24.1 Drug effect on blood pressure

A study investigates how a drug affects blood pressure. 200 patients are recruited at 10 hospitals. We suspect that the average blood pressure differs between hospitals, mainly due to different routines for measurement, but we expect the drug effect to be consistent.

## Generate the data
databp <- data.frame(hospital=factor(rep(1:10, each=20)),
                 drug=rep(c("placebo", "treatment"), times=100),
                 bp=c(123.9,114.6,119.8,113.3,115.0,121.7,120.2,102.9,121.3,110.4,112.4,111.7,112.6,109.1,114.6,104.3,121.9,113.5,112.1,119.0,121.2,112.6,123.6,118.5,123.2,117.5,121.8,113.8,117.5,112.2,115.6,113.0,112.8,124.9,125.1,108.5,117.1,111.7,123.0,113.7,127.5,121.1,126.0,128.1,125.1,128.8,118.5,124.2,126.9,122.3,128.1,118.7,124.6,116.1,120.9,122.8,128.5,121.5,130.8,131.5,117.8,103.7,125.3,111.7,116.8,120.4,118.9,109.2,121.2,114.6,120.3,117.2,118.4,118.5,119.2,116.9,125.8,117.5,118.7,121.0,125.5,118.3,121.7,112.4,127.3,112.5,131.5,123.2,119.3,110.4,117.0,116.8,119.3,113.8,115.8,115.3,116.6,107.2,118.6,120.1,124.0,124.9,118.8,121.6,129.5,123.4,127.4,118.7,122.6,116.7,127.4,117.1,124.4,120.6,136.1,118.6,128.0,122.3,122.1,121.5,129.1,119.1,122.0,114.7,111.6,122.5,114.5,120.5,131.4,109.6,125.4,115.5,114.0,109.3,113.8,114.2,114.5,120.3,132.3,110.4,118.9,113.8,116.6,104.9,114.3,108.5,117.8,108.1,119.8,108.1,120.2,104.7,108.6,126.1,112.9,111.4,118.1,107.5,117.5,111.8,116.2,112.6,117.1,122.9,113.5,106.8,117.4,113.8,119.4,110.0,111.9,118.6,115.5,107.9,116.1,111.3,122.8,112.7,121.0,109.8,119.3,111.6,118.7,108.7,111.7,123.2,121.2,107.0,115.2,107.3,129.2,119.8,116.9,115.9,116.1,110.8,114.3,110.2,126.5,112.9))

Visualize the data using e.g. boxplots or jitter plots, both overall and by hospital.

## Blood pressure vs drug treatment, treat each measurement as independent
databp %>%
  ggplot(aes(x = drug, y = bp)) +
  geom_boxplot() +
  geom_jitter(width = 0.2, alpha = 0.5) +
  theme_bw()
Figure 24.1: Blood pressure vs drug treatment, overall
## Blood pressure per hospital and drug treatment
databp |>
  ggplot(aes(x = hospital, y = bp, color = drug)) +
  geom_boxplot() +
  geom_jitter(width = 0.2, alpha = 0.5) +
  theme_bw()
Figure 24.2: Blood pressure vs drug treatment, per hospital

Based on the plots, do you think there is variability in blood pressure across hospitals that has to be taken care of?

Yes, the plots suggest that there is variability in blood pressure across hospitals. The boxplots show different medians for each hospital, indicating that the baseline blood pressure differs between hospitals. This variability should be accounted for in our analysis.

If we ignore any grouping structure, we can fit an ordinary linear regression model.

Fit a linear regression model to the data with blood pressure as the response variable and drug treatment as the independent variable.

lm_model <- lm(bp ~ drug, data = databp)
summary(lm_model)

Call:
lm(formula = bp ~ drug, data = databp)

Residuals:
    Min      1Q  Median      3Q     Max 
-12.329  -4.302  -1.016   4.603  16.271 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   120.4020     0.5835 206.338  < 2e-16 ***
drugtreatment  -5.1730     0.8252  -6.269 2.24e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 5.835 on 198 degrees of freedom
Multiple R-squared:  0.1656,    Adjusted R-squared:  0.1614 
F-statistic:  39.3 on 1 and 198 DF,  p-value: 2.236e-09

According to the linear regression model, is the average blood pressure higher in the treatment group compared to the placebo group? By how much does the drug change the blood pressure (on average)?

The average blood pressure is lower in the treatment group compared to the placebo group. The estimated effect of the drug treatment on blood pressure is approximately -5.17, indicating that patients treated with the drug have an average blood pressure that is 5.17 units lower than those treated with the placebo.

Is this a valid model given the data structure? Why/why not?

No, not really. The model treats all observations as independent, but patients within the same hospital may be more similar to each other than patients from different hospitals. This violates the independence assumption and can lead to incorrect standard errors and inference.

To account for the variability in blood pressure across hospitals, fit a mixed model with a random intercept for hospital. Random intercepts allow each hospital to have its own baseline blood pressure.

model1 <- lmer(bp ~ drug + (1 | hospital), data = databp)
summary(model1)

In addition to the summary function, try VarCorr and fixef. The function VarCorr gives the variance and correlations in a model, including the the variance of the random intercept for hospital. fixef extracts the estimated fixed effects of the model.

print(VarCorr(model1), comp="Variance")
 Groups   Name        Variance
 hospital (Intercept) 11.897  
 Residual             23.234  
fixef(model1)
  (Intercept) drugtreatment 
      120.402        -5.173 

Based on the mixed model results, what is the estimated effect of the drug treatment on blood pressure?

The estimated effect of the drug treatment on blood pressure is -5.17.

Based on the mixed model, how much variability in blood pressure is due to differences between hospitals?

To estimate the between-hospital variability, we can use the variance component from VarCorr. The proportion of total variance due to differences between hospitals, called the intraclass correlation coefficient (ICC), can be calculated as:

vc <- as.data.frame(VarCorr(model1))
icc <- vc$vcov[1] / (vc$vcov[1] + vc$vcov[2])

Based on the variance due to the difference between hospitals, do you think it is appropriate to use an ordinary linear regression model instead of a mixed model? Why or why not?

The estimated between-hospital variability is substantial, indicating that there are significant differences in blood pressure between hospitals. This suggests that the ordinary linear regression is not appropriate.

The random intercepts for each hospital can be computed using the ranef function. This gives us the deviations from the overall mean (as reported by the fixed effect intercept) for each hospital.

Compute the random effects using ranef. Note that ranef gives the deviations from the overall mean for each hospital.

ranef(model1)
$hospital
   (Intercept)
1   -2.8246697
2   -0.4104221
3    6.1809293
4   -0.1462214
5    0.2865211
6    4.9829159
7    0.3821799
8   -3.9498001
9   -2.6880141
10  -1.8134188

with conditional variances for "hospital" 

What would the average blood pressure be at hospital 3 for a patient treated with the placebo?

fixef(model1)[1] + ranef(model1)$hospital[3,1]
(Intercept) 
   126.5829 

24.2 Sleep study

The sleepstudy dataset from the lme4 package contains data on reaction times of subjects measured over several days of sleep deprivation (not allowed more than 3h of sleep).

Days 0 and 1 are adaptation and training and will be excluded. Day 2 is baseline, but for simplicity we will rename it to 0.

We will initially work with a subset of the data from only 5 subjects.

##Remove the first two days (before baseline) and adjust Days to be 0 at baseline.
sleepall <- sleepstudy |> filter(Days>=2) |> mutate(Days=Days-2)
##Select a subset of 5 individuals
sleep <- sleepstudy |> filter(Subject %in% c(333, 334, 369, 349, 372), Days>=2) |> mutate(Days=Days-2)

Investigate the dataset. What columns are available? Plot the data, e.g. using ggplot and add a linear regression line to visualize the relationship between reaction time and days of sleep deprivation.

head(sleep, 12)
##Plot Reaction time vs days of sleep deprivation, treat all measurements as independent
sleep |> ggplot(aes(x=Days, y=Reaction)) + geom_point() + geom_smooth(method="lm", se=FALSE) + theme_bw() +
  labs(x = "Days of sleep deprivation",
       y = "Reaction time (ms)")
`geom_smooth()` using formula = 'y ~ x'
##Plot Reaction time vs days of sleep deprivation, treating each subject as a separate group
sleep |> ggplot(aes(x=Days, y=Reaction, color=Subject)) + geom_point() + geom_smooth(method="lm", se=FALSE) + 
  theme_bw() + theme(legend.position = "none") +
  labs(x = "Days of sleep deprivation",
       y = "Reaction time (ms)")
`geom_smooth()` using formula = 'y ~ x'
   Reaction Days Subject
1  276.7693    0     333
2  299.8097    1     333
3  297.1710    2     333
4  338.1665    3     333
5  332.0265    4     333
6  348.8399    5     333
7  333.3600    6     333
8  362.0428    7     333
9  243.3647    0     334
10 254.6723    1     334
11 279.0244    2     334
12 284.1912    3     334
(a) Overall
(b) Per subject
Figure 24.3: Reaction time vs Days of sleep deprivation

Fit a linear model to the data, ignoring the individual differences.

lm_sleep <- lm(Reaction ~ Days, data = sleep)
summary(lm_sleep)

Call:
lm(formula = Reaction ~ Days, data = sleep)

Residuals:
    Min      1Q  Median      3Q     Max 
-39.385 -14.384   0.434  15.952  35.621 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  261.976      5.905   44.36  < 2e-16 ***
Days          14.060      1.412    9.96 3.82e-12 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 20.46 on 38 degrees of freedom
Multiple R-squared:  0.723, Adjusted R-squared:  0.7157 
F-statistic:  99.2 on 1 and 38 DF,  p-value: 3.816e-12

According to this model, how many milliseconds does reaction time increase per day of sleep deprivation?

The model estimates that reaction time increases by approximately 14.060 milliseconds per day of sleep deprivation.

Fit a mixed model with a random intercept for each subject to account for individual differences in reaction time.

mm1_sleep <- lmer(Reaction ~ Days + (1 | Subject), data = sleep)
summary(mm1_sleep)
Linear mixed model fit by REML ['lmerMod']
Formula: Reaction ~ Days + (1 | Subject)
   Data: sleep

REML criterion at convergence: 325.3

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-1.79871 -0.71049 -0.07413  0.64819  1.91336 

Random effects:
 Groups   Name        Variance Std.Dev.
 Subject  (Intercept) 278.9    16.70   
 Residual             183.6    13.55   
Number of obs: 40, groups:  Subject, 5

Fixed effects:
            Estimate Std. Error t value
(Intercept) 261.9758     8.4307   31.07
Days         14.0599     0.9351   15.04

Correlation of Fixed Effects:
     (Intr)
Days -0.388

According to this models, how many milliseconds does reaction time increase per day of sleep deprivation?

The model estimates that reaction time increases by approximately 14.060 milliseconds per day of sleep deprivation, which is the same as in the linear model.

How much variability in reaction time is explained by individual differences?

The estimated variance of the random intercept for Subject is 278.9, which indicates substantial between-subject variability in baseline reaction time.

Now, include the full dataset and redo the analyses, i.e. visualize and fit a mixed model with random intercept.

sleepall |> ggplot(aes(x=Days, y=Reaction, color=Subject)) + geom_point() + geom_smooth(method="lm", se=FALSE) + 
  theme_bw() + theme(legend.position = "none") +
  labs(x = "Days of sleep deprivation",
       y = "Reaction time (ms)")
`geom_smooth()` using formula = 'y ~ x'
Figure 24.4: Reaction time vs Days of sleep deprivation
mm_sleepall <- lmer(Reaction ~ Days + (1 | Subject), data = sleepall)
summary(mm_sleepall)
Linear mixed model fit by REML ['lmerMod']
Formula: Reaction ~ Days + (1 | Subject)
   Data: sleepall

REML criterion at convergence: 1430

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.6261 -0.4450  0.0474  0.5199  4.1378 

Random effects:
 Groups   Name        Variance Std.Dev.
 Subject  (Intercept) 1746.9   41.80   
 Residual              913.1   30.22   
Number of obs: 144, groups:  Subject, 18

Fixed effects:
            Estimate Std. Error t value
(Intercept)  267.967     10.871   24.65
Days          11.435      1.099   10.40

Correlation of Fixed Effects:
     (Intr)
Days -0.354

What is the estimated fixed effect of sleep deprivation on reaction time in the full dataset?

What is the estimated variability in reaction time explained by subject differences?

The plot suggests that also the slope varies between subjects, i.e. some subjects are more affected by sleep deprivation than others. Investigate this further by including a random slope for Days within Subject.

mm2_sleepall <- lmer(Reaction ~ Days + (Days | Subject), data = sleepall)
summary(mm2_sleepall)
Linear mixed model fit by REML ['lmerMod']
Formula: Reaction ~ Days + (Days | Subject)
   Data: sleepall

REML criterion at convergence: 1404.1

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-4.0157 -0.3541  0.0069  0.4681  5.0732 

Random effects:
 Groups   Name        Variance Std.Dev. Corr 
 Subject  (Intercept) 958.35   30.957        
          Days         45.78    6.766   0.18 
 Residual             651.60   25.526        
Number of obs: 144, groups:  Subject, 18

Fixed effects:
            Estimate Std. Error t value
(Intercept)  267.967      8.266  32.418
Days          11.435      1.845   6.197

Correlation of Fixed Effects:
     (Intr)
Days -0.062

Based on the model with random slope, what is the average effect of sleep deprivation on reaction time? How does it compare to the model with only a random intercept?

Compute the random effects for the model with random slope using ranef(mm2_sleepall). What is the estimated effect of sleep deprivation on reaction time for subject 309?

24.3 Orthodontic measurement over time

The Orthodont dataset from the nlme package contains orthodontic measurements of patients over time. The dataset includes measurements of the distance from the pituitary to the pterygomaxillary fissure (mm).

Use this dataset to study how the distance changes with age, accounting for individual differences in growth patterns. Also, take any relevant covariates into account.

library(nlme)
data("Orthodont", package = "nlme")

Start by looking at the data table.

summary(Orthodont)
    distance          age          Subject       Sex    
 Min.   :16.50   Min.   : 8.0   M16    : 4   Male  :64  
 1st Qu.:22.00   1st Qu.: 9.5   M05    : 4   Female:44  
 Median :23.75   Median :11.0   M02    : 4              
 Mean   :24.02   Mean   :11.0   M11    : 4              
 3rd Qu.:26.00   3rd Qu.:12.5   M07    : 4              
 Max.   :31.50   Max.   :14.0   M08    : 4              
                                (Other):84              

Visualize the data.

Orthodont |> 
  ggplot(aes(x = age, y = distance, group = Subject, color = Sex)) +
  geom_line() +
  geom_point() + 
  xlab("Age (years)") +
  ylab("Distance (mm)") +
  theme_bw()
Figure 24.5

What is the grouping structure in the data?

What covariates could be relevant to include in the model?

First, fit a linear model to the data, ignoring the grouping structure, but include any relevant covariates.

Then, fit a mixed model taking the grouping structure into account. Use a random intercept for Subject to account for individual differences in growth patterns.

The groups would be the individual subjects, and the covariates could be age and Sex.

lmortho <- lm(distance ~ age + Sex, data=Orthodont)
summary(lmortho)

Call:
lm(formula = distance ~ age + Sex, data = Orthodont)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.9882 -1.4882 -0.0586  1.1916  5.3711 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 17.70671    1.11221  15.920  < 2e-16 ***
age          0.66019    0.09776   6.753 8.25e-10 ***
SexFemale   -2.32102    0.44489  -5.217 9.20e-07 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.272 on 105 degrees of freedom
Multiple R-squared:  0.4095,    Adjusted R-squared:  0.3983 
F-statistic: 36.41 on 2 and 105 DF,  p-value: 9.726e-13
mmortho <- lmer(distance ~ age + Sex + (1 | Subject), data=Orthodont)
summary(mmortho)
Linear mixed model fit by REML ['lmerMod']
Formula: distance ~ age + Sex + (1 | Subject)
   Data: Orthodont

REML criterion at convergence: 437.5

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.7489 -0.5503 -0.0252  0.4534  3.6575 

Random effects:
 Groups   Name        Variance Std.Dev.
 Subject  (Intercept) 3.267    1.807   
 Residual             2.049    1.432   
Number of obs: 108, groups:  Subject, 27

Fixed effects:
            Estimate Std. Error t value
(Intercept) 17.70671    0.83392  21.233
age          0.66019    0.06161  10.716
SexFemale   -2.32102    0.76142  -3.048

Correlation of Fixed Effects:
          (Intr) age   
age       -0.813       
SexFemale -0.372  0.000

What is the estimated effect of age on distance in the linear model? How does it compare to the mixed model?

What is the estimated variability in distance due to differences between subjects? Does this suggest that the random intercept is needed in the model?