It's a trade-off...
Accurately representing the world
everything that can vary is modelled as varying
Being able to fit the model
in our sample, some things will not vary enough to fit x|g
g
+g
instead of (1|g)
mm|g
vs km|g
y~x
between groups"maximal model" = the most complex random effect structure that you can fit to the data
"maximal model" = the most complex random effect structure that you can fit to the data
"maximal model" = the most complex random effect structure that you can fit to the data
Simple(ish) design:
20 participants, each with approx 10 observations
d3 <- read_csv("https://uoepsy.github.io/data/dapr3_mindfuldecline.csv")maxmodel <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3)
isSingular(maxmodel)
## [1] FALSE
Complicated design:
14 Study sites, each with c18 ppts, each with approx 10 observations
d3full <- read_csv("https://uoepsy.github.io/data/dapr3_mindfuldeclineFULL.csv")maxmodelfull <- lmer(ACE ~ visit * condition + (1 + visit * condition | sitename) + (1 + visit | sitename:ppt), data = d3full)
# boundary (singular) fit: see help('isSingular')
isSingular(maxmodelfull)
## [1] TRUE
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
summary(allFit(model))
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
summary(allFit(model))
This probably won't help. In most cases, it is just that our model is too complex to fit to our available data.
Keep it maximal
Selection based
Use a criterion for model selection (e.g. LRT, AIC, BIC) to choose a random effect structure that is supported by the data (see Matsuchek et al., 2017)
Keep it maximal
Selection based
Use a criterion for model selection (e.g. LRT, AIC, BIC) to choose a random effect structure that is supported by the data (see Matsuchek et al., 2017)
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
Look for:
Variances/standard deviations of 0 (or very small, e.g. 3.56e-05
)
Correlations of -1 or 1
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
Consider removing most complex random effects first.
(1 + x1 * x2 | g)
↓
(1 + x1 + x2 | g)
↓
(1 + x1 | g)
OR (1 + x2 | g)
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
Categorical predictors with >2 levels are "more complex" (because they require more parameters).
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
If multiple levels of nesting, you'll have fewer groups as you go up the levels (fewer groups to be able to fit things to).
sometimes (but not always) it makes sense to remove higher level random effects first.
(1 + x1 + x2 | g1/g2)
is the same as
(1 + x1 + x2 | g1) + (1 + x1 + x2 | g1:g2)
↓
(1 + x1 | g1) + (1 + x1 + x2 | g1:g2)
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
You will be faced with subjective choices
which simplification can you most easily accept?
i've kind of been ignoring these so far..
m1 <- lmer(y ~ 1 + x1 + (1 + x1 | clusterid), data = df)VarCorr(m1)
## Groups Name Std.Dev. Corr## clusterid (Intercept) 0.759 ## x1 0.117 1.00## Residual 0.428
maxmodel <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3)VarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
plot the predictions:
plot(ranef(maxmodel)$ppt)
maxmodelZC <- lmer(ACE ~ visit * condition + (1 + visit || ppt), data = d3)VarCorr(maxmodelZC)
## Groups Name Std.Dev.## ppt (Intercept) 0.343 ## ppt.1 visit 0.263 ## Residual 0.498
removing correlations between random effects simplifies your model.
plot the predictions:
plot(ranef(maxmodelZC)$ppt)
cor(ranef(maxmodelZC)$ppt)
## (Intercept) visit## (Intercept) 1.0000 -0.2384## visit -0.2384 1.0000
Different data! (here i have simulated the data so that people do have a very strong correlation in their intercepts and slopes)
maxmodel_COR <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3COR)VarCorr(maxmodel_COR)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.206 ## visit 0.364 -1.00## Residual 0.105
maxmodel_CORsuppress <- lmer(ACE ~ visit * condition + (1 + visit || ppt), data = d3COR)VarCorr(maxmodel_CORsuppress)
## Groups Name Std.Dev.## ppt (Intercept) 0.199 ## ppt.1 visit 0.361 ## Residual 0.107
ggplot(d3COR, aes(x=visit, y=ACE))+ geom_line(aes(group=ppt, col=condition), alpha=.7)+ theme(legend.position = "bottom")
maxmodel_COR <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3COR)VarCorr(maxmodel_COR)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.206 ## visit 0.364 -1.00## Residual 0.105
maxmodel_CORsuppress <- lmer(ACE ~ visit * condition + (1 + visit || ppt), data = d3COR)VarCorr(maxmodel_CORsuppress)
## Groups Name Std.Dev.## ppt (Intercept) 0.199 ## ppt.1 visit 0.361 ## Residual 0.107
plot(ranef(maxmodel_COR)$ppt)plot(ranef(maxmodel_CORsuppress)$ppt)
cor(ranef(maxmodel_CORsuppress)$ppt)
## (Intercept) visit## (Intercept) 1.0000 -0.9678## visit -0.9678 1.0000
random effect structures can get complicated quite quickly
there may multiple ways in things can be clustered.
the maximal random effect structure is the most complex structure we can fit to the data.
building lmer
s is a balancing act between accounting for different sources of variation and attempting to fitting a model that can be supported by our data.
It's a trade-off...
Accurately representing the world
everything that can vary is modelled as varying
Being able to fit the model
in our sample, some things will not vary enough to fit x|g
g
+g
instead of (1|g)
mm|g
vs km|g
y~x
between groupsKeyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
o | Tile View: Overview of Slides |
Esc | Back to slideshow |
It's a trade-off...
Accurately representing the world
everything that can vary is modelled as varying
Being able to fit the model
in our sample, some things will not vary enough to fit x|g
g
+g
instead of (1|g)
mm|g
vs km|g
y~x
between groups"maximal model" = the most complex random effect structure that you can fit to the data
"maximal model" = the most complex random effect structure that you can fit to the data
"maximal model" = the most complex random effect structure that you can fit to the data
Simple(ish) design:
20 participants, each with approx 10 observations
d3 <- read_csv("https://uoepsy.github.io/data/dapr3_mindfuldecline.csv")maxmodel <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3)
isSingular(maxmodel)
## [1] FALSE
Complicated design:
14 Study sites, each with c18 ppts, each with approx 10 observations
d3full <- read_csv("https://uoepsy.github.io/data/dapr3_mindfuldeclineFULL.csv")maxmodelfull <- lmer(ACE ~ visit * condition + (1 + visit * condition | sitename) + (1 + visit | sitename:ppt), data = d3full)
# boundary (singular) fit: see help('isSingular')
isSingular(maxmodelfull)
## [1] TRUE
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
summary(allFit(model))
Don't report results from a model that doesn't converge. You will probably not be able to trust the estimates.
summary(allFit(model))
This probably won't help. In most cases, it is just that our model is too complex to fit to our available data.
Keep it maximal
Selection based
Use a criterion for model selection (e.g. LRT, AIC, BIC) to choose a random effect structure that is supported by the data (see Matsuchek et al., 2017)
Keep it maximal
Selection based
Use a criterion for model selection (e.g. LRT, AIC, BIC) to choose a random effect structure that is supported by the data (see Matsuchek et al., 2017)
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
Look for:
Variances/standard deviations of 0 (or very small, e.g. 3.56e-05
)
Correlations of -1 or 1
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
Consider removing most complex random effects first.
(1 + x1 * x2 | g)
↓
(1 + x1 + x2 | g)
↓
(1 + x1 | g)
OR (1 + x2 | g)
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
Categorical predictors with >2 levels are "more complex" (because they require more parameters).
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
If multiple levels of nesting, you'll have fewer groups as you go up the levels (fewer groups to be able to fit things to).
sometimes (but not always) it makes sense to remove higher level random effects first.
(1 + x1 + x2 | g1/g2)
is the same as
(1 + x1 + x2 | g1) + (1 + x1 + x2 | g1:g2)
↓
(1 + x1 | g1) + (1 + x1 + x2 | g1:g2)
One thing at a time!
# to extract random effectsVarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
You will be faced with subjective choices
which simplification can you most easily accept?
i've kind of been ignoring these so far..
m1 <- lmer(y ~ 1 + x1 + (1 + x1 | clusterid), data = df)VarCorr(m1)
## Groups Name Std.Dev. Corr## clusterid (Intercept) 0.759 ## x1 0.117 1.00## Residual 0.428
maxmodel <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3)VarCorr(maxmodel)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.383 ## visit 0.277 -0.49## Residual 0.495
plot the predictions:
plot(ranef(maxmodel)$ppt)
maxmodelZC <- lmer(ACE ~ visit * condition + (1 + visit || ppt), data = d3)VarCorr(maxmodelZC)
## Groups Name Std.Dev.## ppt (Intercept) 0.343 ## ppt.1 visit 0.263 ## Residual 0.498
removing correlations between random effects simplifies your model.
plot the predictions:
plot(ranef(maxmodelZC)$ppt)
cor(ranef(maxmodelZC)$ppt)
## (Intercept) visit## (Intercept) 1.0000 -0.2384## visit -0.2384 1.0000
Different data! (here i have simulated the data so that people do have a very strong correlation in their intercepts and slopes)
maxmodel_COR <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3COR)VarCorr(maxmodel_COR)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.206 ## visit 0.364 -1.00## Residual 0.105
maxmodel_CORsuppress <- lmer(ACE ~ visit * condition + (1 + visit || ppt), data = d3COR)VarCorr(maxmodel_CORsuppress)
## Groups Name Std.Dev.## ppt (Intercept) 0.199 ## ppt.1 visit 0.361 ## Residual 0.107
ggplot(d3COR, aes(x=visit, y=ACE))+ geom_line(aes(group=ppt, col=condition), alpha=.7)+ theme(legend.position = "bottom")
maxmodel_COR <- lmer(ACE ~ visit * condition + (1 + visit | ppt), data = d3COR)VarCorr(maxmodel_COR)
## Groups Name Std.Dev. Corr ## ppt (Intercept) 0.206 ## visit 0.364 -1.00## Residual 0.105
maxmodel_CORsuppress <- lmer(ACE ~ visit * condition + (1 + visit || ppt), data = d3COR)VarCorr(maxmodel_CORsuppress)
## Groups Name Std.Dev.## ppt (Intercept) 0.199 ## ppt.1 visit 0.361 ## Residual 0.107
plot(ranef(maxmodel_COR)$ppt)plot(ranef(maxmodel_CORsuppress)$ppt)
cor(ranef(maxmodel_CORsuppress)$ppt)
## (Intercept) visit## (Intercept) 1.0000 -0.9678## visit -0.9678 1.0000
random effect structures can get complicated quite quickly
there may multiple ways in things can be clustered.
the maximal random effect structure is the most complex structure we can fit to the data.
building lmer
s is a balancing act between accounting for different sources of variation and attempting to fitting a model that can be supported by our data.