# Analysing longitudinal data: Multilevel growth models (II)

September 5, 2015
By

[This article was first published on DataScience+, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This is the third post in the longitudinal data series. Previously, we introduced what longitudinal data is, how we can convert between long and wide format data-sets, and a basic multilevel model for analysis. Apparently, the basic multilevel model is not quite enough to analyse our imaginary randomised controlled trial (RCT) data-set. This post is going to continue our analysis and introduce a proper way to handle treatment effects in multilevel models.

## Generate a longitudinal dataset and convert it into long format

As usual, we start by generating our longitudinal data-set and convert it into long format.

```library(MASS)

dat.tx.a <- mvrnorm(n=250, mu=c(30, 20, 28),
Sigma=matrix(c(25.0, 17.5, 12.3,
17.5, 25.0, 17.5,
12.3, 17.5, 25.0), nrow=3, byrow=TRUE))

dat.tx.b <- mvrnorm(n=250, mu=c(30, 20, 22),
Sigma=matrix(c(25.0, 17.5, 12.3,
17.5, 25.0, 17.5,
12.3, 17.5, 25.0), nrow=3, byrow=TRUE))

dat <- data.frame(rbind(dat.tx.a, dat.tx.b))
names(dat) <- c(‘measure.1’, ‘measure.2’, ‘measure.3’)

dat <- data.frame(subject.id = factor(1:500), tx = rep(c(‘A’, ‘B’), each = 250), dat)

rm(dat.tx.a, dat.tx.b)

dat <- reshape(dat, varying = c(‘measure.1’, ‘measure.2’, ‘measure.3’),
idvar = ‘subject.id’, direction = ‘long’)
```

## A multilevel growth model considering treatment effect

This is a RCT data-set, implying that there should be some potential differences between the two treatment groups. Last time we ignored this heterogeneity and specified only a common time effect across the two groups. Intuitively, we could add the treatment variable as an fixed effect in the model to capture the between group difference.

```library(lmerTest)
m1 <- lmer(measure ~ time + tx + (1 | subject.id), data=dat)
```

We still used the package `lmerTest` because it allows the test of fixed effect using approximate degrees of freedom. The model formula is the same as the one we used last time except we added the treatment variable `tx` as an independent variable.

```summary(m1)

Linear mixed model fit by REML t-tests use Satterthwaite approximations to degrees of
freedom [lmerMod]
Formula: measure ~ time + tx + (1 | subject.id)
Data: dat

REML criterion at convergence: 9788.5

Scaled residuals:
Min       1Q   Median       3Q      Max
-2.68035 -0.66131  0.07007  0.66151  2.87378

Random effects:
Groups     Name        Variance Std.Dev.
subject.id (Intercept)  9.918   3.149
Residual               32.114   5.667
Number of obs: 1500, groups:  subject.id, 500

Fixed effects:
Estimate Std. Error        df t value Pr(>|t|)
(Intercept)   30.5932     0.4593 1474.4000  66.610  < 2e-16 ***
time          -2.2201     0.1792  999.0000 -12.389  < 2e-16 ***
txB           -2.3377     0.4062  498.0000  -5.755 1.51e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
(Intr) time
time -0.780
txB  -0.442  0.000

```

It is clear from the model summary that both `time` and `tx` effects are statistically significant and participants in treatment B had a depression score 2.34 points lower than those in treatment A. Should we conclude that treatment B is more effective? Definitely no. The model `m1` is not a proper way to compare group difference in the context of longitudinal data. The ‘treatment effect’ in `m1` is in fact the the average treatment effect along time. In other words, the coefficient -2.34 is the difference in depression score between treatments A and B averaged in time 1, 2, and 3. This is an RCT, we therefore expect to see no (or very little) difference at time 1 (pre-intervention) between the participants in the two treatments. We are actually not interested in knowing the average difference between the two treatments but how the trajectories differ at time 2 and 3.

The concept may be a bit tricky for first-timers but the execution is fairly simple: we just add the interaction term between `time` and `tx`.

```m2 <- lmer(measure ~ time * tx + (1 | subject.id), data=dat)
```

In R, pure interaction term is indicated by the operator `:` so we could specify the model by `time + tx + time:tx`. But we have a short hand instead: `time * tx`. The `*` operator asks R to include both main and treatment effects so we could just use the `*` operator and skip to indicate all effects separately. Please note that the `*` operator also works for higher dimension interactions. For instance, `A * B * C` asks R to include all the three main effects, the three two-way interaction effects, and the one three-way interaction effects.

```summary(m2)

Linear mixed model fit by REML t-tests use Satterthwaite approximations to degrees of
freedom [lmerMod]
Formula: measure ~ time * tx + (1 | subject.id)
Data: dat

REML criterion at convergence: 9721.9

Scaled residuals:
Min       1Q   Median       3Q      Max
-2.71431 -0.65906  0.08873  0.65358  2.63778

Random effects:
Groups     Name        Variance Std.Dev.
subject.id (Intercept) 10.60    3.256
Residual               30.06    5.483
Number of obs: 1500, groups:  subject.id, 500

Fixed effects:
Estimate Std. Error        df t value Pr(>|t|)
(Intercept)   27.7106     0.5683 1456.6000  48.757  < 2e-16 ***
time          -0.7788     0.2452  998.0000  -3.176  0.00154 **
txB            3.4275     0.8037 1456.6000   4.264 2.13e-05 ***
time:txB      -2.8826     0.3468  998.0000  -8.312 4.44e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
(Intr) time   txB
time     -0.863
txB      -0.707  0.610
time:txB  0.610 -0.707 -0.863

```

So we now have three fixed effects in the model `m2`. As mentioned, the `time` effect is averaged across group and the `txB` effect is averaged along time, which provide little information about the trajectory difference between the two treatments. The effect of interest is the `time:txB` interaction. This term is a little bit tricky to interpret: the coefficient value indicates the difference between the two treatments per unit time increment, conditional on average time and treatment effect. In words that human can understand, this indicates how the two treatment groups diverge with the progression of time. For each unit time increase (e.g. from time 1 to time 2), participants in treatment B could expect a 2.88 lower depression score than those in treatment A after accounting for the average treatment effect difference. So at time 2, participants in treatment B could expect a -2.88 x 2 + 3.43 = -2.33 score difference and at time 3, participants in treatment B could expect a -2.88 x 3 + 3.43 = -5.21 difference. We should always consider both treatment main effect and treatment-time interaction effect when comparing treatment differences at a given time. But why? Try to see what would be the treatment difference at time 1 if you omit the treatment main effect.

## To choose the best model

I can tell you model `m2` is better than `m1` and `m0` (the model in the last post) because the data-set is generated by me. But in real life, we could have a hard time deciding which model is better. One way to choose is to see the statistical significance of the additional variables using the `summary()` function as we did above. Other common approaches are Analysis of Variance (ANOVA) table (only for nested models) and model fit indices.

```m0 <- lmer(measure ~ time + (1 | subject.id), data=dat)
anova(m0, m1, m2)

Data: dat
Models:
object: measure ~ time + (1 | subject.id)
..1: measure ~ time + tx + (1 | subject.id)
..2: measure ~ time * tx + (1 | subject.id)
Df    AIC    BIC  logLik deviance  Chisq Chi Df Pr(>Chisq)
object  4 9825.8 9847.0 -4908.9   9817.8
..1     5 9795.6 9822.2 -4892.8   9785.6 32.197      1  1.393e-08 ***
..2     6 9730.6 9762.5 -4859.3   9718.6 66.944      1  2.794e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

```

Here we include both `m0`, `m1`, and `m2` in the `anova()` function because `m0` is nested within `m1`, which in turn nested within `m2`. A model is nested within another if it is a subset of another. In other words, a model with independent variables A and B is nested within another model with independent variables A, B, and C.

This ANOVA table should be quite familiar to you if you have experience with ANOVA analysis. The `Df` column indicates the degrees of freedom associated with the model, which simply means the number of parameters estimated in this case. For example, the 4 degrees of freedom in `m0` indicates there were 4 parameters estimated in the model (1. Coefficient of the fixed intercept effect, 2. Coefficient of time effect, 3. Variance of the random intercept, 4. Variance of the residual).

The AIC and BIC are two commonly used fit indices. Both AIC and BIC consider two factors: how well the model fit the data and how simple the model is. The difference between AIC and BIC is just the penalisation on model complexity (BIC penalise complex model more). I personally find AIC easier to interpret because it asymptotically (when n approaches infinity) converge to the leave-one-out cross-validation (LOOCV) prediction performance. We would cover the concept of cross-validation and prediction performance later so just leave it if you do not understand what they are.

The `logLik` column is the logarithm of likelihood in estimating the models. Basically it is an indicator of how well the model fit the data. `Deviance` is simply (-2 times logLik). Why do we need that? Because the difference in deviance between two nested model follows a Chi-squared distribution under null hypothesis (no difference in deviance between two nested model) which we could use as a test for model fit difference. The difference in deviance is listed under the `Chisq` column. The associated degrees of freedom of the Chi-squared statistics are listed under `Chi Df` (which is simply the number of additional parameter). The last column should be quite clear now: it indicates the p-values for testing the difference in model fit between two nested models. Nonetheless, please be cautious about the ANOVA tests results because type I error would inflate furiously if there are many candidate models.

As `m2` is strictly better than the other two candidate models in AIC, BIC, and the ANOVA tests, we could now conclude it to be our best model so far. Of course there are still some ways to improve further. We will cover the possible improvements and the plotting of the predicted outcomes later. Stay tuned.

To leave a comment for the author, please follow the link and comment on their blog: DataScience+.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.

# Never miss an update! Subscribe to R-bloggers to receive e-mails with the latest R posts.(You will not see this message again.)

Click here to close (This popup will not appear again)