0% found this document useful (0 votes)
13 views

Computer Lab 3 MM

CL3
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

Computer Lab 3 MM

CL3
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

MSc Epidemiology Specialization Course Mixed Models

Exercises day 3: Longitudinal Data II, Modelling Time as Categorical

During this computer lab we will:


• Try to reproduce the results from the lecture (Ex 1 & 2)
• (optional) Repeat the model checks from the lecture (Ex 3)
• Revisit the London schools example, do proper model building and diagnosis (Ex 4)
• Briefly revisit the stroke data from yesterday (Ex 5)
• Analyze data from a multi-center, longitudinal trial (Ex 6)
• Time permitting, take a look at a (two-level) dataset with a complicated “random part” (Ex 7)
Note: you may choose to use R, SPSS or both to answer the questions below. If you get stuck, step-by-step
R code for the first 3 exercises is available in the third section of this document and SPSS syntax (MM
analysis day 3.sps) is available on Moodle.

Exercises
Exercise 1
Repeat the analysis of the Reisby data in R or SPSS (or both). Note: SPSS users will find slight discrepancies
between R and SPSS (for unstructured, AR(1) and heterogeneous AR(1) models), but overall conclusions
remain the same. Note that we can just use the reisby_long.sav dataset since we already did the descriptive
statistics yesterday and we don’t need a copy of the baseline HDRS variable today.
a. Using the full fixed model, fit a CPM with compound symmetry, and compare this to a mixed model
with only random intercepts.
b. Using the full fixed model, also fit CPM’s AR(1), heterogeneous AR(1) and unstructured covariances.
c. Examine the estimated residual variance structure for the models above, and comment on their
appropriateness for the Reisby data.

Exercise 2
In this exercise we’ll examine the (implicit) covariance patterns of mixed effects models, add the cAR(1)
“refinement” to a LME, and look at the effects of baseline adjustment in the Reisby dataset.
a. Now we’ll take a look at the modelled (assumed, or implicit) covariance matrices for the LMEs from the
lecture: using full fixed effects, fit a model with a random intercept and a model with a random intercept
and a random time effect. Examine the covariance structure of both models (use the getVarCov()
function).
b. Add a continuous AR(1) covariance to the model with random intercepts and slopes for time. Use the
likelihood ratio test to compare the three models. Which of these three models fits the data best? Is
the conclusion different if you use the AIC?
c. Finally, choosing the model with the best random effects, reduce the fixed effects, if possible. Re-run
the final model with REML.

Exercise 3
Do some model diagnostics on the final model for the Reisby data from 2d. (Note that some of the graphs for
checking model assumptions are not easily produced in SPSS; see the extra slides on Moodle for help.)

1
a. Test the normality of the residuals.
b. Check the homoscedasticity of the residuals.
c. Check the distributions of the random effects.

Exercise 4
We will re-analyze the schools data (school.sav or school.dat) in R or SPSS according to the strategy
described yesterday: starting with the full fixed model we first test the random effects, then the fixed effects.
(Note that some of the graphs for checking model assumptions are not easily produced in SPSS; see the extra
slides on Moodle for help.)
a. Making use of the full fixed model from Day 1, test (with the likelihood ratio test) whether a random
slope for LRT is necessary, or whether a random intercept model would be sufficient. Which model do
you prefer, and why?
b. Using maximum likelihood estimation and the “random part” chosen in part (a), try to reduce the fixed
part of the model by removing non-significant explanatory variables.
c. Once you have a final model, run it one last time using REML estimation for correct parameter estimates.
Interpret the results of this model.
d. Check the model assumptions for the final model. Are the residuals (approximately) normally dis-
tributed? Also take a look at the distributions of the random intercepts (and random slopes if you used
them).

Exercise 5
On day 2 we examined data from an experiment to promote the recovery of stroke patients. Recall that there
were three experimental groups (a new occupational therapy intervention; the existing stroke rehab program
from the same hospital; and the usual care regime for stroke patients provided in a different hospital). The
response variable was the Barthel index (a measure of functional ability).
Each program lasted for 8 weeks, and all subjects were evaluated at the start of the program and at weekly
intervals until the end of the program; in total, there were 8 post-randomization measures of the Barthel
index. On day 2 we used time as linear, and used random intercepts and slopes for time per person to analyze
the data.
a. What approach(es) from today’s lecture might you consider using to model the random part of this
data?
b. How might you decide between the RI+RS model from yesterday and a CPM from today? Think about
how you would approach this knowing what you know about the observed data, but also if you were
writing a study protocol before any data had been collected.

Exercise 6
On day 1 we looked at a multi-center, randomized, double-blind clinical trial to compare three treatments
for hypertension. One treatment was a new drug (A = Carvedilol) and the other two were standard drugs
for controlling hypertension (B = Nifedipine, C = Atenolol). Twenty-nine centers participated in the trial
and patients were randomized in order of entry. One pre-randomization and four post-treatment visits (at
weeks 3, 5, 7 and 9) were made. We would like to see if there is a difference among the three treatments. The
data can be found in the file bp.csv. Read the data into R or SPSS. The research question is which of the
medicines (treat) is more effective in reducing DBP. Since baseline (pre-randomization) DBP (dbp) will likely
be associated with post-treatment DBP, we wish to include it here as a covariate. Note that the dataset has
already been restructured to “long” format with the baseline dbp included as a (level-2) covariate, and that
the first visit (visit 1) has been removed from the data frame.
a. Start with some exploratory data analysis.
b. First fit a two-level model, examining the effects of treatment, time and their interaction, adjusting for
baseline dbp. Adjust only for multiple measurements per person (i.e. ignore clustering in centers) by

2
including a random intercept and random slope for time per patient. Use ML estimation.
c. Now fit a three-level model, examining the fixed effects of treatment, time, their interaction, and
baseline dbp, including a random intercept and random slope per patient and a random intercept per
center. Use ML estimation.
Note: R users will perhaps find it easier to switch to the lmer() function in the lme4 package,
allowing one to add random effects per level using + (effect|level) to the equation. If you have problems
with convergence of a model, try adding the arguments control = lmerControl(optimizer =
"optimx", calc.derivs = FALSE, optCtrl = list(method = "nlminb", starttests = FALSE,
kkt = FALSE)) to your lmer model.
SPSS users will need to use the syntax for this. Paste the syntax from part (a) and add an extra
RANDOM statement:
/RANDOM=INTERCEPT | SUBJECT(center) COVTYPE(ID)
d. Compare the two models using the likelihood ratio test. Is the random intercept per center significant?
Should you base your decision to include the intercept on this outcome?
e. Is the time*treatment interaction statistically significant? What does that mean for our answer to the
research question?
f. Drop the interaction from the model, and test for a treatment effect.
g. Run the final model once more using REML estimation.
h. Since there was an overall treatment effect (and no time*tx interaction), it may be interesting to look at
pairwise comparisons among the three groups, controlling for visit and baseline DBP. See if you can get
some post-hoc tests of treatments. Hint: EMMEANS statement in SPSS. In R, the package emmeans
contains the function emmeans(), see if you can use the help function or a vignette to see how it works.
i. Interpret the results in the context of the research question.
j. Oops, which step did we forget here?

Exercise 7 (Optional, challenge exercise in R)


Kroesbergen et al. (Eur Respir J 1999) investigated the flow-dependency of exhaled nitric oxide (NO) in
healthy children (n=20) and children with asthma (n=19). The concentration of NO in exhaled are was
measured four times, at four different target values of the flow of exhalation (2, 5, 10, and 20% of their vital
capacity per second). The actual flows differed from the target flows. The following questions are addressed:
is there is an association between NO (pbb) and FLOW (L/sec) for healthy and asthmatic children? Is this
association different for the two groups? The variables NO and FLOW have been been log-transformed.
The dataset no.dat contains the following variables:

Variable Description
num child’s identification number
diagnose 0 = “healthy” and 1 = “asthma”
flow log10 (flow)-log10 (2)
no log10 (NO concentration)

The spaghetti plots are displayed below:

3
healthy athsma

1.5
log10(NO conc)

1.0

0.5

0.0

−0.5 0.0 0.5 −0.5 0.0 0.5


log10(flow)− log10(2)

a. Describe what you see in the graphs. What would be your first impression with respect to the research
questions?
b. In R, fit a model with a fixed effect for flow and random intercepts and random slopes for flow for the
healthy group. What is the mean slope and intercept? What are the estimated variances of the random
components? Is a random slope model necessary, or would a random intercept be sufficient?
c. Answer the same questions for the group of children with asthma.
d. Compare the results of the two groups. What would you conclude on the basis of these results?
e. Fit a fully stratified model for the two groups combined, using an interaction for diagnosis*flow in both
the fixed and random parts of the model, and allowing for a different residual variance the two groups
(note: as far as we know, this cannot be done in SPSS). In R, use the following command:
no.lme.3 <- lme(no~flow + factor(diagnose)+ flow:factor(diagnose),
random=~flow + diagnose + flow*diagnose| num,
weights=varIdent(form=~1|diagnose), method="ML", data=nodat)
The random argument contains a term for group, flow and the group*flow interaction and thus allows for a
separate variances for the random intercepts and slopes for flow in the two diagnosis groups. The weights
argumentfurther allows differing residual variances for the two groups. Check that the log-likelihood of this
model is the sum of the log-likelihoods of the models in b and c.
f. Can the variance structure be simplified by assuming equal residual variances? (Hint: try removing the
weights statement.)
g. Can the covariance structure be further reduced? Try to simplify the random command by removing
the diagnose*flow interaction, and then by removing diagnose.
h. Is the fixed diagnose*flow interaction necessary? In other words: do the asthmatic children indeed have
a significantly more negative slope than the healthy children?
i. Answer the following questions for the model you end up with. What is the slope for the healthy
group, what for the asthma group? What is the standard deviation of the random intercept the healthy

4
children, and is the variance for asthmatic children larger or smaller? What is the residual standard
deviation for the two groups?

Answers to exercises
Exercise 1
See lecture notes and/or step-by-step instructions below.

Exercise 2
See lecture notes and/or step-by-step instructions below.

Exercise 3
See lecture notes and/or step-by-step instructions below.

Exercise 4
a. Read in data, fit the model with all fixed effects, plus random intercepts & slopes for LRT per school,
compare to a model with just random intercepts:
london <- data.frame(read.table(file.path(mypath,"school.dat"), header=TRUE))
sch.lme.1 <- lme(normexam~standlrt + factor(gender)+
factor(schgend) + factor(schav), random=~standlrt | school,
data=london, method="ML")
sch.lme.2 <- update(sch.lme.1, random=~1 | school)
anova(sch.lme.1, sch.lme.2)

## Model df AIC BIC logLik Test L.Ratio p-value


## sch.lme.1 1 11 9300.414 9369.809 -4639.207
## sch.lme.2 2 9 9336.279 9393.057 -4659.139 1 vs 2 39.86525 <.0001
The model with random intercepts and slopes for London Reading Test fits data better than model with just
random intercepts.
b. Now try to reduce the fixed effects:
drop1(sch.lme.1, test="Chisq")

## Single term deletions


##
## Model:
## normexam ~ standlrt + factor(gender) + factor(schgend) + factor(schav)
## Df AIC LRT Pr(>Chi)
## <none> 9300.4
## standlrt 1 9454.8 156.383 < 2.2e-16 ***
## factor(gender) 1 9322.7 24.320 8.159e-07 ***
## factor(schgend) 2 9302.3 5.927 0.05163 .
## factor(schav) 2 9299.1 2.677 0.26227
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The variable schav can be dropped. Drop it, re-fit the model, and check again:

5
sch.lme.3 <- update(sch.lme.1, fixed= normexam~standlrt + factor(gender)+ factor(schgend))
drop1(sch.lme.3, test="Chisq")

## Single term deletions


##
## Model:
## normexam ~ standlrt + factor(gender) + factor(schgend)
## Df AIC LRT Pr(>Chi)
## <none> 9299.1
## standlrt 1 9455.4 158.312 < 2.2e-16 ***
## factor(gender) 1 9321.7 24.659 6.844e-07 ***
## factor(schgend) 2 9301.4 6.267 0.04356 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
No other variables can be dropped. The final model should include fixed intercept, slope for London reading
test, gender and school gender and random intercepts and slopes for reading test.
c. Run the final model using REML estimation for correct parameter estimates, get the summary, and
interpret:
sch.lme.fin <- lme(normexam~standlrt + factor(gender)+ factor(schgend),
random=~standlrt | school, data=london, method="REML")
summary(sch.lme.fin)

## Linear mixed-effects model fit by REML


## Data: london
## AIC BIC logLik
## 9321.058 9377.825 -4651.529
##
## Random effects:
## Formula: ~standlrt | school
## Structure: General positive-definite, Log-Cholesky parametrization
## StdDev Corr
## (Intercept) 0.2892361 (Intr)
## standlrt 0.1227839 0.574
## Residual 0.7418142
##
## Fixed effects: normexam ~ standlrt + factor(gender) + factor(schgend)
## Value Std.Error DF t-value p-value
## (Intercept) -0.1888921 0.05248756 3992 -3.598797 0.0003
## standlrt 0.5539994 0.02011862 3992 27.536647 0.0000
## factor(gender)1 0.1685081 0.03383955 3992 4.979619 0.0000
## factor(schgend)2 0.1797390 0.10170944 62 1.767181 0.0821
## factor(schgend)3 0.1744336 0.08078764 62 2.159162 0.0347
## Correlation:
## (Intr) stndlr fct()1 fct()2
## standlrt 0.313
## factor(gender)1 -0.302 -0.038
## factor(schgend)2 -0.464 0.006 0.150
## factor(schgend)3 -0.458 0.019 -0.230 0.240
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -3.84334881 -0.63773954 0.02664107 0.67776175 3.42763339

6
##
## Number of Observations: 4059
## Number of Groups: 65
Standardized LRT, child’s gender, and school gender all have a significant effect on the exam scores. The
intercept is for boys (gender = 0) at mixed schools (schgend=1) with an average LRT (standlrt = 0). On
average is the normalized exam score 0.55 SD higher when the standardized LRT is increased by 1 SD. Girls
score, on average, 0.17 SD higher than boys, and children at single-sex schools score 0.18 (boys’ schools) or
0.17 (girls’ schools) SD higher than children at mixed-gender schools.
d. Model checks:
# Q-Q plot of residuals, scatterplot residuals vs. fitted
qqnorm(sch.lme.fin)

4
Quantiles of standard normal

−2

−4
−4 −2 0 2

Standardized residuals

plot(sch.lme.fin)

7
2
Standardized residuals

−2

−4

−2 −1 0 1 2

Fitted values

# Dotplot of residuals by school


plot(sch.lme.fin, school ~ resid(.))

8
60

40
school

20

−3 −2 −1 0 1 2

Residuals

# Observed versus fitted values by school


plot(sch.lme.fin, normexam ~ fitted(.) | school, abline = c(0,1))

9
−2 0 1 2 −2 0 1 2

school school school school school


4
0
−4
school school school school school school school school school school
4
0
−4
school school school school school school school school school school
4
0
normexam

−4
school school school school school school school school school school
4
0
−4
school school school school school school school school school school
4
0
−4
school school school school school school school school school school
4
0
−4
school school school school school school school school school school
4
0
−4
−2 0 1 2 −2 0 1 2 −2 0 1 2 −2 0 1 2 −2 0 1 2

Fitted values

# Dotplot of random intercepts & slopes


plot(ranef(sch.lme.fin))

10
(Intercept) standlrt
65
64
63
62
61
60
59
58
57
56
55
54
53
52
51
50
49
48
47
46
45
44
43
42
41
40
39
38
37
school

36
35
34
33
32
31
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
−0.6 −0.4 −0.2 0.0 0.2 0.4 0.6 −0.2 −0.1 0.0 0.1 0.2 0.3

Random effects

# Which is the school with the high random slope?


which.max(ranef(sch.lme.fin)[,2])

## [1] 53
ranef(sch.lme.fin)[53,]

## (Intercept) standlrt
## 53 0.5040069 0.3349675
No obvious problems with normality of residuals, distribution of residuals across fitted values. No one school
stands out with high/low residuals. One clearly higher slope for stdlrt (school #53).

Exercise 5
a. Measurements are at equally spaced intervals and measurements closer together are likely to be more
correlated than measurements further apart, so a CPM with AR(1) is a good option. If we had not yet
seen the data and needed to set up a trial protocol/analysis plan ahead of time, a heterogeneous AR(1)
structure would be a better bet. A CPM with heterogeneous AR(1) will almost certainly fit the data
(a bit) better, but with 8 time points we have to estimate 7 extra variance components; having seen
the data yesterday, it seems unlikely that the variances of the Barthel index are very different across
the time points. A CPM with compound symmetry (equivalent to a LME model with only random
intercepts) is an unlikely candidate, and a CPM with an unstructured correlation matrix is a terrible
choice, because it would require estimating 36 variance parameters!
b. Since we have seen the data and know the individual and group trajectories to be approximately linear,
we know a LME model with random intercepts and slopes for time per person to be a reasonable fit.
If we were to take time as categorical in the fixed part of the model and use a heterogeneous AR(1)

11
model, we would use a lot of extra parameters, both fixed (time and time*tx interactions) and random
(with het AR(1) 8 residual variances and 1 AR(1) correlation term vs. 3 variances (RI, RS, residuals)
and 1 correlation/covariance between RI and RS). We could “test” whether the LME RI+RS with its
many fewer parameters is about as good as a CPM with categorical time and a heterogeneous AR(1)
covariance by comparing the AIC’s of the two models. The log likelihood of the latter will almost
certainly be higher, but the use of all those extra parameters will likely not be worth it. However, if
we were to choose the model before seeing any data (as we usually must do when designing a trial
protocol and statistical analysis plan), it could be very difficult to assume linearity, either overall (fixed)
or individually (random). With a small number (2-3) of post-randomization measurements, using time
as categorical with heterogeneous AR(1) or unstructured CPM’s would be good, flexible choices. With
8 post-randomization measurements, as mentioned above, the number of extra parameters to estimate
gets large rather quickly. I might then be tempted to try adding a time2 term to both the fixed and
random parts of the model. This would add 2 extra fixed effects (time2 and its interaction with tx) and
three extra random parameters (time2 , its correlation with the random intercepts, and its correlation
with the random time effect). While such a model would be difficult to interpret in and of itself, an
estimate of differences of means across the three groups at the end of the study (or at some important
time point considered to be the primary endpoint) coming from this model could give a good answer
the research question.

Exercise 6
a. Read in the data and get some exploratory plots
bp <- data.frame(read.csv(file.path(mypath, "bp.csv"),header=TRUE))
head(bp)

## patient visit center treat dbp dbp1


## 1 1 3 29 C 101 97
## 2 1 5 29 C 88 97
## 3 1 7 29 C 89 97
## 4 1 9 29 C 86 97
## 5 2 3 29 C 72 109
## 6 3 3 5 B 121 117
p <- ggplot(data = bp, aes(x = visit, y = dbp, group = patient)) + theme_bw()
# Spaghetti plot per treatment, ignoring center
p + geom_line() + facet_grid(~ treat)

12
A B C

140

120
dbp

100

80

60

4 6 8 4 6 8 4 6 8
visit

p + geom_line() + stat_summary(aes(group = 1), geom = "point", fun.y = mean,


colour="red", shape = 17, size = 3) +
facet_grid(. ~ treat)

13
A B C

140

120
dbp

100

80

60

4 6 8 4 6 8 4 6 8
visit

# Spaghetti plot per center, ignoring treatment


p + geom_line() + facet_wrap(~ center)

14
1 2 3 4 5 6
140
120
100
80
60
7 8 9 11 12 13
140
120
100
80
60
14 15 18 23 24 25
140
120
dbp

100
80
60
26 27 29 30 31 32
140
120
100
80
60
4 6 8
35 36 37 40 41
140
120
100
80
60
4 6 8 4 6 8 4 6 8 4 6 8 4 6 8
visit

p + geom_line() + stat_summary(aes(group = 1), geom = "point", fun.y = mean,


colour="red", shape = 17, size = 2) +
facet_wrap(~ center)

15
1 2 3 4 5 6
140
120
100
80
60
7 8 9 11 12 13
140
120
100
80
60
14 15 18 23 24 25
140
120
dbp

100
80
60
26 27 29 30 31 32
140
120
100
80
60
4 6 8
35 36 37 40 41
140
120
100
80
60
4 6 8 4 6 8 4 6 8 4 6 8 4 6 8
visit

b. Here, I use the the lmer() function from nlme package, which I find easier for defining the random
effects for 3-level models. Detach the lme4 library, and attach nlme. (You can leave them both attached,
but there is some overlap in functions so it’s better to only have one open at a time). Then I fit the
two-level model (ignoring center): fixed effects time, tx, baseline DBP, time*tx and random intercept &
time per patient, using ML estimation (REML=FALSE).
detach("package:nlme")
library(lme4)
bp.2l.lme <- lmer(dbp ~ visit + treat+ visit*treat + dbp1 + (visit|patient),
data=bp, REML=FALSE)
summary(bp.2l.lme)

## Linear mixed model fit by maximum likelihood ['lmerMod']


## Formula: dbp ~ visit + treat + visit * treat + dbp1 + (visit | patient)
## Data: bp
##
## AIC BIC logLik deviance df.resid
## 7501.6 7556.6 -3739.8 7479.6 1081
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -3.9424 -0.5186 -0.0414 0.5045 3.4140
##
## Random effects:
## Groups Name Variance Std.Dev. Corr
## patient (Intercept) 50.9908 7.1408
## visit 0.4885 0.6989 -0.47

16
## Residual 33.0284 5.7470
## Number of obs: 1092, groups: patient, 288
##
## Fixed effects:
## Estimate Std. Error t value
## (Intercept) 44.82497 9.02458 4.967
## visit -0.58399 0.15629 -3.737
## treatB -1.57395 1.59435 -0.987
## treatC -3.28604 1.58231 -2.077
## dbp1 0.50026 0.08692 5.755
## visit:treatB 0.05733 0.22140 0.259
## visit:treatC 0.04369 0.21842 0.200
##
## Correlation of Fixed Effects:
## (Intr) visit treatB treatC dbp1 vst:tB
## visit -0.094
## treatB -0.120 0.538
## treatC -0.078 0.542 0.490
## dbp1 -0.992 -0.001 0.035 -0.008
## visit:tretB 0.059 -0.706 -0.770 -0.382 0.008
## visit:tretC 0.063 -0.716 -0.385 -0.771 0.005 0.505
## optimizer (nloptwrap) convergence code: 0 (OK)
## Model failed to converge with max|grad| = 0.00325065 (tol = 0.002, component 1)
c. Add a random intercept per center to model in part b:
bp.3l.lme <- lmer(dbp ~ visit + treat+ visit*treat + dbp1 +
(visit|patient) + (1|center), data=bp, REML=F)
#summary(bp.3l.lme)

d. Compare the two:


anova(bp.2l.lme,bp.3l.lme)

## Data: bp
## Models:
## bp.2l.lme: dbp ~ visit + treat + visit * treat + dbp1 + (visit | patient)
## bp.3l.lme: dbp ~ visit + treat + visit * treat + dbp1 + (visit | patient) + (1 | center)
## npar AIC BIC logLik deviance Chisq Df Pr(>Chisq)
## bp.2l.lme 11 7501.6 7556.6 -3739.8 7479.6
## bp.3l.lme 12 7492.4 7552.4 -3734.2 7468.4 11.221 1 0.0008087 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Adding a random intercept per center significantly improves the model fit. It also reduces the variance of the
patient random intercepts, indicating that some of the variation between patients is actually due to variation
between centers. Since the design is 3-level, (at least) a random intercept per center should be included. The
decision to include this random intercept per center should be based on the study design, and not on the
results of testing the random intercept.
e. The treatment*time interaction can be dropped (LRT = 0.07 on 2 df, p almost 1).
bp.3l.lme2 <- lmer(dbp ~ visit + treat + dbp1 + (visit|patient) + (1|center),
data=bp, REML=F)
anova(bp.3l.lme, bp.3l.lme2)

## Data: bp
## Models:

17
## bp.3l.lme2: dbp ~ visit + treat + dbp1 + (visit | patient) + (1 | center)
## bp.3l.lme: dbp ~ visit + treat + visit * treat + dbp1 + (visit | patient) + (1 | center)
## npar AIC BIC logLik deviance Chisq Df Pr(>Chisq)
## bp.3l.lme2 10 7488.5 7538.5 -3734.2 7468.5
## bp.3l.lme 12 7492.4 7552.4 -3734.2 7468.4 0.0707 2 0.9652
Interpretation: starting from week 3, there is no difference in the patterns of DBP over time for the three
treatment groups! That either means that there is no treatment effect in the study, or that the treatment
effect was already present by week 3, the first post-randomization measurement we have here.
f. Treatment effect cannot be dropped, as it significantly improves the fit of the model:
bp.3l.lme3 <- lmer(dbp ~ visit + dbp1 + (visit|patient) + (1|center), data=bp, REML=F)
anova(bp.3l.lme2, bp.3l.lme3)

## Data: bp
## Models:
## bp.3l.lme3: dbp ~ visit + dbp1 + (visit | patient) + (1 | center)
## bp.3l.lme2: dbp ~ visit + treat + dbp1 + (visit | patient) + (1 | center)
## npar AIC BIC logLik deviance Chisq Df Pr(>Chisq)
## bp.3l.lme3 8 7494.3 7534.3 -3739.2 7478.3
## bp.3l.lme2 10 7488.5 7538.5 -3734.2 7468.5 9.8133 2 0.007397 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
g. Run final model with REML, get summary of model & 95% CI’s.
#This gives convergence error
#bp.3l.lmefin <- lmer(dbp ~ visit + treat + dbp1 + (visit|patient) + (1|center), data=bp, REML=T)
library(optimx) #shouldn't be necessary, but added just in case
bp.3l.lmefin <- lmer(dbp ~ visit + treat + dbp1 + (visit|patient) + (1|center),
data=bp, REML=T,
control = lmerControl(optimizer = "optimx",
calc.derivs = FALSE,
optCtrl = list(method = "nlminb",
starttests = FALSE,
kkt = FALSE)))
summary(bp.3l.lmefin)

## Linear mixed model fit by REML ['lmerMod']


## Formula: dbp ~ visit + treat + dbp1 + (visit | patient) + (1 | center)
## Data: bp
## Control:
## lmerControl(optimizer = "optimx", calc.derivs = FALSE, optCtrl = list(method = "nlminb",
## starttests = FALSE, kkt = FALSE))
##
## REML criterion at convergence: 7470.4
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -3.9044 -0.5108 -0.0248 0.5126 3.4560
##
## Random effects:
## Groups Name Variance Std.Dev. Corr
## patient (Intercept) 51.4619 7.1737
## visit 0.4995 0.7068 -0.55
## center (Intercept) 4.7827 2.1869

18
## Residual 33.0481 5.7487
## Number of obs: 1092, groups: patient, 288; center, 29
##
## Fixed effects:
## Estimate Std. Error t value
## (Intercept) 47.48150 8.93245 5.316
## visit -0.54645 0.08976 -6.088
## treatB -1.27129 0.97663 -1.302
## treatC -3.02811 0.96637 -3.133
## dbp1 0.47860 0.08596 5.567
##
## Correlation of Fixed Effects:
## (Intr) visit treatB treatC
## visit -0.066
## treatB -0.122 -0.010
## treatC -0.051 -0.019 0.487
## dbp1 -0.994 0.009 0.071 -0.002
h. Get “estimated marginal means” and estimated mean differences between treatment groups, controlling
for other variables in the model using the emmeans() function in the emmeans package. We can also get
the confidence intervals of the estimated mean differences:
library(emmeans)
emmeans(bp.3l.lmefin, pairwise~"treat")

## $emmeans
## treat emmean SE df lower.CL upper.CL
## A 93.4 0.840 67.0 91.8 95.1
## B 92.2 0.862 69.9 90.4 93.9
## C 90.4 0.840 71.8 88.7 92.1
##
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
##
## $contrasts
## contrast estimate SE df t.ratio p.value
## A - B 1.27 0.981 266 1.296 0.3987
## A - C 3.03 0.970 264 3.120 0.0057
## B - C 1.76 0.988 263 1.778 0.1791
##
## Degrees-of-freedom method: kenward-roger
## P value adjustment: tukey method for comparing a family of 3 estimates
confint(emmeans(bp.3l.lmefin, pairwise~"treat"))

## $emmeans
## treat emmean SE df lower.CL upper.CL
## A 93.4 0.840 67.0 91.8 95.1
## B 92.2 0.862 69.9 90.4 93.9
## C 90.4 0.840 71.8 88.7 92.1
##
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
##
## $contrasts
## contrast estimate SE df lower.CL upper.CL

19
## A - B 1.27 0.981 266 -1.041 3.58
## A - C 3.03 0.970 264 0.741 5.32
## B - C 1.76 0.988 263 -0.573 4.09
##
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
## Conf-level adjustment: tukey method for comparing a family of 3 estimates
Aside: if there had been a significant interaction between visit and treatment group, we might have wanted
this contrast among treatment groups at one (or more) points in time. Often in a clinical trial, the primary
outcome is the difference between treatment groups at a particular point in the follow-up. We can use the
emmeans() function with an at argument to get those contrasts. First we fit the model with the interaction
(using REML estimation and the optimization controls), then we try a couple of contrasts.
aside <- lmer(dbp ~ visit*treat + dbp1 + (visit|patient) +
(1|center), data=bp, REML=T,
control = lmerControl(optimizer = "optimx", calc.derivs = FALSE,
optCtrl = list(method = "nlminb",
starttests = FALSE,
kkt = FALSE)))
emmeans(aside, pairwise ~ "treat", at = list(visit = 3))

## $emmeans
## treat emmean SE df lower.CL upper.CL
## A 95.1 0.935 95.8 93.3 97.0
## B 93.7 0.965 100.9 91.8 95.6
## C 92.0 0.946 105.2 90.1 93.8
##
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
##
## $contrasts
## contrast estimate SE df t.ratio p.value
## A - B 1.42 1.15 271 1.238 0.4319
## A - C 3.14 1.14 270 2.754 0.0172
## B - C 1.72 1.16 271 1.477 0.3035
##
## Degrees-of-freedom method: kenward-roger
## P value adjustment: tukey method for comparing a family of 3 estimates
emmeans(aside, pairwise ~ "treat", at = list(visit = 6))

## $emmeans
## treat emmean SE df lower.CL upper.CL
## A 93.4 0.840 67.4 91.7 95.1
## B 92.1 0.861 70.1 90.4 93.8
## C 90.4 0.839 71.9 88.7 92.0
##
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
##
## $contrasts
## contrast estimate SE df t.ratio p.value
## A - B 1.26 0.979 266 1.282 0.4069
## A - C 3.02 0.968 264 3.114 0.0058
## B - C 1.76 0.985 262 1.787 0.1760

20
##
## Degrees-of-freedom method: kenward-roger
## P value adjustment: tukey method for comparing a family of 3 estimates
confint(emmeans(aside, pairwise ~ "treat", at = list(visit = 9)))

## $emmeans
## treat emmean SE df lower.CL upper.CL
## A 91.6 0.991 111 89.7 93.6
## B 90.6 1.000 110 88.6 92.5
## C 88.8 0.967 110 86.8 90.7
##
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
##
## $contrasts
## contrast estimate SE df lower.CL upper.CL
## A - B 1.09 1.22 264 -1.7894 3.97
## A - C 2.89 1.20 259 0.0586 5.72
## B - C 1.80 1.21 257 -1.0457 4.65
##
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
## Conf-level adjustment: tukey method for comparing a family of 3 estimates
Notice that the estimated pairwise differences are different at the different time points, because there is an
interaction in the model.
i. Coming back from our aside: when we control for treatment and baseline DBP, patients’ DBP decreases
by a little over half a mmHg per week on average; this decrease is estimated to be the same for all three
treatment groups (because the treat*visit interaction was not significant and we therefore left it out of
the model). There is a significant treatment effect: treatment C reduces blood pressure compared to
treatment A by 3 points; the other two differences were smaller, and not statistically significant.
j. Before we look into interpreting the model and running pairwise comparisons on the treatment effects,
we should have done some model diagnostics! What would you check?

Exercise 7
a. There seems to be a negative association between NO and flow. Clearly random intercepts are necessary
for both groups, possibly also random slopes. The slopes seem to be steeper in the asthmatic group.
There is also more variation between children in the asthmatic group than in the healthy group.
Read in the data, and go back to nlme package.
## detach("package:lme4")
library(nlme)
nodat <- read.table(file.path(mypath, "no.dat"),header=TRUE)

FYI: how I got the spaghetti plots:


diagnames <- c('0'="healthy", '1'="athsma")
ggplot(data = nodat, aes(x = flow, y = no, group = num)) +
geom_point() + geom_line() + facet_grid(. ~ diagnose,
labeller = as_labeller(diagnames)) + theme_bw() +
xlab(expression(log[10]*"(flow)" *-log[10]*"(2)")) +
ylab(expression(log[10]*"(NO conc)"))
b. Make subset of healthy children, run full model (NO as function of flow, random int+slope):

21
healthy <- subset(nodat,nodat$diagnose==0)
no.lme.h <- lme(no~flow , random=~flow| num, method="ML", data=healthy)
summary(no.lme.h)

## Linear mixed-effects model fit by maximum likelihood


## Data: healthy
## AIC BIC logLik
## -162.0047 -147.7125 87.00234
##
## Random effects:
## Formula: ~flow | num
## Structure: General positive-definite, Log-Cholesky parametrization
## StdDev Corr
## (Intercept) 0.1372792 (Intr)
## flow 0.1018528 -0.839
## Residual 0.0496389
##
## Fixed effects: no ~ flow
## Value Std.Error DF t-value p-value
## (Intercept) 0.8108212 0.03162558 59 25.63815 0
## flow -0.3452360 0.02754635 59 -12.53291 0
## Correlation:
## (Intr)
## flow -0.707
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -2.15285664 -0.58959632 -0.03373757 0.48607120 2.46376179
##
## Number of Observations: 80
## Number of Groups: 20
Mean slope for healthy children is -0.345 (se 0.0275), mean intercept 0.811. Standard deviation for random
intercept is 0.137 (variance = 0.0188), and for the random slope 0.102 (var = 0.0104).
Check random slope for healthy group:
no.lme.h2 <- update(no.lme.h, random= ~1|num)
anova(no.lme.h, no.lme.h2)

## Model df AIC BIC logLik Test L.Ratio p-value


## no.lme.h 1 6 -162.0047 -147.7125 87.00234
## no.lme.h2 2 4 -143.5307 -134.0026 75.76534 1 vs 2 22.47401 <.0001
p <0.0001 / 2 →p < 0.00005, so random slope for flow is necessary.
c. Do the same for the asthmatic children:
asthma <- subset(nodat,nodat$diagnose==1)
no.lme.a <- lme(no~flow , random=~flow| num, method="ML", data=asthma)
summary(no.lme.a)

## Linear mixed-effects model fit by maximum likelihood


## Data: asthma
## AIC BIC logLik
## -64.1888 -50.2044 38.0944
##

22
## Random effects:
## Formula: ~flow | num
## Structure: General positive-definite, Log-Cholesky parametrization
## StdDev Corr
## (Intercept) 0.38293951 (Intr)
## flow 0.18164821 -0.393
## Residual 0.06682339
##
## Fixed effects: no ~ flow
## Value Std.Error DF t-value p-value
## (Intercept) 0.8517394 0.08940237 56 9.527034 0
## flow -0.5848256 0.04793471 56 -12.200462 0
## Correlation:
## (Intr)
## flow -0.346
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -2.61299643 -0.53347738 0.04039453 0.38132723 2.06154236
##
## Number of Observations: 76
## Number of Groups: 19
no.lme.a2 <- update(no.lme.a, random= ~1|num)
anova(no.lme.a, no.lme.a2)

## Model df AIC BIC logLik Test L.Ratio p-value


## no.lme.a 1 6 -64.18880 -50.20440 38.09440
## no.lme.a2 2 4 -46.52904 -37.20611 27.26452 1 vs 2 21.65976 <.0001
Mean slope for asthmatic children is -0.585 (se 0.0479), mean intercept 0.852. Standard deviation for the
random intercept is 0.383 (variance = 0.147) and for random slope 0.182 (var=0.0330). LRT random slopes:
p <0.0001 / 2 →p < 0.00005, so random slope is also necessary here.
d. The mean slope appears to be different (-0.345 for healthy vs -0.585 for asthmatic children) for the two
groups, with asthmatics having a steeper (negative) slope. At lower flows, the asthmatic children are
exhaling, on average, more log10 (NO) than the healthy children; at the higher flows, the two groups
have about the same average exhaled log10 (NO). The variation in the random intercepts and slopes is
larger for the asthmatic children.
e. The fully stratified model on the full dataset:
no.lme.3 <- lme(no~flow + factor(diagnose)+ flow:factor(diagnose),
random=~flow + diagnose + flow*diagnose| num,
weights=varIdent(form=~1|diagnose), method="ML", data=nodat)
summary(no.lme.3)

## Linear mixed-effects model fit by maximum likelihood


## Data: nodat
## AIC BIC logLik
## -218.1935 -169.3958 125.0967
##
## Random effects:
## Formula: ~flow + diagnose + flow * diagnose | num
## Structure: General positive-definite, Log-Cholesky parametrization
## StdDev Corr
## (Intercept) 0.13727910 (Intr) flow diagns

23
## flow 0.10185272 -0.839
## diagnose 0.26189010 0.823 -0.680
## flow:diagnose 0.17568175 -0.014 -0.230 0.062
## Residual 0.04963893
##
## Variance function:
## Structure: Different standard deviations per stratum
## Formula: ~1 | diagnose
## Parameter estimates:
## 0 1
## 1.000000 1.346191
## Fixed effects: no ~ flow + factor(diagnose) + flow:factor(diagnose)
## Value Std.Error DF t-value p-value
## (Intercept) 0.8108212 0.03163596 115 25.629735 0.0000
## flow -0.3452360 0.02755540 115 -12.528798 0.0000
## factor(diagnose)1 0.0409182 0.09480556 37 0.431601 0.6685
## flow:factor(diagnose)1 -0.2395895 0.05527596 115 -4.334425 0.0000
## Correlation:
## (Intr) flow fct()1
## flow -0.707
## factor(diagnose)1 -0.334 0.236
## flow:factor(diagnose)1 0.353 -0.499 -0.400
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -2.6129961 -0.5650606 0.0096258 0.4358345 2.4637604
##
## Number of Observations: 156
## Number of Groups: 39
Note that there are now 4 different standard deviations for the random effects: one each for intercepts and
slopes for flow in the healthy group (0.137 and 0.102, respectively) and one each for intercepts and slopes for
flow in the asthma group (unfortunately, these cannot be read easily from the output; with variances we
cannot simply add or subtract main effects and interactions as we can with fixed effects). With the weights
argument we also allow two different residual standard deviations: one for the healthy group, and one for the
asthmatic children. Note that the standard deviations for the healthy group are the same as in the stratified
analysis in (b). Note also that the log-likelihood of the full model in (b) was 87.002 and that the log-likelihood
of the full model in (c) was 38.094. If we add these together, we get 125.09, which is the log-likelihood of
the current model. The current model is thus another way of doing a completely stratified model, as in (b)
and (c). However, since we now have all children in one model, we can test various parts of the stratification
across the two groups by leaving out interactions, etc.
f. Reduce the variance structure: take out differing residual variances (so: remove the weights argument),
test against model with differing residual variances.
no.lme.4 <- lme(no~flow + factor(diagnose)+ flow:factor(diagnose),
random=~flow + diagnose + flow*diagnose| num, method="ML", data=nodat)
anova(no.lme.3,no.lme.4)

## Model df AIC BIC logLik Test L.Ratio p-value


## no.lme.3 1 16 -218.1935 -169.3958 125.0967
## no.lme.4 2 15 -216.8824 -171.1346 123.4412 1 vs 2 3.311073 0.0688
p = 0.0688/2 = 0.0344. It would appear that the residual variances differ. Leave weights argument in.
Is it necessary to have different variances of random slopes for flow in the two groups?

24
no.lme.5 <- lme(no~flow + factor(diagnose)+ flow:factor(diagnose),
random=~flow + diagnose| num, weights=varIdent(form=~1|diagnose),
method="ML", data=nodat)
anova(no.lme.3,no.lme.5)

## Model df AIC BIC logLik Test L.Ratio p-value


## no.lme.3 1 16 -218.1935 -169.3958 125.0967
## no.lme.5 2 12 -222.7014 -186.1031 123.3507 1 vs 2 3.492107 0.4791
p = 0.4791/2 = 0.2396, separate variances for random slope not necessary.
Try to reduce variance structure further: necessary to have different random intercept variances for the two
groups?
no.lme.6 <- lme(no~flow + factor(diagnose)+ flow:factor(diagnose),
random=~flow | num, weights=varIdent(form=~1|diagnose),
method="ML", data=nodat)
anova(no.lme.5,no.lme.6)

## Model df AIC BIC logLik Test L.Ratio p-value


## no.lme.5 1 12 -222.7014 -186.1031 123.3507
## no.lme.6 2 9 -203.5273 -176.0786 110.7636 1 vs 2 25.17408 <.0001
p < 0.0001 / 2 = 0.00005, separate variances for random intercepts necessary. This is not surprising, we saw
that in the spaghetti plots.
h. Is the fixed flow*group interaction necessary? The model without interaction vs. with:
no.lme.7 <- lme(no~flow + factor(diagnose), random=~flow + diagnose| num,
weights=varIdent(form=~1|diagnose), method="ML", data=nodat)
anova(no.lme.5,no.lme.7)

## Model df AIC BIC logLik Test L.Ratio p-value


## no.lme.5 1 12 -222.7014 -186.1031 123.3507
## no.lme.7 2 11 -208.2011 -174.6527 115.1005 1 vs 2 16.5003 <.0001
p < 0.0001, the interaction is significant. The asthmatic children have a significantly different (more negative,
we know from parts (b) and (c)) slope than the healthy children.
i. It turns out that the fully stratfied model fits the data best. Run that final model one last time, with
REML estimation:
no.lme.final <- lme(no~flow + factor(diagnose)+ flow:factor(diagnose),
random=~flow + diagnose| num, weights=varIdent(form=~1|diagnose),
method="REML", data=nodat)
summary(no.lme.final)

## Linear mixed-effects model fit by REML


## Data: nodat
## AIC BIC logLik
## -204.3626 -168.076 114.1813
##
## Random effects:
## Formula: ~flow + diagnose | num
## Structure: General positive-definite, Log-Cholesky parametrization
## StdDev Corr
## (Intercept) 0.16080858 (Intr) flow
## flow 0.14515225 -0.852
## diagnose 0.29348041 0.384 0.001

25
## Residual 0.04850413
##
## Variance function:
## Structure: Different standard deviations per stratum
## Formula: ~1 | diagnose
## Parameter estimates:
## 0 1
## 1.000000 1.420661
## Fixed effects: no ~ flow + factor(diagnose) + flow:factor(diagnose)
## Value Std.Error DF t-value p-value
## (Intercept) 0.8103540 0.03639792 115 22.263748 0.0000
## flow -0.3464639 0.03557681 115 -9.738475 0.0000
## factor(diagnose)1 0.0413823 0.09587913 37 0.431609 0.6685
## flow:factor(diagnose)1 -0.2374768 0.05385543 115 -4.409524 0.0000
## Correlation:
## (Intr) flow fct()1
## flow -0.778
## factor(diagnose)1 -0.380 0.295
## flow:factor(diagnose)1 0.514 -0.661 -0.398
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -2.58783073 -0.53427169 0.04976964 0.47714277 2.26198962
##
## Number of Observations: 156
## Number of Groups: 39
Again, we should do model diagnostics before interpreting the final model. However, in the interest of time:
Mean/overall intercept and slope for healthy children: 0.810 and -0.346; mean/overall intercept and slope for
asthmatic children: (0.810+0.041=) 0.852 and (-0.346-0.237=) -0.584.
Standard deviation random intercepts healthy children: 0.161 (var = 0.0259); standard deviation random
intercept asthmatic children cannot easily be calculated, but is larger than that of healthy children.
Residual standard deviation for healthy children is 0.048; for asthmatic children 1.42*0.0485 = 0.0689. So
the asthmatic children have both more between and more within-child variation than the healthy children in
their (log10 ) NO measurements.

Step-by-step answers to exercises 1-3


You can copy the code below into the R editor (or better yet: into the RStudio editor), and run one or more
lines at a time by highlighting them and pressing Ctrl+Enter (Windows) or Command+Enter (Mac).
Before we get started, we’ll first load a few packages we will need for our analysis.
library(foreign)
library(nlme)
library(ggplot2)

If you get an error message that the package is not available, you will first have to install it. For instance the
foreign library: install.packages("foreign"), or use the menu in RStudio (Tools - Install Packages).
Note: before running any of the R code below, be sure to change the path name to the directory in which you
have stored your data! This can be done using the menu in RStudio (Session- Set Working Directory. . . ), by
using the setwd() function or as below by defining the path (here I call it mypath) and using the file.path()
function.

26
Exercise 1
Read in the data:
# CHANGE THIS TO YOUR OWN DIRECTORY!! Or use setwd() to point to directory with datasets
mypath <- "O:\\Biostatistiek\\Onderwijs\\MedicalStatistics\\Mixed Models\\datasets\\"
reisby.long<-read.spss(file.path(mypath,"reisby_long.sav"), to.data.frame = TRUE, use.missings = TRUE)
head(reisby.long)

## id hdrs week endo


## 1 101 26 0 0
## 2 101 22 1 0
## 3 101 18 2 0
## 4 101 7 3 0
## 5 101 4 4 0
## 6 101 3 5 0
We do still need to change id and week to factor variables, and keep “time” (a copy of week) as continuous
for later use:
reisby.long$id <- factor(reisby.long$id)
reisby.long$time <- reisby.long$week
reisby.long$week <- factor(reisby.long$week)

Now we’ll model the Reisby data using covariance pattern models (CPMs). Note: if a linear mixed model
does not contain one or more random effects (intercept and/or slope(s)), strictly speaking it is not a “mixed
model” but a CPM. To fit models without random effects but with correlated residuals in R, you use the
function gls() in the nlme package.
We’ll fit a few models from the lecture. First, we’ll fit a model with fixed effects for week(factor/categorical),
endo, and their interaction, no random effects, but a residual correlation matrix with a compound symmetry
pattern.
gls.cs <- gls(hdrs ~ week*endo, correlation = corCompSymm(form=~1|id),
data=reisby.long, na.action="na.omit", method="ML")
#summary(gls.cs)
coef(gls.cs)

## (Intercept) week1 week2 week3 week4 week5


## 22.6608707 -2.1781121 -5.6631769 -7.3160431 -10.0401811 -11.3684527
## endo week1:endo week2:endo week3:endo week4:endo week5:endo
## 1.1911784 1.3682638 1.1084251 0.8296468 0.7197389 0.1202209
To get the estimated variance-covariance matrix of the residuals from this model (it is not always possible -
or at least not easy - to get this variance-covariance matrix from more complex CPMs, as we will see later,
but for simpler models it works):
getVarCov(gls.cs, type="marginal")

## Marginal variance covariance matrix


## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 34.110 15.374 15.374 15.374 15.374 15.374
## [2,] 15.374 34.110 15.374 15.374 15.374 15.374
## [3,] 15.374 15.374 34.110 15.374 15.374 15.374
## [4,] 15.374 15.374 15.374 34.110 15.374 15.374
## [5,] 15.374 15.374 15.374 15.374 34.110 15.374
## [6,] 15.374 15.374 15.374 15.374 15.374 34.110
## Standard Deviations: 5.8404 5.8404 5.8404 5.8404 5.8404 5.8404
We see that the variance of the HDRS residuals across the 6 time points is assumed to be equal (the diagonal

27
of the matrix) and that the covariances are assumed constant. That, together with the constant variances,
implies constant correlation between all HDRS measurements, no matter how far apart. To see this better,
we can get the estimated correlation matrix instead of var/cov matrix. We ask for the first element of the
correlation structure (for the first patient; since the correlation structure is assumed the same for all patients,
this is the correlation structure for everyone).
corMatrix(gls.cs$modelStruct$corStruct)[[1]]

## [,1] [,2] [,3] [,4] [,5] [,6]


## [1,] 1.000000 0.450704 0.450704 0.450704 0.450704 0.450704
## [2,] 0.450704 1.000000 0.450704 0.450704 0.450704 0.450704
## [3,] 0.450704 0.450704 1.000000 0.450704 0.450704 0.450704
## [4,] 0.450704 0.450704 0.450704 1.000000 0.450704 0.450704
## [5,] 0.450704 0.450704 0.450704 0.450704 1.000000 0.450704
## [6,] 0.450704 0.450704 0.450704 0.450704 0.450704 1.000000
For purposes of comparison, let’s now estimate a “true” mixed model with fixed effects for week (categorical),
endo, and their interaction, and with only a random intercept per subject.
lme.ric<-lme(fixed=hdrs ~ week*endo, random=~1|id, data=reisby.long, na.action="na.omit", method="ML")
## summary(lme.ric)

## Value Std.Error DF t-value p-value


## (Intercept) 22.6608707 1.114797 299 20.32734905 2.689492e-58
## week1 -2.1781121 1.167293 299 -1.86595229 6.302704e-02
## week2 -5.6631769 1.179220 299 -4.80247788 2.481658e-06
## week3 -7.3160431 1.167293 299 -6.26753204 1.278130e-09
## week4 -10.0401811 1.167293 299 -8.60125553 4.518042e-16
## week5 -11.3684527 1.191900 299 -9.53809123 5.345566e-19
## endo 1.1911784 1.506269 64 0.79081397 4.319734e-01
## week1:endo 1.3682638 1.593345 299 0.85873680 3.911736e-01
## week2:endo 1.1084251 1.584465 299 0.69955803 4.847472e-01
## week3:endo 0.8296468 1.581150 299 0.52471108 6.001729e-01
## week4:endo 0.7197389 1.593345 299 0.45171574 6.518013e-01
## week5:endo 0.1202209 1.631796 299 0.07367399 9.413191e-01
getVarCov(lme.ric,type="marginal")

## id 101
## Marginal variance covariance matrix
## 1 2 3 4 5 6
## 1 34.110 15.374 15.374 15.374 15.374 15.374
## 2 15.374 34.110 15.374 15.374 15.374 15.374
## 3 15.374 15.374 34.110 15.374 15.374 15.374
## 4 15.374 15.374 15.374 34.110 15.374 15.374
## 5 15.374 15.374 15.374 15.374 34.110 15.374
## 6 15.374 15.374 15.374 15.374 15.374 34.110
## Standard Deviations: 5.8404 5.8404 5.8404 5.8404 5.8404 5.8404
Compare this output to the CPM with Compound Symmetry (both the coefficients of the covariates and the
variance-covariance matrix) above; they should be identical.
Returning to the CPMs, we’ll fit a couple more models from the lecture. We fit the model with fixed effects
for week, endo & interaction, without random effects, and with a (homogeneous) AR(1) residual correlation
matrix.
gls.AR1 <- gls(hdrs ~ week*endo, correlation=corAR1(form = ~ 1 | id),
data=reisby.long, na.action="na.omit", method="ML")

28
#summary(gls.AR1)
coef(summary(gls.AR1))

## Value Std.Error t-value p-value


## (Intercept) 22.8038592 1.1279409 20.21724630 2.023091e-61
## week1 -2.1691903 0.9358833 -2.31779995 2.101557e-02
## week2 -5.8584241 1.2048980 -4.86217428 1.732066e-06
## week3 -7.5929868 1.3364271 -5.68155703 2.741541e-08
## week4 -10.2714714 1.4273383 -7.19624160 3.577000e-12
## week5 -11.5236656 1.5015852 -7.67433346 1.550904e-13
## endo 1.1432720 1.5192480 0.75252490 4.522232e-01
## week1:endo 1.4896019 1.2775011 1.16602790 2.443687e-01
## week2:endo 1.3536342 1.6004729 0.84577141 3.982374e-01
## week3:endo 1.1013535 1.7926705 0.61436470 5.393592e-01
## week4:endo 0.6674448 1.9201261 0.34760470 7.283385e-01
## week5:endo 0.1353503 2.0218298 0.06694446 9.466628e-01
getVarCov(gls.AR1, type="marginal")

## Marginal variance covariance matrix


## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 35.0510 23.1050 15.231 10.040 6.6183 4.3627
## [2,] 23.1050 35.0510 23.105 15.231 10.0400 6.6183
## [3,] 15.2310 23.1050 35.051 23.105 15.2310 10.0400
## [4,] 10.0400 15.2310 23.105 35.051 23.1050 15.2310
## [5,] 6.6183 10.0400 15.231 23.105 35.0510 23.1050
## [6,] 4.3627 6.6183 10.040 15.231 23.1050 35.0510
## Standard Deviations: 5.9204 5.9204 5.9204 5.9204 5.9204 5.9204
corMatrix(gls.AR1$modelStruct$corStruct)[[1]]

## [,1] [,2] [,3] [,4] [,5] [,6]


## [1,] 1.0000000 0.6591894 0.4345307 0.2864380 0.1888169 0.1244661
## [2,] 0.6591894 1.0000000 0.6591894 0.4345307 0.2864380 0.1888169
## [3,] 0.4345307 0.6591894 1.0000000 0.6591894 0.4345307 0.2864380
## [4,] 0.2864380 0.4345307 0.6591894 1.0000000 0.6591894 0.4345307
## [5,] 0.1888169 0.2864380 0.4345307 0.6591894 1.0000000 0.6591894
## [6,] 0.1244661 0.1888169 0.2864380 0.4345307 0.6591894 1.0000000
The correlations between measurements is estimated to be larger for two HDRS residuals close together
(weeks 1 and 2 or 2 and 3), and are lower when the HDRS scores are further apart. The variances (or SDs) of
the HDRS scores are assumed to be the same at all six weeks.
Now we fit the model with fixed effects for week, endo & interaction, no random effects, and with a
heterogeneous AR(1) residual correlation matrix. Note that type="marginal" does not work here; we now
have to specify an individual (I picked the first ID, 101) for the var-cov matrix.
gls.hAR1 <- gls(hdrs ~ week*endo, correlation=corAR1(form = ~ 1 | id),
weight = varIdent(form = ~ 1 | week), data=reisby.long,
na.action="na.omit", method="ML")
#summary(gls.hAR1)
coef(summary(gls.hAR1))

## Value Std.Error t-value p-value


## (Intercept) 22.7927207 0.9091115 25.07142445 3.234773e-81
## week1 -2.1736733 0.8109396 -2.68043782 7.687816e-03
## week2 -5.8360065 1.0694537 -5.45699787 8.985142e-08

29
## week3 -7.5844192 1.2221718 -6.20568967 1.486689e-09
## week4 -10.2654556 1.3551546 -7.57511770 3.009280e-13
## week5 -11.5092267 1.5737725 -7.31314514 1.682546e-12
## endo 1.1521121 1.2250671 0.94044820 3.476131e-01
## week1:endo 1.4733749 1.1070585 1.33089164 1.840600e-01
## week2:endo 1.3019162 1.4186215 0.91773327 3.593679e-01
## week3:endo 1.0795628 1.6383613 0.65892843 5.103595e-01
## week4:endo 0.6605790 1.8240871 0.36214225 7.174565e-01
## week5:endo 0.1061191 2.1223738 0.05000021 9.601497e-01
getVarCov(gls.hAR1, individual = "101")

## Marginal variance covariance matrix


## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 22.7480 15.8000 10.634 7.2293 4.9470 3.6599
## [2,] 15.8000 26.8450 18.067 12.2830 8.4055 6.2185
## [3,] 10.6340 18.0670 29.747 20.2240 13.8390 10.2380
## [4,] 7.2293 12.2830 20.224 33.6350 23.0160 17.0280
## [5,] 4.9470 8.4055 13.839 23.0160 38.5290 28.5050
## [6,] 3.6599 6.2185 10.238 17.0280 28.5050 51.5880
## Standard Deviations: 4.7695 5.1812 5.454 5.7996 6.2072 7.1825
corMatrix(gls.hAR1$modelStruct$corStruct)[[1]]

## [,1] [,2] [,3] [,4] [,5] [,6]


## [1,] 1.0000000 0.6393576 0.4087782 0.2613555 0.1670996 0.1068364
## [2,] 0.6393576 1.0000000 0.6393576 0.4087782 0.2613555 0.1670996
## [3,] 0.4087782 0.6393576 1.0000000 0.6393576 0.4087782 0.2613555
## [4,] 0.2613555 0.4087782 0.6393576 1.0000000 0.6393576 0.4087782
## [5,] 0.1670996 0.2613555 0.4087782 0.6393576 1.0000000 0.6393576
## [6,] 0.1068364 0.1670996 0.2613555 0.4087782 0.6393576 1.0000000
Again, the correlations are assumed to decrease as the distance between HRDS measurements increases, but
now the SDs are no longer assumed equal, and are generally estimated to be increasing over time. (We can
see this in the output of the summary of the model. Note that we can’t get the var-cov structure for some
reason. This happens when we use the weight option.)
Finally, we fit the model with fixed effects for week, endo & interaction, no random effects, and with an
unstructured residual correlation matrix:
gls.unstruct <- gls(hdrs ~ week*endo, correlation=corSymm(form = ~1 | id),
weights=varIdent(form=~1|week), data=reisby.long,
na.action="na.omit", method="ML")
#summary(gls.unstruct)
coef(summary(gls.unstruct))

## Value Std.Error t-value p-value


## (Intercept) 22.7059524 0.8463342 26.82858989 3.666524e-88
## week1 -2.1837419 0.8482156 -2.57451272 1.043372e-02
## week2 -5.8515915 1.0934463 -5.35151237 1.548865e-07
## week3 -7.4860388 1.2278080 -6.09707619 2.764235e-09
## week4 -10.2656793 1.3753151 -7.46423826 6.267510e-13
## week5 -11.5531300 1.6062144 -7.19276949 3.657546e-12
## endo 1.2555608 1.1422581 1.09919184 2.724129e-01
## week1:endo 1.4427657 1.1600390 1.24372171 2.144046e-01
## week2:endo 1.1556551 1.4510734 0.79641392 4.263120e-01
## week3:endo 1.0207451 1.6455898 0.62029133 5.354552e-01

30
## week4:endo 0.7803934 1.8396826 0.42420002 6.716712e-01
## week5:endo 0.1690619 2.1443989 0.07883883 9.372043e-01
getVarCov(gls.unstruct, individual = "101")

## Marginal variance covariance matrix


## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 19.6330 10.3980 6.3449 8.086 6.7260 4.5004
## [2,] 10.3980 20.8660 10.2570 10.692 8.3337 5.0218
## [3,] 6.3449 10.2570 25.9020 22.360 23.9850 20.8840
## [4,] 8.0860 10.6920 22.3600 38.439 32.0320 29.9000
## [5,] 6.7260 8.3337 23.9850 32.032 46.8950 38.7660
## [6,] 4.5004 5.0218 20.8840 29.900 38.7660 60.1860
## Standard Deviations: 4.4309 4.5679 5.0894 6.1999 6.848 7.758
corMatrix(gls.unstruct$modelStruct$corStruct)[[1]]

## [,1] [,2] [,3] [,4] [,5] [,6]


## [1,] 1.0000000 0.5137555 0.2813597 0.2943450 0.2216693 0.1309237
## [2,] 0.5137555 1.0000000 0.4412211 0.3775228 0.2664150 0.1417084
## [3,] 0.2813597 0.4412211 1.0000000 0.7086363 0.6881981 0.5289240
## [4,] 0.2943450 0.3775228 0.7086363 1.0000000 0.7544554 0.6216486
## [5,] 0.2216693 0.2664150 0.6881981 0.7544554 1.0000000 0.7296908
## [6,] 0.1309237 0.1417084 0.5289240 0.6216486 0.7296908 1.0000000
Which model would you choose on the basis of the AIC? Explain your answer.
Be sure to save your script for later use!

Exercise 2
a. Now we’ll switch to modeling time as continuous (and linear) and examine a few linear mixed effects
models. The models include fixed effects for time, endo, and their interaction.
lme.ril<-lme(fixed=hdrs ~ time*endo, random=~1|id, data=reisby.long,
na.action="na.omit", method="ML")
lme.ris<-lme(fixed=hdrs ~ time*endo, random=~time|id, data=reisby.long,
na.action="na.omit", method="ML")

b. To get the variance-covariance matrices for these two models:


getVarCov(lme.ril,type="marginal")

## id 101
## Marginal variance covariance matrix
## 1 2 3 4 5 6
## 1 34.321 15.287 15.287 15.287 15.287 15.287
## 2 15.287 34.321 15.287 15.287 15.287 15.287
## 3 15.287 15.287 34.321 15.287 15.287 15.287
## 4 15.287 15.287 15.287 34.321 15.287 15.287
## 5 15.287 15.287 15.287 15.287 34.321 15.287
## 6 15.287 15.287 15.287 15.287 15.287 34.321
## Standard Deviations: 5.8584 5.8584 5.8584 5.8584 5.8584 5.8584
getVarCov(lme.ris,type="marginal")

## id 101
## Marginal variance covariance matrix
## 1 2 3 4 5 6

31
## 1 23.8600 10.240 8.838 7.4364 6.0349 4.6334
## 2 10.2400 23.134 11.591 12.2660 12.9420 13.6170
## 3 8.8380 11.591 26.562 17.0960 19.8480 22.6010
## 4 7.4364 12.266 17.096 34.1440 26.7550 31.5840
## 5 6.0349 12.942 19.848 26.7550 45.8800 40.5680
## 6 4.6334 13.617 22.601 31.5840 40.5680 61.7700
## Standard Deviations: 4.8846 4.8097 5.1538 5.8433 6.7735 7.8594
c. Fit a model with random intercepts and slopes, and a cAR(1), get the variance-covariance matrix, and
compare the three models using the LRT:
lme.ris.CAR1<-lme(fixed=hdrs ~ time*endo, random=~time|id,
correlation = corCAR1(form = ~ time | id), data=reisby.long,
na.action="na.omit", method="ML")
getVarCov(lme.ris.CAR1,type="marginal")

## id 101
## Marginal variance covariance matrix
## 1 2 3 4 5 6
## 1 21.8690 10.916 7.0602 5.8415 5.6024 5.7272
## 2 10.9160 23.825 14.1470 11.5670 11.6230 12.6600
## 3 7.0602 14.147 28.3310 19.9280 18.6240 19.9560
## 4 5.8415 11.567 19.9280 35.3880 28.2610 28.2320
## 5 5.6024 11.623 18.6240 28.2610 44.9970 39.1450
## 6 5.7272 12.660 19.9560 28.2320 39.1450 57.1560
## Standard Deviations: 4.6765 4.8811 5.3227 5.9488 6.708 7.5602
anova(lme.ris.CAR1, lme.ris, lme.ril)

## Model df AIC BIC logLik Test L.Ratio p-value


## lme.ris.CAR1 1 9 2224.838 2260.180 -1103.419
## lme.ris 2 8 2230.929 2262.345 -1107.465 1 vs 2 8.09133 0.0044
## lme.ril 3 6 2294.137 2317.699 -1141.069 2 vs 3 67.20798 <.0001
The first LRT compares the model with a random intercept, random slope for time, and continuous AR(1)
to the model with a random intercept and random slope, and is statistically significant (p=0.0044), so the
model with residual AR(1) correlation fits better than the one without. Not surprisingly, the model with a
random intercept and random slope is also significantly better than the model with just a random intercept
per person. We come to the same conclusion if we use the AIC for model comparisons: AIC fullest model <
AIC RI+RS < AIC RI only.
d. We’ll check to see if the interaction and the main effects are necessary in the last model from part
c. since it’s a bit different from our final model yesterday.
drop1(lme.ris.CAR1, test="Chisq")

## Single term deletions


##
## Model:
## hdrs ~ time * endo
## Df AIC LRT Pr(>Chi)
## <none> 2224.8
## time:endo 1 2222.8 0.0015988 0.9681
The results indicate that we can drop the interaction. Now let’s see if one of the main effects can be dropped:
lme.ris.CAR1.a<-lme(fixed=hdrs ~ time + endo, random=~time|id, correlation = corCAR1(form = ~ time | id)
drop1(lme.ris.CAR1.a, test="Chisq")

32
## Single term deletions
##
## Model:
## hdrs ~ time + endo
## Df AIC LRT Pr(>Chi)
## <none> 2222.8
## time 1 2292.8 71.942 < 2e-16 ***
## endo 1 2224.6 3.720 0.05375 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
lme.ris.CAR1.b<-lme(fixed=hdrs ~ time, random=~time|id,
correlation = corCAR1(form = ~ time | id), data=reisby.long,
na.action="na.omit", method="ML")
drop1(lme.ris.CAR1.b, test="Chisq")

## Single term deletions


##
## Model:
## hdrs ~ time
## Df AIC LRT Pr(>Chi)
## <none> 2224.6
## time 1 2294.3 71.777 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Just as with the model yesterday, the group variable can be dropped, the the time variable cannot. Finally,
update the final model with REML and get a summary of the model:
reisby.fin <- update(lme.ris.CAR1.b, method="REML")
summary(reisby.fin)

## Linear mixed-effects model fit by REML


## Data: reisby.long
## AIC BIC logLik
## 2225.516 2252.967 -1105.758
##
## Random effects:
## Formula: ~time | id
## Structure: General positive-definite, Log-Cholesky parametrization
## StdDev Corr
## (Intercept) 2.114281 (Intr)
## time 1.127777 0.164
## Residual 4.295608
##
## Correlation Structure: Continuous AR(1)
## Formula: ~time | id
## Parameter estimate(s):
## Phi
## 0.3879062
## Fixed effects: hdrs ~ time
## Value Std.Error DF t-value p-value
## (Intercept) 23.492368 0.5434972 308 43.22445 0
## time -2.331563 0.2044898 308 -11.40185 0
## Correlation:
## (Intr)

33
## time -0.446
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -2.34836738 -0.58398498 -0.01365474 0.55673652 3.56315574
##
## Number of Observations: 375
## Number of Groups: 66
Again, be sure to save your script for later use!

Exercise 3
Re-run (if necessary) the final model (random intercepts, slopes for time, and cAR(1) for time and REML
estimation). Then take a look at the Q-Q plot of the standardized residuals, and plot the standardized
residuals vs. fitted values:
reisby.fin <- update(lme.ris.CAR1.b, method="REML")
qqnorm(reisby.fin)

3
Quantiles of standard normal

−1

−2

−3

−2 −1 0 1 2 3

Standardized residuals

plot(reisby.fin)

34
3
Standardized residuals

−1

−2

5 10 15 20 25

Fitted values

What do you see in these graphs?


We can also take a look at a dotplot of the level 1 residuals per school:
plot(reisby.fin, id ~ resid(.))

35
610
609
608
607
606
604
603
507
505
504
502
501
361
360
357
355
354
353
352
351
350
349
348
347
346
345
344
339
338
337
335
334
333
id

331
328
327
322
319
318
316
315
313
312
311
310
309
308
305
304
303
302
123
121
120
118
117
115
114
113
108
107
106
105
104
103
101
−10 −5 0 5 10 15

Residuals

What do you see?


We can check poor fit of the model per individual by plotting the observed versus fitted values per person.
plot(reisby.fin, hdrs ~ fitted(.) | id, abline = c(0,1))

36
5 15 25 5 15 25 5 15 25

604 606 607 608 609 610


40
20
0
355 357 360 361 501 502 504 505 507 603
40
20
0
345 346 347 348 349 350 351 352 353 354
40
20
0
327 328 331 333 334 335 337 338 339 344
40
hdrs

20
0
309 310 311 312 313 315 316 318 319 322
40
20
0
117 118 120 121 123 302 303 304 305 308
40
20
0
101 103 104 105 106 107 108 113 114 115
40
20
0
5 15 25 5 15 25 5 15 25 5 15 25 5 15 25

Fitted values

Most of the individuals seem to more or less follow the fitted lines, though it might still be wise to add the
quadratic terms to the random effects as we did yesterday! This would likely improve the overall fit.
And for the dotplots of the random effects:
plot(ranef(reisby.fin))

37
(Intercept) time
610
609
608
607
606
604
603
507
505
504
502
501
361
360
357
355
354
353
352
351
350
349
348
347
346
345
344
339
338
337
335
334
333
id

331
328
327
322
319
318
316
315
313
312
311
310
309
308
305
304
303
302
123
121
120
118
117
115
114
113
108
107
106
105
104
103
101
−2 −1 0 1 2 3 −1 0 1 2

Random effects

In this plot we see that a few individuals have larger random slopes than the rest and a couple of individuals
have larger-than-usual intercepts. Otherwise, there are no obvious outliers.

38

You might also like