Ling 204: session 07

Author
Affiliations

UC Santa Barbara

JLU Giessen

Published

18 Feb 2026 11-34-56

1 Session 07: Mixed-effects models 3a

In this session, we will look at a data set that involves a generalized linear mixed-effects model. Specifically, we will look at a data set that is also discussed in the book, but we will analyze the data differently from how they are analyzed in the book. The data are on subject realization in spoken conversational Japanese involving native and non-native speakers to test the hypothesis that the intermediately proficient non-native speakers in the sample data will react differently to two other main predictors governing subject realization, namely the givenness of the subject referent and whether it is used contrastively (this study is based on a version of the data in Gries & Adelman 2014).

rm(list=ls(all.names=TRUE))
set.seed(sum(utf8ToInt("Gummibaerchen")))
pkgs2load <- c("dplyr", "car", "effects", "emmeans", "lme4", "magrittr", "multcomp", "MuMIn")
invisible(sapply(pkgs2load, library, character.only=TRUE, logical.return=TRUE, quietly=TRUE))
rm(pkgs2load)
source("_helpers/cohens.kappa.r"); source("_helpers/C.score.r")

We load from _input/subjreal.csv, and you can find information about its contents in _input/subjreal.r:

summary(d <- read.delim(
   file="_input/subjreal.csv",
   stringsAsFactors=TRUE))
      CASE        SPKR          SPEAKERID     SUBJREAL    CONTRAST
 Min.   :    1   njs:7371   26-JE_NNS:  821   no  :5016   no  :6393
 1st Qu.: 3814   nns:7394   19-JC_NNS:  769   yes :1649   yes : 271
 Median : 7634              18-JK_NJS:  760   NA's:8100   NA's:8101
 Mean   : 7630              7-JE_NJS :  731
 3rd Qu.:11443              24-JE_NNS:  710
 Max.   :15265              1-JC_NJS :  705
                            (Other)  :10269
   GIVENNESS
 Min.   : 0.000
 1st Qu.: 8.000
 Median :10.000
 Mean   : 7.875
 3rd Qu.:10.000
 Max.   :10.000
 NA's   :8990    

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

We begin with the response variable, which exhibits a fairly strong preference for ‘no subject’:

d$SUBJREAL %>% table %>% { list(
   "Freqs"=addmargins(.),
   "Props"=prop.table(.)) }
$Freqs
.
  no  yes  Sum
5016 1649 6665

$Props
.
       no       yes
0.7525881 0.2474119 

1.1.2 The fixed-effects structure

Here’s the first predictor, SPKR, which is nearly perfectly balanced …

d$SPKR %>% table %>% { list("Freqs"=addmargins(.), "Props"=prop.table(.)) }
$Freqs
.
  njs   nns   Sum
 7371  7394 14765

$Props
.
      njs       nns
0.4992211 0.5007789 

… and doesn’t seem to be correlated much with the response:

table(d$SPKR, d$SUBJREAL) %>% prop.table(1) %>% round(3)

         no   yes
  njs 0.768 0.232
  nns 0.734 0.266

Here’s the second predictor CONTRAST, which is distributed extremely unevenly …

d$CONTRAST %>% table %>% { list("Freqs"=addmargins(.), "Props"=prop.table(.)) }
$Freqs
.
  no  yes  Sum
6393  271 6664

$Props
.
        no        yes
0.95933373 0.04066627 

… and does seem to be extremely strongly correlated with the response:

table(d$CONTRAST, d$SUBJREAL) %>% prop.table(1) %>% round(3)

         no   yes
  no  0.777 0.223
  yes 0.185 0.815

And the last predictor, GIVENNESS:

d$GIVENNESS %>% table
.
   0    1    2    3    4    5    6    7    8    9   10
1023   12   13   27   24   37   56   77  192  472 3842 
spineplot(d$SUBJREAL ~ d$GIVENNESS)
Figure 1: Exploration of the response as a function of GIVENNESS

This predictor is distributed somewhat annoyingly: many 0s, many many 10s, very little in between. At the same time, the result is very suggestive (and in the expected direction).

A quick look at the combination of predictors, which looks like there will be a strong interaction between CONTRAST and GIVENNESS:

ftable(CONTR=d$CONTRAST, GIV=d$GIVENNESS, SUREAL=d$SUBJREAL) %>%
   prop.table(1) %>% round(3)
          SUREAL    no   yes
CONTR GIV
no    0          0.227 0.773
      1          0.333 0.667
      2          0.545 0.455
      3          0.318 0.682
      4          0.667 0.333
      5          0.514 0.486
      6          0.615 0.385
      7          0.743 0.257
      8          0.781 0.219
      9          0.815 0.185
      10         0.881 0.119
yes   0          0.160 0.840
      1            NaN   NaN
      2          0.000 1.000
      3          0.000 1.000
      4          0.333 0.667
      5          0.000 1.000
      6          0.500 0.500
      7          0.000 1.000
      8          0.357 0.643
      9          0.053 0.947
      10         0.163 0.837

But let’s also quickly check out the distribution of the NAs:

ftable(CONTR=is.na(d$CONTRAST), GIV=is.na(d$GIVENNESS), SREAL=is.na(d$SUBJREAL))
            SREAL FALSE TRUE
CONTR GIV
FALSE FALSE        5775    0
      TRUE          889    0
TRUE  FALSE           0    0
      TRUE            1 8100

Let’s make sure we don’t run into problems later and reduce the dataset to the complete cases:

d <- droplevels(d[complete.cases(d),])

1.1.3 The random-effects structure

As it should be: Every speaker contributes a nicely large number data points and every speaker is a native speaker of Japanese (njs) or not (nns):

table(d$SPEAKERID, d$SPKR) %>% addmargins

             njs  nns  Sum
  1-JC_NJS   299    0  299
  1-JC_NNS     0  266  266
  10-JE_NJS  247    0  247
  10-JE_NNS    0  236  236
  11-JE_NJS  195    0  195
  11-JE_NNS    0  250  250
  16-JE_NJS  359    0  359
  16-JE_NNS    0  189  189
  18-JK_NJS  386    0  386
  18-JK_NNS    0   77   77
  19-JC_NJS  160    0  160
  19-JC_NNS    0  290  290
  2-JK_NJS   222    0  222
  2-JK_NNS     0  278  278
  24-JE_NJS  234    0  234
  24-JE_NNS    0  173  173
  25-JE_NJS  300    0  300
  25-JE_NNS    0  220  220
  26-JE_NJS  179    0  179
  26-JE_NNS    0  248  248
  7-JE_NJS   277    0  277
  7-JE_NNS     0  188  188
  8-JE_NJS   318    0  318
  8-JE_NNS     0  184  184
  Sum       3176 2599 5775

But this table reveals something else: the levels of SPEAKERID seem to indicate the native speaker and the non-native speaker who made up one conversational pair. That means, we could add a column that represents which conversation the two speakers in it were a part of:

d$CONV <- factor(gsub(
   "_.*", "",
   d$SPEAKERID, perl=TRUE))

We can add this is an additional random-effects level, making this a good example of a multi-level model!

What about the predictors? Every speaker produced at least one instance of each level of CONTRAST: we need CONTRAST|SPEAKERID:

table(d$SPEAKERID, d$CONTRAST)

             no yes
  1-JC_NJS  283  16
  1-JC_NNS  242  24
  10-JE_NJS 241   6
  10-JE_NNS 222  14
  11-JE_NJS 188   7
  11-JE_NNS 247   3
  16-JE_NJS 349  10
  16-JE_NNS 180   9
  18-JK_NJS 374  12
  18-JK_NNS  74   3
  19-JC_NJS 152   8
  19-JC_NNS 260  30
  2-JK_NJS  212  10
  2-JK_NNS  259  19
  24-JE_NJS 220  14
  24-JE_NNS 162  11
  25-JE_NJS 289  11
  25-JE_NNS 210  10
  26-JE_NJS 178   1
  26-JE_NNS 244   4
  7-JE_NJS  268   9
  7-JE_NNS  178  10
  8-JE_NJS  306  12
  8-JE_NNS  181   3

And because of the nesting of SPEAKERID into CONV, that obviously means that each conversation also has at least one level of CONTRAST, which means we need CONTRAST|CONV/SPEAKERID:

table(d$CONV, d$CONTRAST)

         no yes
  1-JC  525  40
  10-JE 463  20
  11-JE 435  10
  16-JE 529  19
  18-JK 448  15
  19-JC 412  38
  2-JK  471  29
  24-JE 382  25
  25-JE 499  21
  26-JE 422   5
  7-JE  446  19
  8-JE  487  15

In addition, every speaker produced multiple different values of GIVENNESS: we need GIVENNESS|SPEAKERID:

table(d$SPEAKERID, d$GIVENNESS)

              0   1   2   3   4   5   6   7   8   9  10
  1-JC_NJS   61   1   0   1   2   3   2   7  11  27 184
  1-JC_NNS   50   2   0   3   2   2   2   6   9  20 170
  10-JE_NJS  44   0   0   4   1   3   3   6  17  29 140
  10-JE_NNS  56   1   0   4   1   4   3   4  10  20 133
  11-JE_NJS  30   0   0   1   1   0   1   4   5  20 133
  11-JE_NNS  41   1   0   0   1   3   3   3  11  22 165
  16-JE_NJS  71   0   2   2   1   3   3   4  12  24 237
  16-JE_NNS  32   1   1   0   1   0   3   4  11  24 112
  18-JK_NJS  88   0   1   0   2   4   7   4   7  34 239
  18-JK_NNS  25   0   0   1   0   1   2   0   6   4  38
  19-JC_NJS  23   0   0   1   0   1   1   2   4  10 118
  19-JC_NNS  65   0   0   2   0   0   2   2   8  22 189
  2-JK_NJS   31   2   0   1   2   1   3   1   1  11 169
  2-JK_NNS   55   0   1   1   1   0   0   1   5  15 199
  24-JE_NJS  43   1   1   0   0   2   0   1   6  24 156
  24-JE_NNS  37   0   0   0   0   0   1   1   5  14 115
  25-JE_NJS  45   0   1   1   1   0   1   7  15  26 203
  25-JE_NNS  31   0   0   0   2   0   0   1   5  25 156
  26-JE_NJS  19   0   1   0   0   3   3   2   6  13 132
  26-JE_NNS  29   1   1   0   1   1   2   4   5  18 186
  7-JE_NJS   35   1   3   1   1   2   5   6   8  16 199
  7-JE_NNS   46   1   0   2   1   1   4   1  10  22 100
  8-JE_NJS   39   0   1   2   2   3   4   5   8  14 240
  8-JE_NNS   27   0   0   0   1   0   1   1   7  18 129

And because of the nesting of SPEAKERID into CONV, that obviously means that each conversation also has at least one level of GIVENNESS, which means we need GIVENNESS|CONV/SPEAKERID:

table(d$CONV, d$GIVENNESS)

          0   1   2   3   4   5   6   7   8   9  10
  1-JC  111   3   0   4   4   5   4  13  20  47 354
  10-JE 100   1   0   8   2   7   6  10  27  49 273
  11-JE  71   1   0   1   2   3   4   7  16  42 298
  16-JE 103   1   3   2   2   3   6   8  23  48 349
  18-JK 113   0   1   1   2   5   9   4  13  38 277
  19-JC  88   0   0   3   0   1   3   4  12  32 307
  2-JK   86   2   1   2   3   1   3   2   6  26 368
  24-JE  80   1   1   0   0   2   1   2  11  38 271
  25-JE  76   0   1   1   3   0   1   8  20  51 359
  26-JE  48   1   2   0   1   4   5   6  11  31 318
  7-JE   81   2   3   3   2   3   9   7  18  38 299
  8-JE   66   0   1   2   3   3   5   6  15  32 369

1.2 Modeling

1.2.1 Exploring the random-effects structure 1

Let’s identify the random-effects structure we can/should use. The first model one might fit is this, which will take a long time (because of the complexity of the model and the high number of iterations we permit the model to explore):

summary(m_nest_gxg <- glmer(
   SUBJREAL ~ 1 +                               # intercept
      CONTRAST*GIVENNESS*SPKR +                 # fixefs
      (1+CONTRAST*GIVENNESS|CONV/SPEAKERID),    # ranefs
   family=binomial, data=d, na.action=na.exclude,
   control=glmerControl(optCtrl = list(maxfun=30000))),
   correlation=FALSE)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: SUBJREAL ~ 1 + CONTRAST * GIVENNESS * SPKR + (1 + CONTRAST *
    GIVENNESS | CONV/SPEAKERID)
   Data: d
Control: glmerControl(optCtrl = list(maxfun = 30000))

      AIC       BIC    logLik -2*log(L)  df.resid
   4850.0    5036.5   -2397.0    4794.0      5747

Scaled residuals:
    Min      1Q  Median      3Q     Max
-3.3302 -0.4069 -0.3403  0.3502  3.6251

Random effects:
 Groups         Name                  Variance Std.Dev. Corr
 SPEAKERID:CONV (Intercept)           0.041847 0.20456
                CONTRASTyes           0.351754 0.59309   0.74
                GIVENNESS             0.002043 0.04520  -0.98 -0.61
                CONTRASTyes:GIVENNESS 0.007391 0.08597   0.00 -0.65 -0.20
 CONV           (Intercept)           0.215186 0.46388
                CONTRASTyes           0.156127 0.39513   0.36
                GIVENNESS             0.003378 0.05812  -0.93 -0.68
                CONTRASTyes:GIVENNESS 0.009689 0.09843   0.75  0.88 -0.94
Number of obs: 5775, groups:  SPEAKERID:CONV, 24; CONV, 12

Fixed effects:
                               Estimate Std. Error z value Pr(>|z|)
(Intercept)                    1.266770   0.184426   6.869 6.48e-12 ***
CONTRASTyes                    0.179662   0.467829   0.384    0.701
GIVENNESS                     -0.337782   0.025257 -13.374  < 2e-16 ***
SPKRnns                        0.178030   0.181324   0.982    0.326
CONTRASTyes:GIVENNESS          0.375988   0.072267   5.203 1.96e-07 ***
CONTRASTyes:SPKRnns            0.582911   0.666633   0.874    0.382
GIVENNESS:SPKRnns              0.002105   0.026813   0.078    0.937
CONTRASTyes:GIVENNESS:SPKRnns  0.013189   0.100435   0.131    0.896
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

A few months ago, this thing didn’t actually converge unproblematically – now it does. But …

1.2.2 Exploring the fixed-effects structure 1

Let’s see what the fixed-effects structure turns out to be:

m_nest_gxg_drop1 <- drop1(m_nest_gxg, test="Chisq")
m_nest_gxg_drop1 %>% data.frame
                        npar      AIC      LRT   Pr.Chi.
<none>                    NA 4849.951       NA        NA
CONTRAST:GIVENNESS:SPKR    1 4850.037 2.086235 0.1486319

The 3-way interaction seems to be unnecessary, but we have the singularity warning. Again, welcome to the beautiful hell of mixed-effects modeling …

1.2.3 Exploring the random-effects structure 2

Let’s try all simpler random-effects structures:

m_nest_gpg <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1+CONTRAST+GIVENNESS|CONV/SPEAKERID))
m_nest_g   <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1+         GIVENNESS|CONV/SPEAKERID))
m_nest_c   <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1+CONTRAST          |CONV/SPEAKERID))
m_nest_i   <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1                   |CONV/SPEAKERID))
m_spkr_gpg <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1+CONTRAST+GIVENNESS|     SPEAKERID))
m_spkr_g   <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1+         GIVENNESS|     SPEAKERID))
m_spkr_c   <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1+CONTRAST          |     SPEAKERID))
m_spkr_i   <- update(m_nest_gxg, .~. - (1+CONTRAST*GIVENNESS|CONV/SPEAKERID)
   + (1                   |     SPEAKERID))

Note: these results may change over time as packages get updated. These are the results for lme4 version 1.1-38 and that’s the reason – again – why all your scripts should end the way mine do: with a call to sessionInfo(). Here are the results in a summary form:

  • m_nest_gxg: fine, but drop1 led to singularity;
  • m_nest_gpg: boundary (singular) fit: see help(‘isSingular’);
  • m_nest_g: Model failed to converge with max|grad| = 0.010574;
  • m_nest_c: Model failed to converge with max|grad| = 0.00344046;
  • m_nest_i: fine;
  • m_spkr_gpg: boundary (singular) fit: see help(‘isSingular’);
  • m_spkr_g: Model failed to converge with max|grad| = 0.00705867;
  • m_spkr_c: fine;
  • m_spkr_i: Model failed to converge with max|grad| = 0.00259271.

Let’s see how much the models that converged unproblematically differ in their predictions from each other:

table(predict(m_nest_gxg)>0, predict(m_nest_i)>0) # these

        FALSE TRUE
  FALSE  4545    3
  TRUE      3 1224
   cor(predict(m_nest_gxg), predict(m_nest_i))    # are
[1] 0.9756487
table(predict(m_nest_gxg)>0, predict(m_spkr_c)>0) # all

        FALSE TRUE
  FALSE  4541    7
  TRUE      3 1224
   cor(predict(m_nest_gxg), predict(m_spkr_c))    # virtually
[1] 0.9830091
table(predict(m_nest_i  )>0 ,predict(m_spkr_c)>0) # identical

        FALSE TRUE
  FALSE  4542    6
  TRUE      2 1225
   cor(predict(m_nest_i  ) ,predict(m_spkr_c))
[1] 0.9944702

Not very much really, at least that is somewhat encouraging.

1.2.4 Exploring the fixed-effects structure 2

We look at models without problems:

m_nest_i_drop1 <- drop1(m_nest_i, test="Chisq")
m_nest_i_drop1 %>% data.frame
                        npar      AIC       LRT  Pr.Chi.
<none>                    NA 4859.313        NA       NA
CONTRAST:GIVENNESS:SPKR    1 4857.837 0.5240882 0.469103
m_spkr_c_drop1 <- drop1(m_spkr_c, test="Chisq")
m_spkr_c_drop1 %>% data.frame
                        npar      AIC       LRT   Pr.Chi.
<none>                    NA 4853.277        NA        NA
CONTRAST:GIVENNESS:SPKR    1 4851.476 0.1987317 0.6557465

We go with m_spkr_c since its simplification led to no warning:

summary(m_spkr_c_02 <- update(m_spkr_c, .~.       # update this to a model w/
   - CONTRAST:GIVENNESS:SPKR), correlation=FALSE) # the 3-way interaction dropped
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: SUBJREAL ~ CONTRAST + GIVENNESS + SPKR + (1 + CONTRAST | SPEAKERID) +
    CONTRAST:GIVENNESS + CONTRAST:SPKR + GIVENNESS:SPKR
   Data: d
Control: glmerControl(optCtrl = list(maxfun = 30000))

      AIC       BIC    logLik -2*log(L)  df.resid
   4851.5    4918.1   -2415.7    4831.5      5765

Scaled residuals:
    Min      1Q  Median      3Q     Max
-3.1717 -0.3916 -0.3610  0.4335  3.1688

Random effects:
 Groups    Name        Variance Std.Dev. Corr
 SPEAKERID (Intercept) 0.05051  0.2248
           CONTRASTyes 0.67407  0.8210   -0.86
Number of obs: 5775, groups:  SPEAKERID, 24

Fixed effects:
                       Estimate Std. Error z value Pr(>|z|)
(Intercept)            1.190536   0.121891   9.767   <2e-16 ***
CONTRASTyes            0.160592   0.406949   0.395    0.693
GIVENNESS             -0.325755   0.012390 -26.291   <2e-16 ***
SPKRnns                0.102070   0.176862   0.577    0.564
CONTRASTyes:GIVENNESS  0.340109   0.039606   8.587   <2e-16 ***
CONTRASTyes:SPKRnns    0.501306   0.522611   0.959    0.337
GIVENNESS:SPKRnns      0.008028   0.017958   0.447    0.655
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(m_spkr_c, m_spkr_c_02, test="Chisq") %>% data.frame
            npar      AIC      BIC    logLik X.2.log.L.     Chisq Df Pr..Chisq.
m_spkr_c_02   10 4851.476 4918.089 -2415.738   4831.476        NA NA         NA
m_spkr_c      11 4853.277 4926.551 -2415.639   4831.277 0.1987317  1  0.6557465

Ok, looks good, no issues, so let’s drop stuff:

m_spkr_c_02_drop1 <- drop1(m_spkr_c_02, test="Chisq")
m_spkr_c_02_drop1 %>% data.frame
                   npar      AIC        LRT      Pr.Chi.
<none>               NA 4851.476         NA           NA
CONTRAST:GIVENNESS    1 4911.142 61.6665090 4.068417e-15
CONTRAST:SPKR         1 4850.446  0.9702189 3.246256e-01
GIVENNESS:SPKR        1 4849.675  0.1996845 6.549756e-01

ARGGHHHH, we get a convergence warning, but one that is so small that we will ignore it:

summary(m_spkr_c_03 <- update(m_spkr_c_02, .~. # update this to a model w/
   - GIVENNESS:SPKR), correlation=FALSE)       # this interaction dropped
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: SUBJREAL ~ CONTRAST + GIVENNESS + SPKR + (1 + CONTRAST | SPEAKERID) +
    CONTRAST:GIVENNESS + CONTRAST:SPKR
   Data: d
Control: glmerControl(optCtrl = list(maxfun = 30000))

      AIC       BIC    logLik -2*log(L)  df.resid
   4849.7    4909.6   -2415.8    4831.7      5766

Scaled residuals:
    Min      1Q  Median      3Q     Max
-3.1582 -0.3899 -0.3627  0.4268  3.1496

Random effects:
 Groups    Name        Variance Std.Dev. Corr
 SPEAKERID (Intercept) 0.05049  0.2247
           CONTRASTyes 0.66742  0.8170   -0.86
Number of obs: 5775, groups:  SPEAKERID, 24

Fixed effects:
                      Estimate Std. Error z value Pr(>|z|)
(Intercept)            1.16434    0.10651  10.932   <2e-16 ***
CONTRASTyes            0.16728    0.40553   0.413    0.680
GIVENNESS             -0.32209    0.00925 -34.823   <2e-16 ***
SPKRnns                0.16006    0.12019   1.332    0.183
CONTRASTyes:GIVENNESS  0.33998    0.03960   8.585   <2e-16 ***
CONTRASTyes:SPKRnns    0.48408    0.52030   0.930    0.352
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
optimizer (Nelder_Mead) convergence code: 0 (OK)
Model failed to converge with max|grad| = 0.00201685 (tol = 0.002, component 1)
  See ?lme4::convergence and ?lme4::troubleshooting.
anova(m_spkr_c_02, m_spkr_c_03, test="Chisq") %>% data.frame
            npar      AIC      BIC    logLik X.2.log.L.     Chisq Df Pr..Chisq.
m_spkr_c_03    9 4849.675 4909.627 -2415.838   4831.675        NA NA         NA
m_spkr_c_02   10 4851.476 4918.089 -2415.738   4831.476 0.1996845  1  0.6549756

Anything else to be dropped?

m_spkr_c_03_drop1 <- drop1(m_spkr_c_03, test="Chisq")
m_spkr_c_03_drop1 %>% data.frame
                   npar      AIC      LRT      Pr.Chi.
<none>               NA 4849.675       NA           NA
CONTRAST:GIVENNESS    1 4909.337 61.66155 4.078685e-15
CONTRAST:SPKR         1 4848.589  0.91341 3.392113e-01

Yes and amazingly enough ….

summary(m_spkr_c_04 <- update(m_spkr_c_03, .~. # update this to a model w/
   - CONTRAST:SPKR), correlation=FALSE)        # this interaction dropped
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: SUBJREAL ~ CONTRAST + GIVENNESS + SPKR + (1 + CONTRAST | SPEAKERID) +
    CONTRAST:GIVENNESS
   Data: d
Control: glmerControl(optCtrl = list(maxfun = 30000))

      AIC       BIC    logLik -2*log(L)  df.resid
   4848.6    4901.9   -2416.3    4832.6      5767

Scaled residuals:
    Min      1Q  Median      3Q     Max
-2.9599 -0.3914 -0.3620  0.4260  3.1602

Random effects:
 Groups    Name        Variance Std.Dev. Corr
 SPEAKERID (Intercept) 0.0515   0.2269
           CONTRASTyes 0.6350   0.7968   -0.87
Number of obs: 5775, groups:  SPEAKERID, 24

Fixed effects:
                       Estimate Std. Error z value Pr(>|z|)
(Intercept)            1.133759   0.102217  11.092   <2e-16 ***
CONTRASTyes            0.395916   0.327583   1.209   0.2268
GIVENNESS             -0.322140   0.009251 -34.821   <2e-16 ***
SPKRnns                0.222996   0.102688   2.172   0.0299 *
CONTRASTyes:GIVENNESS  0.338731   0.039519   8.571   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(m_spkr_c_03, m_spkr_c_04, test="Chisq") %>% data.frame
            npar      AIC      BIC    logLik X.2.log.L.   Chisq Df Pr..Chisq.
m_spkr_c_04    8 4848.589 4901.879 -2416.294   4832.589      NA NA         NA
m_spkr_c_03    9 4849.675 4909.627 -2415.838   4831.675 0.91341  1  0.3392113

… this one converges unproblematically. Are we done now? Looks like it, which would be interesting …

m_spkr_c_04_drop1 <- drop1(m_spkr_c_04, test="Chisq")
m_spkr_c_04_drop1 %>% data.frame
                   npar      AIC       LRT      Pr.Chi.
<none>               NA 4848.589        NA           NA
SPKR                  1 4850.863  4.273919 3.870157e-02
CONTRAST:GIVENNESS    1 4908.154 61.564927 4.283816e-15

Wow, we are: SPKR is significant:

m_final <- m_spkr_c_04; rm(m_spkr_c_04)

1.3 Numerical interpretation

Let’s put everything together in one place, this time also with (just the fastest) CIs:

m_final %>% {
   list( "Summary"=summary(., correlation=FALSE),
        "P-values"=drop1(., test="Chisq") %>% data.frame,
        "ConfInts"=Confint(m_final)) }
$Summary
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: SUBJREAL ~ CONTRAST + GIVENNESS + SPKR + (1 + CONTRAST | SPEAKERID) +
    CONTRAST:GIVENNESS
   Data: d
Control: glmerControl(optCtrl = list(maxfun = 30000))

      AIC       BIC    logLik -2*log(L)  df.resid
   4848.6    4901.9   -2416.3    4832.6      5767

Scaled residuals:
    Min      1Q  Median      3Q     Max
-2.9599 -0.3914 -0.3620  0.4260  3.1602

Random effects:
 Groups    Name        Variance Std.Dev. Corr
 SPEAKERID (Intercept) 0.0515   0.2269
           CONTRASTyes 0.6350   0.7968   -0.87
Number of obs: 5775, groups:  SPEAKERID, 24

Fixed effects:
                       Estimate Std. Error z value Pr(>|z|)
(Intercept)            1.133759   0.102217  11.092   <2e-16 ***
CONTRASTyes            0.395916   0.327583   1.209   0.2268
GIVENNESS             -0.322140   0.009251 -34.821   <2e-16 ***
SPKRnns                0.222996   0.102688   2.172   0.0299 *
CONTRASTyes:GIVENNESS  0.338731   0.039519   8.571   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

$`P-values`
                   npar      AIC       LRT      Pr.Chi.
<none>               NA 4848.589        NA           NA
SPKR                  1 4850.863  4.273919 3.870157e-02
CONTRAST:GIVENNESS    1 4908.154 61.564927 4.283816e-15

$ConfInts
                        Estimate       2.5 %     97.5 %
(Intercept)            1.1337588  0.93341774  1.3340999
CONTRASTyes            0.3959162 -0.24613452  1.0379670
GIVENNESS             -0.3221404 -0.34027260 -0.3040082
SPKRnns                0.2229958  0.02173139  0.4242603
CONTRASTyes:GIVENNESS  0.3387314  0.26127535  0.4161874

The overall significance test of m_final can again be conducted via model comparison: we compare m_final to a model that has the same ranef structure but no fixefs but the overall intercept; in addition, we compute m_final’s relative likelihood over the null model:

m_null_re <- glmer( # create a null model, now with ranefs:
   SUBJREAL ~ 1 +                 # no fixefs, an overall intercept plus
      (1 + CONTRAST | SPEAKERID), # the ranef structure of the final model
      family=binomial, data=d)
anova(m_final,      # LRT comparison of the final model
      m_null_re,    # with this null model
      test="Chisq") %>% data.frame # !!
          npar      AIC      BIC    logLik X.2.log.L.    Chisq Df Pr..Chisq.
m_null_re    4 6423.130 6449.775 -3207.565   6415.130       NA NA         NA
m_final      8 4848.589 4901.879 -2416.294   4832.589 1582.541  4          0
# pchisq(1582.541, 4, lower.tail=FALSE)
LMERConvenienceFunctions::relLik( # how much more likely
   m_final,   # m_final is the right model than
   m_null_re) # m_null_re (i.e., insanely likely)
   AIC(x)    AIC(y)      diff    relLik
 4848.589  6423.130 -1574.541       Inf 
1/(exp((AIC(m_final)-AIC(m_null_re))/2)) # evidence ratio
[1] Inf

Let’s now turn to the predictions. First, we compute fixed-effects predictions for all combinations of predictors:

nd <- expand.grid(
   GIVENNESS=0:10,               # all values of GIVENNESS
   CONTRAST =levels(d$CONTRAST), # all levels of CONTRAST
   SPKR=levels(d$SPKR))          # all levels of SPKR
nd <- cbind(nd, PRED_LO_2=predict(   # add predicted log odds
   m_final, re.form=NA, newdata=nd)) # (for now w/out ranefs)
nd <- cbind(nd, PRED_PP_2=plogis(nd$PRED_LO_2)) # add predicted probs
(nd <- nd[,c(3,2,1,4,5)]) # reorder columns to something better
   SPKR CONTRAST GIVENNESS   PRED_LO_2 PRED_PP_2
1   njs       no         0  1.13375883 0.7565319
2   njs       no         1  0.81161843 0.6924543
3   njs       no         2  0.48947802 0.6199835
4   njs       no         3  0.16733761 0.5417371
5   njs       no         4 -0.15480279 0.4613764
6   njs       no         5 -0.47694320 0.3829742
7   njs       no         6 -0.79908361 0.3102216
8   njs       no         7 -1.12122401 0.2457843
9   njs       no         8 -1.44336442 0.1910249
10  njs       no         9 -1.76550483 0.1461022
11  njs       no        10 -2.08764523 0.1103035
12  njs      yes         0  1.52967505 0.8219588
13  njs      yes         1  1.54626604 0.8243738
14  njs      yes         2  1.56285702 0.8267629
15  njs      yes         3  1.57944801 0.8291263
16  njs      yes         4  1.59603900 0.8314641
17  njs      yes         5  1.61262999 0.8337762
18  njs      yes         6  1.62922098 0.8360629
19  njs      yes         7  1.64581197 0.8383242
20  njs      yes         8  1.66240296 0.8405603
21  njs      yes         9  1.67899394 0.8427713
22  njs      yes        10  1.69558493 0.8449572
23  nns       no         0  1.35675466 0.7952317
24  nns       no         1  1.03461425 0.7378095
25  nns       no         2  0.71247385 0.6709476
26  nns       no         3  0.39033344 0.5963630
27  nns       no         4  0.06819303 0.5170417
28  nns       no         5 -0.25394737 0.4368522
29  nns       no         6 -0.57608778 0.3598333
30  nns       no         7 -0.89822819 0.2894147
31  nns       no         8 -1.22036859 0.2278716
32  nns       no         9 -1.54250900 0.1761708
33  nns       no        10 -1.86464940 0.1341620
34  nns      yes         0  1.75267088 0.8522894
35  nns      yes         1  1.76926186 0.8543659
36  nns      yes         2  1.78585285 0.8564181
37  nns      yes         3  1.80244384 0.8584462
38  nns      yes         4  1.81903483 0.8604503
39  nns      yes         5  1.83562582 0.8624306
40  nns      yes         6  1.85221681 0.8643872
41  nns      yes         7  1.86880780 0.8663203
42  nns      yes         8  1.88539879 0.8682300
43  nns      yes         9  1.90198977 0.8701166
44  nns      yes        10  1.91858076 0.8719801

The probably most interpretable display:

tapply(nd$PRED_PP_2, list(
   GIVENNESS=nd$GIVENNESS,
    CONTRAST=nd$CONTRAST,
        SPKR=nd$SPKR), c)
, , SPKR = njs

         CONTRAST
GIVENNESS        no       yes
       0  0.7565319 0.8219588
       1  0.6924543 0.8243738
       2  0.6199835 0.8267629
       3  0.5417371 0.8291263
       4  0.4613764 0.8314641
       5  0.3829742 0.8337762
       6  0.3102216 0.8360629
       7  0.2457843 0.8383242
       8  0.1910249 0.8405603
       9  0.1461022 0.8427713
       10 0.1103035 0.8449572

, , SPKR = nns

         CONTRAST
GIVENNESS        no       yes
       0  0.7952317 0.8522894
       1  0.7378095 0.8543659
       2  0.6709476 0.8564181
       3  0.5963630 0.8584462
       4  0.5170417 0.8604503
       5  0.4368522 0.8624306
       6  0.3598333 0.8643872
       7  0.2894147 0.8663203
       8  0.2278716 0.8682300
       9  0.1761708 0.8701166
       10 0.1341620 0.8719801

We also generate the predictions for all cases in our data, first based on fixed and random effects …

d$PRED_PP_2_ae <- predict( # make d$PRED_PP_2_ae the result of predicting
   m_final,                # from m_final
   type="response")        # predicted probabilities of "yes"
d$PRED_CAT_ae <- factor(       # make d$PRED_CAT_ae the result of checking
   ifelse(d$PRED_PP_2_ae>=0.5, # if the predicted prob. of "yes" is >=0.5
      levels(d$SUBJREAL)[2],   # if yes, predict "yes"
      levels(d$SUBJREAL)[1]),  # otherwise, predict "no"
   levels=levels(d$SUBJREAL))  # make sure both levels are attested

… and then only on fixed effects:

d$PRED_PP_2_fe <- predict( # make d$PRED_PP_2_fe the result of predicting
   m_final,                # from m_final
   type="response",        # predicted probabilities of "yes"
   re.form=NA)             # use no ranefs
d$PRED_CAT_fe <- factor(       # make d$PRED_CAT_fe the result of checking
   ifelse(d$PRED_PP_2_fe>=0.5, # if the predicted prob. of "yes" is >=0.5
      levels(d$SUBJREAL)[2],   # if yes, predict "yes"
      levels(d$SUBJREAL)[1]),  # otherwise, predict "no"
   levels=levels(d$SUBJREAL))  # make sure both levels are attested

Looks like the ranefs do indeed not make that much of a difference here:

table(d$PRED_CAT_fe, d$PRED_CAT_ae)

        no  yes
  no  4542    7
  yes    5 1221

Let’s generate and evaluate a confusion matrix (based on the fixed effects only!) as well as compute C and Cohen’s κ:

(c_m <- table(              # confusion matrix: cross-tabulate
   "OBS"  =d$SUBJREAL,      # observed orders in the rows
   "PREDS"=d$PRED_CAT_fe)) # predicted orders in the columns
     PREDS
OBS     no  yes
  no  3920  273
  yes  629  953
c( # precisions & accuracies/recalls for each level of the response
   "Overall acc."       =mean(d$SUBJREAL==d$PRED_CAT_fe, na.rm=TRUE),
   "Prec. for yes"    =c_m["yes","yes"] / sum(c_m[     ,"yes"]),
   "Acc./rec. for yes"=c_m["yes","yes"] / sum(c_m["yes",]),
   "Prec. for  no"    =c_m[ "no", "no"] / sum(c_m[     , "no"]),
   "Acc./rec. for  no"=c_m[ "no", "no"] / sum(c_m[ "no",]),
   "C"                =C.score(d$SUBJREAL, d$PRED_PP_2_fe),
   "Cohen's kappa"    =cohens.kappa(c_m)[[1]])
     Overall acc.     Prec. for yes Acc./rec. for yes     Prec. for  no
        0.8438095         0.7773246         0.6024020         0.8617279
Acc./rec. for  no                 C     Cohen's kappa
        0.9348915         0.8028429         0.5777748 

Not too bad, actually … Finally, lets compute R2-values, which are ‘fine’; the random-effects structure really doesn’t do much:

r.squaredGLMM(m_final)
                  R2m       R2c
theoretical 0.3487088 0.3614607
delta       0.2806099 0.2908714

Computing Tjur’s R2 might also be good, it’s a very intuitive measure:

setNames(
   tapply(d$PRED_PP_2_fe, d$SUBJREAL, mean) %>% print() %>% diff,
   "Tjur's R-squared")
       no       yes
0.1745694 0.5351449 
Tjur's R-squared
       0.3605756 

1.4 Visual interpretation

1.4.1 Fixed effects

We begin with the main effect of SPKR that was actually not significant in the last model: The result is that there is a weak tendency for non-native speakers to realize the subject more often than the native speakers:

sp_d <- data.frame(
   sp <- effect("SPKR", m_final))[,-3]
sp_d$fit_lo <- qlogis(sp_d$fit) # add predicted log odds
plot(sp, type="response", ylim=c(0, 1), grid=TRUE)
Figure 2: The effect of SPKR in the current final model

We continue with CONTRAST:GIVENNESS:

cogi_d <- data.frame(
   cogi <- effect("CONTRAST:GIVENNESS", m_final,
      xlevels=list(GIVENNESS=0:10)))[,c(2,1,3,5,6)]
cogi_d$fit_lo <- qlogis(cogi_d$fit) # add predicted log odds
plot(cogi, type="response", ylim=c(0, 1), grid=TRUE,
   multiline=TRUE, confint=list(style="auto"))
Figure 3: The effect of CONTRAST:GIVENNESS in the current final model

A very clear finding:

  • when CONTRAST is yes, GIVENNESS seems to have no effect on subject realization;
  • when CONTRAST is no, GIVENNESS seems to have a strong effect on subject realization such that, the more given the referent of the subject, the less likely it will be realized.

1.4.2 Random effects

Let’s quickly plot the intercept and slope adjustments, first as simple histograms (and I only do one bin width here for each because different bin widths don’t change anything here so I can save):

par(mfrow=c(1,2))
ranef(m_final)$SPEAKERID[,1] %>% hist(main="Intercept adjustments", xlim=c(-1.5, 1.5))
ranef(m_final)$SPEAKERID[,2] %>% hist(main="CONTRAST adjustments" , xlim=c(-1.5, 1.5))
par(mfrow=c(1,1))
Figure 4: The random effects of the final model

Looks fine, I suppose. What about the lattice plots? The maybe only interesting observation is that conversation 10-JE sticks out a bit:

lattice::dotplot(ranef(m_final, condVar=TRUE))
$SPEAKERID
Figure 5: The random effects of the final model

1.5 Exercises

1.5.1 Coefficients (yes, again)

The interpretation of the fixef interaction seemed very clear, but here are some test and follow-up questions because, after all, this is 204 >:) Here’s what you will probably want to work with:

fixef(m_final)
          (Intercept)           CONTRASTyes             GIVENNESS
            1.1337588             0.3959162            -0.3221404
              SPKRnns CONTRASTyes:GIVENNESS
            0.2229958             0.3387314 
(qwe <- tapply(nd$PRED_LO_2, list(
   GIVENNESS=nd$GIVENNESS,
    CONTRAST=nd$CONTRAST,
        SPKR=nd$SPKR), c))
, , SPKR = njs

         CONTRAST
GIVENNESS         no      yes
       0   1.1337588 1.529675
       1   0.8116184 1.546266
       2   0.4894780 1.562857
       3   0.1673376 1.579448
       4  -0.1548028 1.596039
       5  -0.4769432 1.612630
       6  -0.7990836 1.629221
       7  -1.1212240 1.645812
       8  -1.4433644 1.662403
       9  -1.7655048 1.678994
       10 -2.0876452 1.695585

, , SPKR = nns

         CONTRAST
GIVENNESS          no      yes
       0   1.35675466 1.752671
       1   1.03461425 1.769262
       2   0.71247385 1.785853
       3   0.39033344 1.802444
       4   0.06819303 1.819035
       5  -0.25394737 1.835626
       6  -0.57608778 1.852217
       7  -0.89822819 1.868808
       8  -1.22036859 1.885399
       9  -1.54250900 1.901990
       10 -1.86464940 1.918581

And now the questions:

  1. What is the slope of GIVENNESS when SPKR is njs and
    1. when CONTRAST is no?
    2. when CONTRAST is yes?
# 1a.
# -0.3221404
fixef(m_final)[3] %>% sum
[1] -0.3221404
nd$PRED_LO_2[2]-nd$PRED_LO_2[1]
[1] -0.3221404
qwe["1","no","njs"] - qwe["0","no","njs"]
[1] -0.3221404
glht(m_final, matrix(c(0,0,1,0,0), nrow=1))

     General Linear Hypotheses

Linear Hypotheses:
       Estimate
1 == 0  -0.3221
# 1b.
# 0.016591
fixef(m_final)[c(3,5)] %>% sum
[1] 0.01659099
qwe["1","yes","njs"] - qwe["0","yes","njs"]
[1] 0.01659099
nd$PRED_LO_2[13]-nd$PRED_LO_2[12]
[1] 0.01659099
glht(m_final, matrix(c(0,0,1,0,1), nrow=1))

     General Linear Hypotheses

Linear Hypotheses:
       Estimate
1 == 0  0.01659
# both
emtrends(             # compute the trends/slopes
   object=m_final,    # for the model m_final
   specs= ~ CONTRAST, # compute comparisons over this predictor
   var="GIVENNESS")   # namely all slopes of this predictor
 CONTRAST GIVENNESS.trend      SE  df asymp.LCL asymp.UCL
 no               -0.3221 0.00925 Inf   -0.3403   -0.3040
 yes               0.0166 0.03840 Inf   -0.0588    0.0919

Results are averaged over the levels of: SPKR
Confidence level used: 0.95 
  1. What is the slope of GIVENNESS when SPKR is nns and
    1. when CONTRAST is no?
    2. when CONTRAST is yes?
# 2a.
# -0.3221404
nd$PRED_LO_2[24]-nd$PRED_LO_2[23]
[1] -0.3221404
qwe["1","no","nns"] - qwe["0","no","nns"]
[1] -0.3221404
# 2b.
# 0.016591
nd$PRED_LO_2[35]-nd$PRED_LO_2[34]
[1] 0.01659099
qwe["1","yes","njs"] - qwe["0","yes","njs"]
[1] 0.01659099

Why are the two identical? Because there’s no 3-way interaction CONTRAST:GIVENNESS:SPKR in m_final!

  1. What are the confidence intervals of these two slopes (not corrected for ‘multiple testing’)?
# 3.
glht(m_final, matrix(c(0,0,1,0,0), nrow=1)) %>% confint %>% "["("confint")
$confint
    Estimate        lwr        upr
1 -0.3221404 -0.3402726 -0.3040082
attr(,"conf.level")
[1] 0.95
attr(,"calpha")
[1] 1.959964
glht(m_final, matrix(c(0,0,1,0,1), nrow=1)) %>% confint %>% "["("confint")
$confint
    Estimate         lwr        upr
1 0.01659099 -0.05876573 0.09194771
attr(,"conf.level")
[1] 0.95
attr(,"calpha")
[1] 1.959964
emtrends(               # compute the trends/slopes
   object=m_final,      # for the model m_final
   specs= ~ CONTRAST,   # compute comparisons over this predictor
   var="GIVENNESS") %>% # namely all slopes of this predictor
data.frame              # but return a data frame
  CONTRAST GIVENNESS.trend          SE  df   asymp.LCL   asymp.UCL
1       no     -0.32214041 0.009251289 Inf -0.34027260 -0.30400821
2      yes      0.01659099 0.038448013 Inf -0.05876573  0.09194771
  1. Is the slope of GIVENNESS when CONTRAST: no significantly different from when CONTRAST is yes?
# 4.
summary(m_final)$coef[5,]
# overkill:
# summary(glht(m_final, matrix(c(0,0,0,0,1), nrow=1)))
    Estimate   Std. Error      z value     Pr(>|z|)
3.387314e-01 3.951911e-02 8.571330e+00 1.022962e-17 
  1. We saw the confidence intervals for the two slopes of GIVENNESS above already but now, what is the p-value for whether the slope of GIVENNESS is significantly diff from 0
    1. when CONTRAST is no?
    2. when CONTRAST is yes?
# 5. Here's EVERYTHING:
glht(m_final, matrix(c(0,0,1,0,0), nrow=1)) %>% { list(
   "Summary"=summary(.)$test[3:6] %>% unlist,
   "ConfInt"=confint(.)$confint[2:3]) }
$Summary
coefficients.1        sigma.1        tstat.1        pvalues
  -0.322140407    0.009251289  -34.821137322    0.000000000

$ConfInt
[1] -0.3402726 -0.3040082
glht(m_final, matrix(c(0,0,1,0,1), nrow=1)) %>% { list(
   "Summary"=summary(.)$test[3:6] %>% unlist,
   "ConfInt"=confint(.)$confint[2:3]) }
$Summary
coefficients.1        sigma.1        tstat.1        pvalues
    0.01659099     0.03844801     0.43151745     0.66609216

$ConfInt
[1] -0.05876573  0.09194771

1.5.2 Making & understanding predictions manually

Let’s take a random speaker, e.g. 25-JE_NNS, and compute the predicted probability of this speaker realizing the subject when GIVENNESS is 4 and `CONTRAST is yes? Ideally, you’d do this manually first and then check it ‘less manually’.

fixef(m_final)
          (Intercept)           CONTRASTyes             GIVENNESS
            1.1337588             0.3959162            -0.3221404
              SPKRnns CONTRASTyes:GIVENNESS
            0.2229958             0.3387314 
# adjustments because of the speaker ID ...
ranef(m_final)$SPEAKERID["25-JE_NNS",]
          (Intercept) CONTRASTyes
25-JE_NNS  -0.3617135    1.209507
# and the resulting coefficients
coef(m_final)$SPEAKERID["25-JE_NNS",]
          (Intercept) CONTRASTyes  GIVENNESS   SPKRnns CONTRASTyes:GIVENNESS
25-JE_NNS   0.7720454    1.605424 -0.3221404 0.2229958             0.3387314
# completely manually
prediction_lo <-
  1.1337588          + # overall intercept
 -0.3617135          + #    adjustment for the SPEAKERID
  0.3959162          + # CONTRAST: yes
  1.209507           + #    adjustment for the SPEAKERID
(-0.3221404 * 4)     + # GIVENNESS: 4
  0.2229958          + # SPKR: nns
( 0.3387314 * 4)       # CONTRAST:GIVENNESS
prediction_pp <- plogis(prediction_lo)
c("predicted log odds"   =prediction_lo,
  "predicted probability"=prediction_pp)
   predicted log odds predicted probability
            2.6668283             0.9350406 
# with predict
qwe <- data.frame(CONTRAST="yes", GIVENNESS=4, SPKR="nns", SPEAKERID="25-JE_NNS")
c("predicted log odds"     =predict(m_final, newdata=qwe),
  "predicted probability 1"=plogis(predict(m_final, newdata=qwe)),
  "predicted probability 1"=predict(m_final, newdata=qwe, type="response"))
     predicted log odds.1 predicted probability 1.1 predicted probability 1.1
                2.6668287                 0.9350407                 0.9350407 
# or you use the fixed-effects prediction from nd, ...
nd[38,1:4]
   SPKR CONTRAST GIVENNESS PRED_LO_2
38  nns      yes         4  1.819035
# or from the model matrix, ...
(model.matrix(m_final)[1729,]*fixef(m_final)) %>% sum
[1] 1.819035
(qwe <- 1.819035 +
# adjust it by the relevant random effects, ...
 -0.3617135      + # adjustment to the intercept for the speaker
  1.209507)        # adjustment to CONTRAST for the speaker
[1] 2.666829
# and compute the predicted probability from this:
plogis(qwe)
[1] 0.9350407

2 Session info

sessionInfo()
R version 4.5.2 (2025-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Pop!_OS 22.04 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so;  LAPACK version 3.10.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C
 [9] LC_ADDRESS=C               LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C

time zone: America/Los_Angeles
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  compiler  methods
[8] base

other attached packages:
 [1] MuMIn_1.48.11   multcomp_1.4-29 TH.data_1.1-5   MASS_7.3-65
 [5] survival_3.8-6  mvtnorm_1.3-3   lme4_1.1-38     Matrix_1.7-4
 [9] emmeans_2.0.1   effects_4.2-4   car_3.1-5       carData_3.0-6
[13] dplyr_1.2.0     STGmisc_1.06    Rcpp_1.1.1      magrittr_2.0.4

loaded via a namespace (and not attached):
 [1] dotCall64_1.2                spam_2.11-3
 [3] xfun_0.56                    htmlwidgets_1.6.4
 [5] insight_1.4.6                lattice_0.22-9
 [7] vctrs_0.7.1                  tools_4.5.2
 [9] Rdpack_2.6.6                 generics_0.1.4
[11] parallel_4.5.2               stats4_4.5.2
[13] sandwich_3.1-1               tibble_3.3.1
[15] pkgconfig_2.0.3              RColorBrewer_1.1-3
[17] LCFdata_2.0                  lifecycle_1.0.5
[19] fields_17.1                  LMERConvenienceFunctions_3.0
[21] mitools_2.4                  codetools_0.2-20
[23] survey_4.4-8                 htmltools_0.5.9
[25] maps_3.4.3                   yaml_2.3.12
[27] Formula_1.2-5                pillar_1.11.1
[29] nloptr_2.2.1                 reformulas_0.4.4
[31] boot_1.3-32                  abind_1.4-8
[33] nlme_3.1-168                 tidyselect_1.2.1
[35] digest_0.6.39                splines_4.5.2
[37] fastmap_1.2.0                grid_4.5.2
[39] colorspace_2.1-2             cli_3.6.5
[41] estimability_1.5.1           rmarkdown_2.30
[43] otel_0.2.0                   nnet_7.3-20
[45] zoo_1.8-15                   coda_0.19-4.1
[47] evaluate_1.0.5               knitr_1.51
[49] rbibutils_2.4.1              viridisLite_0.4.3
[51] mgcv_1.9-4                   rlang_1.1.7
[53] xtable_1.8-4                 glue_1.8.0
[55] DBI_1.2.3                    rstudioapi_0.18.0
[57] minqa_1.2.8                  jsonlite_2.0.0
[59] R6_2.6.1