In this session, we will look at another data set in a language acquisition paper published in Cognition in (2013), namely Pine et al. (2013). It, too, is not really ideal for the application of mixed-effects modeling, but again nicely small (so they are easy to look at comprehensively and fast to run) and also didactically interesting. Still, bear in mind that they are not the best examples and that the analysis we will proceed with here is quite some overkill, given the size and complexity of this data set.
CASE OVERLAP NAME SAMPLE USED
Min. : 1.00 Min. :0.0500 Anne : 6 Min. :100.0 no :36
1st Qu.:18.75 1st Qu.:0.1000 Aran : 6 1st Qu.:100.0 yes:36
Median :36.50 Median :0.1900 Becky : 6 Median :200.0
Mean :36.50 Mean :0.2614 Carl : 6 Mean :266.7
3rd Qu.:54.25 3rd Qu.:0.3475 Dominic: 6 3rd Qu.:500.0
Max. :72.00 Max. :1.0000 Gail : 6 Max. :500.0
(Other):36
They did this case study because they expected that the amount of overlap would be positively correlated with the sample size.
1.1 Exploration & preparation
As always, it is indispensable to do a decent amount of exploratory plotting before any statistical analysis is begun. If you do mixed-effects modeling, you really must look at
every variable in isolation (the response, predictors, controls, random effects, everything);
every rhs variable (fixed and random effects) with the response;
every combination of rhs variables that will be considered in the model on their own and with the response.
1.1.1 The response
Since we want to stay as close to their data as possible, we follow them in applying the arcsine transformation to the response variable:
CASE NAME OVERLAP.asin SAMPLE USED
Min. : 1.00 Anne : 6 Min. :0.05002 Min. :100.0 no :36
1st Qu.:18.75 Aran : 6 1st Qu.:0.10017 1st Qu.:100.0 yes:36
Median :36.50 Becky : 6 Median :0.19116 Median :200.0
Mean :36.50 Carl : 6 Mean :0.28496 Mean :266.7
3rd Qu.:54.25 Dominic: 6 3rd Qu.:0.35494 3rd Qu.:500.0
Max. :72.00 Gail : 6 Max. :1.57080 Max. :500.0
(Other):36
OVERLAP
Min. :0.0500
1st Qu.:0.1000
Median :0.1900
Mean :0.2614
3rd Qu.:0.3475
Max. :1.0000
What do the response and its transformed version look like?
Not really clear to me that this transformation does so much good here …
1.1.2 The fixed-effects structure
Here’s the first predictor, SAMPLE, which we convert to a factor first because that’s what Pine et al. did; all levels are equally frequent, perfectly balanced:
d$SAMPLE <-factor(d$SAMPLE)table(d$SAMPLE) # frequencies of levels
100 200 500
24 24 24
tapply(d$OVERLAP.asin, d$SAMPLE, mean) # means of response per level
Every kid is recorded in each condition of USED (& even equally often), which means we need varying slopes of USED|NAME (and maybe the interaction w/ SAMPLE):
table(d$NAME, d$USED)
no yes
Anne 3 3
Aran 3 3
Becky 3 3
Carl 3 3
Dominic 3 3
Gail 3 3
Joel 3 3
John 3 3
Liz 3 3
Nicole 3 3
Ruth 3 3
Warren 3 3
Every kid is recorded once in each combo of SAMPLE & USED (!)
ftable(d$NAME, d$USED, d$SAMPLE)
100 200 500
Anne no 1 1 1
yes 1 1 1
Aran no 1 1 1
yes 1 1 1
Becky no 1 1 1
yes 1 1 1
Carl no 1 1 1
yes 1 1 1
Dominic no 1 1 1
yes 1 1 1
Gail no 1 1 1
yes 1 1 1
Joel no 1 1 1
yes 1 1 1
John no 1 1 1
yes 1 1 1
Liz no 1 1 1
yes 1 1 1
Nicole no 1 1 1
yes 1 1 1
Ruth no 1 1 1
yes 1 1 1
Warren no 1 1 1
yes 1 1 1
1.2 Wrong & traditional analyses
One might be tempted to fit this model, but …
summary(lm(OVERLAP ~ NAME*SAMPLE*USED, data=d))
that would be wrong, because …? Because
NAME is not a fixed effect but a random effect;
this model doesn’t account for the repeated measurements;
this model cannot be fit because the highest-level interaction has only 1 case per combo.
Here’s what Pine et al. did, a repeated-measures ANOVA with the 2 predictors SAMPLE and USED nested into NAME:
So we eliminate the interaction in the ranef structure, but first we do something else that is potentially useful: we set successive-difference contrasts for SAMPLE:
Linear mixed model fit by REML ['lmerMod']
Formula: OVERLAP.asin ~ 1 + SAMPLE * USED + (1 + SAMPLE + USED | NAME)
Data: d
REML criterion at convergence: -158.9
Scaled residuals:
Min 1Q Median 3Q Max
-3.4748 -0.3831 -0.0187 0.3337 2.9520
Random effects:
Groups Name Variance Std.Dev. Corr
NAME (Intercept) 5.961e-05 0.007721
SAMPLE200-100 5.407e-03 0.073530 1.00
SAMPLE500-200 3.087e-04 0.017569 -1.00 -1.00
USEDyes 8.482e-02 0.291235 1.00 1.00 -1.00
Residual 1.821e-03 0.042667
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.117109 0.007452 15.714
SAMPLE200-100 0.037645 0.027459 1.371
SAMPLE500-200 0.080888 0.018142 4.459
USEDyes 0.335704 0.084672 3.965
SAMPLE200-100:USEDyes 0.110288 0.024634 4.477
SAMPLE500-200:USEDyes 0.065122 0.024634 2.644
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
Of course we get a singularity warning … We explore the ranef structure to see what we might end up with:
rePCA(m_01b)$NAME$sdev %>%round(4)
[1] 7.0542 0.0005 0.0001 0.0000
The output suggests we will end up with 1 source of ranef variation. We have two kinds of random slopes we might eliminate and we cannot see from the ranef output which one comes with the smaller variance/sd. Thus, we try eliminating both (separately). First, we drop the slopes for SAMPLE:
summary(m_02a <-update( m_01b, .~.- (1+ SAMPLE+USED|NAME) # random effects dropped+ (1+ USED|NAME)), # random effects inserted: no slopes for SAMPLEcorrelation=FALSE)
Linear mixed model fit by REML ['lmerMod']
Formula: OVERLAP.asin ~ SAMPLE + USED + (1 + USED | NAME) + SAMPLE:USED
Data: d
REML criterion at convergence: -131.6
Scaled residuals:
Min 1Q Median 3Q Max
-4.9561 -0.3226 -0.0054 0.2551 4.0874
Random effects:
Groups Name Variance Std.Dev. Corr
NAME (Intercept) 6.006e-05 0.00775
USEDyes 8.442e-02 0.29056 1.00
Residual 3.003e-03 0.05480
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.117109 0.009403 12.454
SAMPLE200-100 0.037645 0.022371 1.683
SAMPLE500-200 0.080888 0.022371 3.616
USEDyes 0.335704 0.084865 3.956
SAMPLE200-100:USEDyes 0.110288 0.031638 3.486
SAMPLE500-200:USEDyes 0.065122 0.031638 2.058
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
anova(m_01b, m_02a, test="Chisq", refit=FALSE) # note the refit=FALSE, we stick with REML
We still get a singularity warning and the LR-test says we need subject-specific slopes for USED. Just like last time,
the model summaries say you cannot fit the models because of singularity warnings;
LR-tests say you cannot simplify the models’ ranef structure because the slopes are all significant.
So what do we do? We go with trying to get models without any convergence warnings and of the two we just fit, m_02a is less different from m_01b than m_02b, so that’s the we one we continue to do model selection with:
summary(m_03 <-update( m_02a, .~.- (1+ USED|NAME) # random effects dropped+ (1|NAME)), # random effects inserted: no slopes for USEDcorrelation=FALSE)
Linear mixed model fit by REML ['lmerMod']
Formula: OVERLAP.asin ~ SAMPLE + USED + (1 | NAME) + SAMPLE:USED
Data: d
REML criterion at convergence: -14.8
Scaled residuals:
Min 1Q Median 3Q Max
-2.1555 -0.3730 -0.0158 0.2510 4.4267
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.01897 0.1377
Residual 0.02856 0.1690
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.11711 0.04873 2.403
SAMPLE200-100 0.03765 0.06899 0.546
SAMPLE500-200 0.08089 0.06899 1.172
USEDyes 0.33570 0.03983 8.428
SAMPLE200-100:USEDyes 0.11029 0.09757 1.130
SAMPLE500-200:USEDyes 0.06512 0.09757 0.667
anova(m_02a, m_03, test="Chisq", refit=FALSE) # note the refit=FALSE, we stick with REML
The LR-test again says we shouldn’t simplify, but this model converges so we’re done with the ranef structure and begin the exploration of the fixef structure with (the ML version of) m_03.
1.3.2 Exploring the fixed-effects structure
First, we switch to an ML version of m_03:
summary(m_03.ml <-update( # update m_03, .~., # this to a model w/REML=FALSE), # ML estimationcorrelation=FALSE)
Linear mixed model fit by maximum likelihood ['lmerMod']
Formula: OVERLAP.asin ~ SAMPLE + USED + (1 | NAME) + SAMPLE:USED
Data: d
AIC BIC logLik -2*log(L) df.resid
-22.7 -4.5 19.3 -38.7 64
Scaled residuals:
Min 1Q Median 3Q Max
-2.2514 -0.3896 -0.0165 0.2622 4.6236
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.01739 0.1319
Residual 0.02618 0.1618
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.11711 0.04665 2.510
SAMPLE200-100 0.03765 0.06605 0.570
SAMPLE500-200 0.08089 0.06605 1.225
USEDyes 0.33570 0.03814 8.803
SAMPLE200-100:USEDyes 0.11029 0.09341 1.181
SAMPLE500-200:USEDyes 0.06512 0.09341 0.697
Then, we determine what, if anything, we can / need to drop:
drop1(m_03.ml, test="Chisq") %>% data.frame
npar AIC LRT Pr.Chi.
<none> NA -22.67822 NA NA
SAMPLE:USED 2 -23.17829 3.499933 0.1737798
The interaction of SAMPLE:USED is ns so we drop it:
summary(m_04.ml <-update( # update m_03.ml, .~. # this to a model w/- SAMPLE:USED), # fixef interaction droppedcorrelation=FALSE)
Linear mixed model fit by maximum likelihood ['lmerMod']
Formula: OVERLAP.asin ~ SAMPLE + USED + (1 | NAME)
Data: d
AIC BIC logLik -2*log(L) df.resid
-23.2 -9.5 17.6 -35.2 66
Scaled residuals:
Min 1Q Median 3Q Max
-2.1833 -0.4533 -0.0623 0.2739 4.5693
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.01713 0.1309
Residual 0.02775 0.1666
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.11711 0.04688 2.498
SAMPLE200-100 0.09279 0.04809 1.930
SAMPLE500-200 0.11345 0.04809 2.359
USEDyes 0.33570 0.03926 8.550
npar AIC BIC logLik X.2.log.L. Chisq Df Pr..Chisq.
m_04.ml 6 -23.17829 -9.518289 17.58914 -35.17829 NA NA NA
m_03.ml 8 -22.67822 -4.464889 19.33911 -38.67822 3.499933 2 0.1737798
Can/must we drop even more?
drop1(m_04.ml, test="Chisq") %>% data.frame
npar AIC LRT Pr.Chi.
<none> NA -23.17829 NA NA
SAMPLE 2 -11.08792 16.09036 3.206430e-04
USED 1 22.62703 47.80531 4.707142e-12
Nope, that’s it, we’re done: Our final model is the REML version of m_04.ml:
summary(m_final <-update( # update m_04.ml, .~., # this to a model w/REML=TRUE), # REML estimationcorrelation=FALSE)
Linear mixed model fit by REML ['lmerMod']
Formula: OVERLAP.asin ~ SAMPLE + USED + (1 | NAME)
Data: d
REML criterion at convergence: -17.4
Scaled residuals:
Min 1Q Median 3Q Max
-2.1482 -0.4389 -0.0580 0.2695 4.4334
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.01886 0.1373
Residual 0.02921 0.1709
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.11711 0.04882 2.399
SAMPLE200-100 0.09279 0.04934 1.881
SAMPLE500-200 0.11345 0.04934 2.299
USEDyes 0.33570 0.04028 8.333
We first generate a summary output of the final model that provides p-values for the fixef coefficients. Note that I am again not fully loading the package lmerTest with library but use explicit namespacing instead:
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: formula(m_final)
Data: d
REML criterion at convergence: -17.4
Scaled residuals:
Min 1Q Median 3Q Max
-2.1482 -0.4389 -0.0580 0.2695 4.4334
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.01886 0.1373
Residual 0.02921 0.1709
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error df t value Pr(>|t|)
(Intercept) 0.11711 0.04882 15.84772 2.399 0.0291 *
SAMPLE200-100 0.09279 0.04934 57.00000 1.881 0.0651 .
SAMPLE500-200 0.11345 0.04934 57.00000 2.299 0.0252 *
USEDyes 0.33570 0.04028 57.00000 8.333 1.93e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
With a one-tailed test, which we allowed to do given that we suspected positive correlation of SAMPLE with the response, all coefficients are significant. Also, we should maybe generate nd again for all combinations of predictors:
nd <-expand.grid(SAMPLE=levels(d$SAMPLE), # all levels of SAMPLEUSED=levels(d$USED)) # all levels of USEDnd <-cbind(nd, PREDS=predict( # note: now we're not using ranefs m_final, re.form=NA, newdata=nd)) # for the predictions!nd$PREDS_backtr <-sin(nd$PREDS)tapply(nd$PREDS, list(nd$USED, nd$SAMPLE), c) %>%round(4)
Figure 5: The residuals of the final model (per child)
Doing some more detailed exploration of whether Ruth’s data ‘do damage’ to the model might be useful …
1.4.3 Exploring residuals against fitted
Let’s also generate the usual diagnostic plot that plot(lm(...)) would generate: residuals vs. fitted. This certainly doesn’t look good: there are Ruth’s data points and – because of them? – a clear curved trend:
plot(m_final, type=c("p", "smooth"), id=0.05)
Figure 6: Residuals against fitted for the final model
One can sometimes also do the following, but in this case this is not useful:
Figure 7: Residuals against predictors for the final model
Figure 8: Residuals against predictors for the final model
1.4.5 Looking for solutions 1: influence measures
It doesn’t seem as if omitting Ruth would change things a bit, but not all that much:
influence.ME::pchange(influence.ME::influence(m_final, group="NAME")) # percentage changeinfluence.ME::sigtest(influence.ME::influence(m_final, group="NAME")) # does sign change?
But let’s look at the diagnostics if we drop her, which is of course simplistic (because we only use the final model when omitting her might actually lead to a different final model). Definitely no progress with regard to the curvature:
Linear mixed model fit by REML ['lmerMod']
Formula: OVERLAP.asin ~ SAMPLE + USED + (1 | NAME)
Data: d
Subset: d$NAME != "Ruth"
REML criterion at convergence: -138.9
Scaled residuals:
Min 1Q Median 3Q Max
-1.90513 -0.62064 0.02778 0.54927 2.66754
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.002149 0.04635
Residual 0.004073 0.06382
Number of obs: 66, groups: NAME, 11
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.11526 0.01785 6.456
SAMPLE200-100 0.07015 0.01924 3.646
SAMPLE500-200 0.11822 0.01924 6.144
USEDyes 0.25542 0.01571 16.258
Figure 10: Residuals against fitted for the final model (all names)
It seems as if there is a negative correlation between residuals and fitted values when USED is no, but a positive correlation between residuals and fitted values when USED is yes …
1.4.6 Looking for solutions 2: curvature
What if we build curvature into the model by making SAMPLE a curved numeric predictor? We start from/with the minimal ranef structure only:
d$SAMPLE.n <-as.numeric(as.character(d$SAMPLE))summary(m_curved <-lmer( OVERLAP.asin ~1+# interceptpoly(SAMPLE.n, 2) + USED +# fixed effects (1|NAME), # random effectsdata=d), correlation=FALSE)
Linear mixed model fit by REML ['lmerMod']
Formula: OVERLAP.asin ~ 1 + poly(SAMPLE.n, 2) + USED + (1 | NAME)
Data: d
REML criterion at convergence: -22.7
Scaled residuals:
Min 1Q Median 3Q Max
-2.1482 -0.4389 -0.0580 0.2695 4.4334
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.01886 0.1373
Residual 0.02921 0.1709
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.11711 0.04882 2.399
poly(SAMPLE.n, 2)1 0.69786 0.17091 4.083
poly(SAMPLE.n, 2)2 -0.15845 0.17091 -0.927
USEDyes 0.33570 0.04028 8.333
drop1(m_curved, test="Chisq") %>% data.frame
npar AIC LRT Pr.Chi.
<none> NA -23.17829 NA NA
poly(SAMPLE.n, 2) 2 -11.08792 16.09036 3.206430e-04
USED 1 22.62703 47.80531 4.707142e-12
These are actually the p-values from m_04.ml because, here, we use the same number of coefficients (2) to study SAMPLE: one time with 2 level differences (200-100 and 500-200), one time with the 2 polynomial columns. Thus, no progress really. We’ll stick with the current model for now to not overcomplicate things.
1.5 Numerical interpretation
Let’s generate all numerical results again, just to have it all in one place:
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: formula(m_final)
Data: d
REML criterion at convergence: -17.4
Scaled residuals:
Min 1Q Median 3Q Max
-2.1482 -0.4389 -0.0580 0.2695 4.4334
Random effects:
Groups Name Variance Std.Dev.
NAME (Intercept) 0.01886 0.1373
Residual 0.02921 0.1709
Number of obs: 72, groups: NAME, 12
Fixed effects:
Estimate Std. Error df t value Pr(>|t|)
(Intercept) 0.11711 0.04882 15.84772 2.399 0.0291 *
SAMPLE200-100 0.09279 0.04934 57.00000 1.881 0.0651 .
SAMPLE500-200 0.11345 0.04934 57.00000 2.299 0.0252 *
USEDyes 0.33570 0.04028 57.00000 8.333 1.93e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
drop1(m_final, test="Chisq") %>% data.frame
npar AIC LRT Pr.Chi.
<none> NA -23.17829 NA NA
SAMPLE 2 -11.08792 16.09036 3.206430e-04
USED 1 22.62703 47.80531 4.707142e-12
The overall significance test of (the fixed effects of ) m_final can be conducted via model comparison again: we compare m_final to a model that has the same ranef structure but no fixefs but the overall intercept. That way, an LRT with anova will determine the p-value of all fixefs together:
m_null_re <-lmer( # create a null model, now with ranefs: OVERLAP.asin ~1+# no fixefs but an overall intercept plus (1|NAME), # the ranef structure of the final modeldata=d)anova(m_final, # LRT comparison of the final model m_null_re, # with this null modeltest="Chisq") %>% data.frame # with ML! The diff is ***
npar AIC BIC logLik X.2.log.L. Chisq Df Pr..Chisq.
m_null_re 3 26.41766 33.247661 -10.20883 20.41766 NA NA NA
m_final 6 -23.17829 -9.518289 17.58914 -35.17829 55.59595 3 5.12349e-12
---title: "Ling 204: session 06"author: - name: "[Stefan Th. Gries](https://www.stgries.info)" affiliation: - UC Santa Barbara - JLU Giessen orcid: 0000-0002-6497-3958date: "2026-02-11 12:34:56 PDT"date-format: "DD MMM YYYY HH-mm-ss"editor: sourceformat: html: page-layout: full code-fold: false code-link: true code-copy: true code-tools: true code-line-numbers: true code-overflow: scroll number-sections: true smooth-scroll: true toc: true toc-depth: 4 number-depth: 4 toc-location: left monofont: lucida console tbl-cap-location: top fig-cap-location: bottom fig-width: 4 fig-height: 4 fig-format: png fig-dpi: 300 fig-align: center embed-resources: true link-external-newwindow: trueexecute: cache: false echo: true eval: true warning: false---# Session 06: Mixed-effects models 2a {#sec-s06}In this session, we will look at another data set in a language acquisition paper published in Cognition in (2013), namely [Pine et al. (2013)](https://www.sciencedirect.com/science/article/abs/pii/S0010027713000346). It, too, is not really ideal for the application of mixed-effects modeling, but again nicely small (so they are easy to look at comprehensively and fast to run) and also didactically interesting. Still, bear in mind that they are not the best examples and that the analysis we will proceed with here is quite some overkill, given the size and complexity of this data set.```{r}#| results: holdrm(list=ls(all.names=TRUE))set.seed(sum(utf8ToInt("Gummibaerchen")))pkgs2load <-c("dplyr", "car", "effects", "emmeans", "lme4", "magrittr", "multcomp", "MuMIn")suppressMessages(sapply(pkgs2load, library, character.only=TRUE, logical.return=TRUE, quietly=TRUE))rm(pkgs2load)```This analysis will be based on the data in their Table 2, which we load from [_input/pine-etal-table2.csv](_input/pine-etal-table2.csv), and you can find information about its contents in [_input/pine-etal-table2.r](_input/pine-etal-table2.r):```{r}summary(d <-read.delim(file="_input/pine-etal-table2.csv",stringsAsFactors=TRUE))```They did this case study because they expected that the amount of overlap would be positively correlated with the sample size.## Exploration & preparationAs always, it is indispensable to do a decent amount of exploratory plotting before any statistical analysis is begun. If you do mixed-effects modeling, you really must look at* every variable in isolation (the response, predictors, controls, random effects, everything);* every rhs variable (fixed and random effects) with the response;* every combination of rhs variables that will be considered in the model on their own and with the response.### The responseSince we want to stay as close to their data as possible, we follow them in applying the [arcsine transformation](https://esajournals.onlinelibrary.wiley.com/doi/pdf/10.1890/10-0340.1) to the response variable:```{r}d$OVERLAP.asin <-asin(d$OVERLAP)summary(d <- d[,c(1,3,6,4,5,2)])```What do the response and its transformed version look like?```{r}#| label: fig-s06_descrresp#| fig-cap: Descriptive exploration of the response#| fig-width: 9par(mfrow=c(1,3))hist(d$OVERLAP, breaks="FD", main="")hist(d$OVERLAP.asin, breaks="FD", main="")plot(d$OVERLAP, d$OVERLAP.asin); grid(); abline(0, 1)par(mfrow=c(1,1))```Not really clear to me that this transformation does so much good here ...### The fixed-effects structureHere's the first predictor, `SAMPLE`, which we convert to a factor first because that's what [Pine et al.](https://www.sciencedirect.com/science/article/abs/pii/S0010027713000346) did; all levels are equally frequent, perfectly balanced:```{r}#| label: fig-s06_descrrespAFAsample#| fig-cap: Exploration of the response as a function of SAMPLEd$SAMPLE <-factor(d$SAMPLE)table(d$SAMPLE) # frequencies of levelstapply(d$OVERLAP.asin, d$SAMPLE, mean) # means of response per levelboxplot(d$OVERLAP.asin ~ d$SAMPLE, notch=TRUE); grid()```Here's the second predictor `USED`; same story: everything's perfectly balanced and at least from what we see now, this should be highly significant:```{r}#| label: fig-s06_descrrespAFAused#| fig-cap: Exploration of the response as a function of USEDtable(d$USED) # frequencies of levelstapply(d$OVERLAP.asin, d$USED, mean) # means of response per levelboxplot(d$OVERLAP.asin ~ d$USED, notch=TRUE); grid()```Now the combination of predictors, which looks like there could be an interaction between `SAMPLE` and `USED`:```{r}table(d$SAMPLE, d$USED) # frequencies of combinations of levelstapply(d$OVERLAP.asin, list(d$SAMPLE, d$USED), mean) # means of ...```### The random-effects structureEvery kid contributes the same (small) number of data points:```{r}table(d$NAME)```Every kid is recorded with each sample size, which means we need varying slopes of `SAMPLE|NAME` (and maybe the interaction w/ `USED`):```{r}table(d$NAME, d$SAMPLE)```Every kid is recorded in each condition of `USED` (& even equally often), which means we need varying slopes of `USED|NAME` (and maybe the interaction w/ `SAMPLE`):```{r}table(d$NAME, d$USED)```Every kid is recorded once in each combo of `SAMPLE` & `USED` (!)```{r}ftable(d$NAME, d$USED, d$SAMPLE)```## Wrong & traditional analysesOne might be tempted to fit this model, but ...```{r}#| eval: falsesummary(lm(OVERLAP ~ NAME*SAMPLE*USED, data=d))```that would be wrong, because ...? Because * `NAME` is not a fixed effect but a random effect;* this model doesn't account for the repeated measurements;* this model cannot be fit because the highest-level interaction has only 1 case per combo.Here's what Pine et al. did, a repeated-measures ANOVA with the 2 predictors `SAMPLE` and `USED` nested into `NAME`:```{r}summary(aov(OVERLAP.asin ~ SAMPLE*USED +Error(NAME/(SAMPLE*USED)), data=d))ez::ezANOVA(data=d, dv=.(OVERLAP.asin), wid=.(NAME), within=.(SAMPLE, USED))```In their results, there is* a main effect of noun type (= `USED`);* a main effect of developmental stage (= `SAMPLE`);* an interaction of `USED:SAMPLE`.BTW: note the violation of sphericity.## Modeling### Exploring the random-effects structureThe first model one might fit is nonsense -- why?```{r}#| eval: falsesummary(m_01a <-lmer( OVERLAP.asin ~1+# intercept SAMPLE*USED +# fixed effects (1+ SAMPLE*USED|NAME), # random effectsdata=d), correlation=FALSE)```So we eliminate the interaction in the ranef structure, but first we do something else that is potentially useful: we set successive-difference contrasts for `SAMPLE`:```{r}contrasts(d$SAMPLE) <- contr.sdifsummary(m_01b <-lmer( OVERLAP.asin ~1+# intercept SAMPLE*USED +# fixed effects (1+ SAMPLE+USED|NAME), # random effectsdata=d), correlation=FALSE)```Of course we get a singularity warning ... We explore the ranef structure to see what we might end up with:```{r}rePCA(m_01b)$NAME$sdev %>%round(4)```The output suggests we will end up with 1 source of ranef variation. We have two kinds of random slopes we might eliminate and we cannot see from the ranef output which one comes with the smaller variance/sd. Thus, we try eliminating both (separately). First, we drop the slopes for `SAMPLE`:```{r}summary(m_02a <-update( m_01b, .~.- (1+ SAMPLE+USED|NAME) # random effects dropped+ (1+ USED|NAME)), # random effects inserted: no slopes for SAMPLEcorrelation=FALSE)anova(m_01b, m_02a, test="Chisq", refit=FALSE) # note the refit=FALSE, we stick with REML```We still get a singularity warning and the LR-test says we need subject-specific slopes for `SAMPLE`.Second, we drop the slopes for `USED`:```{r}summary(m_02b <-update( m_01b, .~.- (1+ SAMPLE+USED|NAME) # random effects dropped+ (1+ SAMPLE |NAME)), # random effects inserted: no slopes for USEDcorrelation=FALSE)anova(m_01b, m_02b, test="Chisq", refit=FALSE) # note the refit=FALSE, we stick with REML```We still get a singularity warning and the LR-test says we need subject-specific slopes for `USED`. Just like last time,1. the model summaries say you cannot fit the models because of singularity warnings;2. LR-tests say you cannot simplify the models' ranef structure because the slopes are all significant.So what do we do? We go with trying to get models without any convergence warnings and of the two we just fit, `m_02a` is less different from `m_01b` than `m_02b`, so that's the we one we continue to do model selection with:```{r}summary(m_03 <-update( m_02a, .~.- (1+ USED|NAME) # random effects dropped+ (1|NAME)), # random effects inserted: no slopes for USEDcorrelation=FALSE)anova(m_02a, m_03, test="Chisq", refit=FALSE) # note the refit=FALSE, we stick with REML```The LR-test again says we shouldn't simplify, but this model converges so we're done with the ranef structure and begin the exploration of the fixef structure with (the ML version of) `m_03`.### Exploring the fixed-effects structureFirst, we switch to an ML version of `m_03`:```{r}summary(m_03.ml <-update( # update m_03, .~., # this to a model w/REML=FALSE), # ML estimationcorrelation=FALSE)```Then, we determine what, if anything, we can / need to drop:```{r}drop1(m_03.ml, test="Chisq") %>% data.frame```The interaction of `SAMPLE:USED` is ns so we drop it:```{r}summary(m_04.ml <-update( # update m_03.ml, .~. # this to a model w/- SAMPLE:USED), # fixef interaction droppedcorrelation=FALSE)anova(m_03.ml, m_04.ml, test="Chisq") %>% data.frame```Can/must we drop even more?```{r}drop1(m_04.ml, test="Chisq") %>% data.frame```Nope, that's it, we're done: Our final model is the REML version of `m_04.ml`:```{r}summary(m_final <-update( # update m_04.ml, .~., # this to a model w/REML=TRUE), # REML estimationcorrelation=FALSE)```We first generate a summary output of the final model that provides *p*-values for the fixef coefficients. Note that I am again *not* fully loading the package `lmerTest` with `library` but use explicit namespacing instead:```{r}summary(lmerTest::lmer(formula(m_final), data=d), correlation=FALSE)```With a one-tailed test, which we allowed to do given that we suspected positive correlation of `SAMPLE` with the response, all coefficients are significant. Also, we should maybe generate `nd` again for all combinations of predictors:```{r}nd <-expand.grid(SAMPLE=levels(d$SAMPLE), # all levels of SAMPLEUSED=levels(d$USED)) # all levels of USEDnd <-cbind(nd, PREDS=predict( # note: now we're not using ranefs m_final, re.form=NA, newdata=nd)) # for the predictions!nd$PREDS_backtr <-sin(nd$PREDS)tapply(nd$PREDS, list(nd$USED, nd$SAMPLE), c) %>%round(4)```## Diagnostics### Exploring residuals generallyThe overall distribution of the residuals is not that great (even though we need to bear in mind how small the data set really is):```{r}#| label: fig-s06_mfinalr2residualhist#| fig-cap: The residuals of the current final modelhist(residuals(m_final), breaks="FD", xlim=c(-1, 1), main="")```### Exploring residuals per kidNow we see which kid sticks out:```{r}#| label: fig-s06_mfinalresiduals#| fig-cap: The residuals of the final model (per child)#| fig-width: 9#| fig-height: 5plot(residuals(m_final), type="h", ylim=c(-0.8, 0.8),col=rainbow(length(levels(d$NAME)))[as.numeric(d$NAME)]); grid()text(d$CASE,residuals(m_final),substr(as.character(d$SAMPLE), 1, 1),col=rainbow(length(levels(d$NAME)))[as.numeric(d$NAME)])text(tapply(d$CASE, d$NAME, mean), 0.2, levels(d$NAME),col=rainbow(length(levels(d$NAME)))[seq(length(levels(d$NAME)))])```Doing some more detailed exploration of whether Ruth's data 'do damage' to the model might be useful ...### Exploring residuals against fittedLet's also generate the usual diagnostic plot that `plot(lm(...))` would generate: residuals vs. fitted. This certainly doesn't look good: there are Ruth's data points and -- because of them? -- a clear curved trend:```{r}#| label: fig-s06_mfinalresidualsagfitted#| fig-cap: Residuals against fitted for the final modelplot(m_final, type=c("p", "smooth"), id=0.05)```One can sometimes also do the following, but in this case this is not useful:```{r}#| eval: falseplot(m_final, resid(.) ~fitted(.) | NAME, abline=c(h=0), lty=1, type=c("p", "smooth"))```### Exploring residuals against predictorsFinally, let's see whether there's anything noteworthy when we plot residuals against predictors; the residual distribution for `USED` is imbalanced.```{r}#| label: fig-s06_mfinalresidualsagpreds#| fig-cap: Residuals against predictors for the final modelstripchart(residuals(m_final) ~ d$SAMPLE, method="jitter"); grid(); abline(v=0)stripchart(residuals(m_final) ~ d$USED , method="jitter"); grid(); abline(v=0)```### Looking for solutions 1: influence measuresIt doesn't seem as if omitting Ruth would change things a bit, but not all that much:```{r}#| eval: falseinfluence.ME::pchange(influence.ME::influence(m_final, group="NAME")) # percentage changeinfluence.ME::sigtest(influence.ME::influence(m_final, group="NAME")) # does sign change?```But let's look at the diagnostics if we drop her, which is of course simplistic (because we only use the final model when omitting her might actually lead to a different final model). Definitely no progress with regard to the curvature:```{r}#| label: fig-s06_mfinalresidualsagfitted_noRuth#| fig-cap: Residuals against fitted for the final model (no Ruth)summary(m_final.noRuth <-lmer(formula(m_final),data=d, subset=d$NAME!="Ruth"), correlation=FALSE)plot(m_final.noRuth, type=c("p", "smooth"), id=0.05)```Let's explore in more detail:```{r}#| label: fig-s06_mfinalresidualsagfitted_names#| fig-cap: Residuals against fitted for the final model (all names)#| fig-width: 8par(mfrow=c(1,2))plot(type="n",xlab="Fitted values", x=fitted(m_final),ylab="Residuals" , y=residuals(m_final)); grid(); abline(h=0, lty=2)text(fitted(m_final), y=residuals(m_final), abbreviate(d$NAME, 2),col=rainbow(3)[d$SAMPLE])text(0.2, 0.6, levels(d$SAMPLE), col=rainbow(3), pos=c(2:4))plot(type="n",xlab="Fitted values", x=fitted(m_final),ylab="Residuals" , y=residuals(m_final)); grid(); abline(h=0, lty=2)text(fitted(m_final), y=residuals(m_final), abbreviate(d$NAME, 2),col=rainbow(2)[d$USED])text(0.2, 0.6, levels(d$USED), col=rainbow(2), pos=c(1,3))par(mfrow=c(1,1))```It seems as if there is a negative correlation between residuals and fitted values when `USED` is *no*, but a positive correlation between residuals and fitted values when `USED` is *yes* ...### Looking for solutions 2: curvatureWhat if we build curvature into the model by making `SAMPLE` a curved numeric predictor? We start from/with the minimal ranef structure only:```{r}d$SAMPLE.n <-as.numeric(as.character(d$SAMPLE))summary(m_curved <-lmer( OVERLAP.asin ~1+# interceptpoly(SAMPLE.n, 2) + USED +# fixed effects (1|NAME), # random effectsdata=d), correlation=FALSE)drop1(m_curved, test="Chisq") %>% data.frame```These are actually the *p*-values from `m_04.ml` because, here, we use the same number of coefficients (2) to study `SAMPLE`: one time with 2 level differences (*200-100* and *500-200*), one time with the 2 polynomial columns. Thus, no progress really. We'll stick with the current model for now to not overcomplicate things.## Numerical interpretationLet's generate all numerical results again, just to have it all in one place:```{r}summary(lmerTest::lmer(formula(m_final), data=d), correlation=FALSE)drop1(m_final, test="Chisq") %>% data.frame```The overall significance test of (the fixed effects of ) `m_final` can be conducted via model comparison again: we compare `m_final` to a model that has the same ranef structure but no fixefs but the overall intercept. That way, an LRT with `anova` will determine the *p*-value of all fixefs together:```{r}m_null_re <-lmer( # create a null model, now with ranefs: OVERLAP.asin ~1+# no fixefs but an overall intercept plus (1|NAME), # the ranef structure of the final modeldata=d)anova(m_final, # LRT comparison of the final model m_null_re, # with this null modeltest="Chisq") %>% data.frame # with ML! The diff is ***```Our three ways to compute confidence intervals:```{r confidenceintervals}#| warning: false(m_final_ci.car <- Confint(m_final))(m_final_ci.prof <- confint(m_final, method="profile"))(m_final_ci.boot <- confint(m_final, method="boot", nsim=200, parallel="snow", ncpus=10))save.image(file="_output/204_06_mfinal2&confints.RData")```Also, we want *R*^2^-values, which are all too often not provided (for reasons I don't understand):```{r}r.squaredGLMM(m_final)```Quite nice: decent *R*^2^~marginal~ and *R*^2^~conditional~ doesn't provide *that* much more variance explanation.Here're the most straightforward ways to extract fixefs and ranefs (in a way similar to what `coef` did for `lm`/`glm`):```{r}(fixdeffs <-fixef(m_final)) # same as coef(lm(...))(randeffs <-ranef(m_final, condVar=TRUE, drop=TRUE)) # a ranef.mer object(randeffs <-ranef(m_final, condVar=TRUE, drop=TRUE)$NAME) # a data frameVarCorr(m_final)```Let's make 'predictions' for all cases based on fixed *and* random effects:```{r}d$PREDS_ae <-predict( # make d$PREDS_ae the result of predicting m_final) # from m_final```Let's make 'predictions' for all cases based on fixefs only:```{r}d$PREDS_fe <-predict( # make d$PREDS_fe the result of predicting m_final, # from m_finalre.form=NA) # use no ranefs```And we check out the effects results: First, for `SAMPLE`, then for `USED`:```{r}sa_d <-data.frame( # sa_d the data frame of sa <-effect( # sa, the effect of"SAMPLE", m_final))# SAMPLE in m_finalsa_d$fit.backtr <-sin(sa_d$fit)sa_dus_d <-data.frame( # us_d the data frame of us <-effect( # us, the effect of"USED", m_final)) # USED in m_finalus_d$fit.backtr <-sin(us_d$fit)us_d```## Visual interpretation### Fixed effectsWe settle for the standard effects plot for the fixed effects:```{r}#| label: fig-s06_mfinalEffects1#| fig-cap: The effect of SAMPLE in the current final modelplot(effect("SAMPLE", m_final),ylim=c(0, 1.6), grid=TRUE,multiline=TRUE, confint=list(style="auto"))```... then for `USED`:```{r}#| label: fig-s06_mfinalEffects2#| fig-cap: The effect of USED in the current final modelplot(effect("USED", m_final),ylim=c(0, 1.6), grid=TRUE, x.var="USED",multiline=TRUE, confint=list(style="auto"))```### Random effectsWe also plot the intercept adjustments: no news there, we see what we already figured out above:```{r}#| label: fig-s06_mfinal2_randomeffects#| fig-cap: The adjustments to intercepts of the final modellattice::dotplot(ranef(m_final, condVar=TRUE))```# Session info```{r}sessionInfo()```