Ling 105: session 03: linear regr. 2 (key)

Author
Affiliations

UC Santa Barbara

JLU Giessen

Published

15 Apr 2024 12-34-56

1 Introduction: A multifactorial model selection process

We are dealing with the same data set as last session; as a reminder, the data are in _input/rts.csv and you can find information about the variables/columns in _input/rts.r.

rm(list=ls(all.names=TRUE))
library(car); library(effects); library(magrittr); library(multcomp)
summary(d <- read.delim(   # summarize d, the result of loading
   file="_input/rts.csv",  # this file
   stringsAsFactors=TRUE)) # change categorical variables into factors
      CASE            RT             LENGTH         LANGUAGE         GROUP
 Min.   :   1   Min.   : 271.0   Min.   :3.000   english:4023   english :3961
 1st Qu.:2150   1st Qu.: 505.0   1st Qu.:4.000   spanish:3977   heritage:4039
 Median :4310   Median : 595.0   Median :5.000
 Mean   :4303   Mean   : 661.5   Mean   :5.198
 3rd Qu.:6450   3rd Qu.: 732.0   3rd Qu.:6.000
 Max.   :8610   Max.   :4130.0   Max.   :9.000
                NA's   :248
      CORRECT        FREQPMW           ZIPFFREQ     SITE
 correct  :7749   Min.   :   1.00   Min.   :3.000   a:2403
 incorrect: 251   1st Qu.:  17.00   1st Qu.:4.230   b:2815
                  Median :  42.00   Median :4.623   c:2782
                  Mean   :  81.14   Mean   :4.591
                  3rd Qu.: 101.00   3rd Qu.:5.004
                  Max.   :1152.00   Max.   :6.061
                                                            

However, this session, we will deal with this in a multifactorial way. Specifically, we are asking, does the reaction time to a word (in ms) vary as a function of

  • the Zipf frequency of that word (ZIPFFREQ);
  • the length of that word (LENGTH);
  • the language that word was presented in (LANGUAGE: english vs. spanish);
  • the speaker group that words was presented to (GROUP: english vs. heritage);
  • any pairwise interaction of these predictors?

2 Exploration & preparation

Since there are some missing data but only in the response variable, we immediately reduce d to all the complete cases a regression would consider:

summary(d <- droplevels(d[complete.cases(d),])) # note the droplevels!
      CASE            RT             LENGTH       LANGUAGE         GROUP
 Min.   :   1   Min.   : 271.0   Min.   :3.0   english:3977   english :3793
 1st Qu.:2123   1st Qu.: 505.0   1st Qu.:4.0   spanish:3775   heritage:3959
 Median :4262   Median : 595.0   Median :5.0
 Mean   :4268   Mean   : 661.5   Mean   :5.2
 3rd Qu.:6405   3rd Qu.: 732.0   3rd Qu.:6.0
 Max.   :8610   Max.   :4130.0   Max.   :9.0
      CORRECT        FREQPMW           ZIPFFREQ     SITE
 correct  :7749   Min.   :   1.00   Min.   :3.000   a:2403
 incorrect:   3   1st Qu.:  18.00   1st Qu.:4.255   b:2815
                  Median :  43.00   Median :4.633   c:2534
                  Mean   :  82.88   Mean   :4.609
                  3rd Qu.: 104.00   3rd Qu.:5.017
                  Max.   :1152.00   Max.   :6.061           

Some exploration of the relevant variables; in the interest of time we only look at univariate and monofactorial stuff – a big ‘no no’ for any real analysis! How would we want to explore the response variable?

hist(d$RT); hist(d$RT, breaks="FD")

How would we want to explore the predictors and its relation to the response?

# the predictors on their own
hist(d$ZIPFFREQ); hist(d$ZIPFFREQ, breaks="FD")

hist(d$LENGTH); hist(d$LENGTH, breaks="FD")

table(d$LANGUAGE)

english spanish
   3977    3775 
table(d$GROUP)

 english heritage
    3793     3959 
# the predictor(s) w/ the response
plot(main="RT in ms as a function of frequency per million words",
   pch=16, col="#00000020",
   xlab="Zipf frequency", xlim=c(  3,    6), x=d$ZIPFFREQ,
   ylab="RT (in ms)"    , ylim=c(250, 4250), y=d$RT); grid()
   abline(lm(d$RT ~ d$ZIPFFREQ), col="green", lwd=2)

plot(main="RT in ms as a function of length in characters",
   pch=16, col="#00000020",
   xlab="Length"    , xlim=c(  3,    9), x=jitter(d$LENGTH),
   ylab="RT (in ms)", ylim=c(250, 4250), y=d$RT); grid()
   abline(lm(d$RT ~ d$LENGTH), col="green", lwd=2)

qwe <- boxplot(main="RT in ms as a function of stimulus language",
   d$RT ~ d$LANGUAGE, notch=TRUE,
   xlab="Stimulus language",
   ylab="RT (in ms)"       , ylim=c(250, 1250)); grid()

asd <- boxplot(main="RT in ms as a function of speaker group",
   d$RT ~ d$GROUP, notch=TRUE,
   xlab="Speaker group",
   ylab="RT (in ms)"   , ylim=c(250, 1250)); grid()

table(d$LANGUAGE, d$GROUP)

          english heritage
  english    1988     1989
  spanish    1805     1970

Clearly, the long tail of the response variable RT might cause problems … For now, we will use it as is, but we’ll revisit this issue later.

3 Modeling & numerical interpretation

Let’s fit and evaluate our initial regression model:

summary(m_01 <- lm(       # make/summarize the linear model m_01:
   RT ~ 1 +               # RT ~ an overall intercept (1)
   # & these predictors & their pairwise interactions
   (ZIPFFREQ+LANGUAGE+GROUP+LENGTH)^2,
   data=d,                # those vars are in d
   na.action=na.exclude)) # skip cases with NA/missing data (good habit)

Call:
lm(formula = RT ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2,
    data = d, na.action = na.exclude)

Residuals:
   Min     1Q Median     3Q    Max
-473.2 -140.1  -54.2   67.2 3377.1

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    392.216    126.137   3.109 0.001881 **
ZIPFFREQ                        30.825     25.934   1.189 0.234634
LANGUAGEspanish                211.823     61.219   3.460 0.000543 ***
GROUPheritage                   39.993     58.276   0.686 0.492570
LENGTH                          75.070     23.996   3.128 0.001764 **
ZIPFFREQ:LANGUAGEspanish       -19.369     10.973  -1.765 0.077564 .
ZIPFFREQ:GROUPheritage           4.100     10.421   0.393 0.693967
ZIPFFREQ:LENGTH                -14.688      4.974  -2.953 0.003154 **
LANGUAGEspanish:GROUPheritage -116.685     11.687  -9.985  < 2e-16 ***
LANGUAGEspanish:LENGTH          12.398      6.185   2.005 0.045048 *
GROUPheritage:LENGTH            -1.988      5.564  -0.357 0.720931
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 249.6 on 7741 degrees of freedom
Multiple R-squared:  0.1007,    Adjusted R-squared:  0.09956
F-statistic:  86.7 on 10 and 7741 DF,  p-value: < 2.2e-16
drop1(m_01,  # drop each droppable predictor at a time from m_01 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2
                  Df Sum of Sq       RSS   AIC F value    Pr(>F)
<none>                         482437102 85594
ZIPFFREQ:LANGUAGE  1    194199 482631300 85595  3.1160  0.077564 .
ZIPFFREQ:GROUP     1      9650 482446751 85592  0.1548  0.693967
ZIPFFREQ:LENGTH    1    543538 482980640 85600  8.7214  0.003154 **
LANGUAGE:GROUP     1   6212940 488650041 85691 99.6904 < 2.2e-16 ***
LANGUAGE:LENGTH    1    250420 482687522 85596  4.0181  0.045048 *
GROUP:LENGTH       1      7953 482445055 85592  0.1276  0.720931
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 2nd model with that interaction GROUP:LENGTH deleted:

summary(m_02 <- update( # make m_02 an update of
   m_01, .~.            # m_01, namely all of it (.~.), but then
   - GROUP:LENGTH))     # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH,
    data = d, na.action = na.exclude)

Residuals:
   Min     1Q Median     3Q    Max
-473.7 -139.8  -54.1   67.2 3377.7

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    398.363    124.951   3.188 0.001438 **
ZIPFFREQ                        30.572     25.923   1.179 0.238290
LANGUAGEspanish                212.790     61.155   3.480 0.000505 ***
GROUPheritage                   28.615     48.799   0.586 0.557641
LENGTH                          73.977     23.799   3.108 0.001888 **
ZIPFFREQ:LANGUAGEspanish       -19.428     10.971  -1.771 0.076611 .
ZIPFFREQ:GROUPheritage           4.428     10.380   0.427 0.669659
ZIPFFREQ:LENGTH                -14.668      4.973  -2.950 0.003191 **
LANGUAGEspanish:GROUPheritage -117.650     11.370 -10.348  < 2e-16 ***
LANGUAGEspanish:LENGTH          12.361      6.184   1.999 0.045653 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 249.6 on 7742 degrees of freedom
Multiple R-squared:  0.1007,    Adjusted R-squared:  0.09966
F-statistic: 96.33 on 9 and 7742 DF,  p-value: < 2.2e-16
anova(m_01, m_02, # compare m_01 to m_02
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1   7741 482437102
2   7742 482445055 -1   -7953.1 0.1276 0.7209
drop1(m_02,  # drop each droppable predictor at a time from m_02 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
                  Df Sum of Sq       RSS   AIC  F value    Pr(>F)
<none>                         482445055 85592
ZIPFFREQ:LANGUAGE  1    195434 482640489 85593   3.1362  0.076611 .
ZIPFFREQ:GROUP     1     11342 482456397 85590   0.1820  0.669659
ZIPFFREQ:LENGTH    1    542149 482987204 85598   8.7001  0.003191 **
LANGUAGE:GROUP     1   6672611 489117666 85696 107.0782 < 2.2e-16 ***
LANGUAGE:LENGTH    1    248989 482694044 85594   3.9956  0.045653 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 3rd model with the ns interaction ZIPFFREQ:GROUP deleted:

summary(m_03 <- update( # make m_03 an update of
   m_02, .~.            # m_02, namely all of it (.~.), but then
   - ZIPFFREQ:GROUP))   # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH, data = d,
    na.action = na.exclude)

Residuals:
   Min     1Q Median     3Q    Max
-473.0 -140.3  -53.9   67.3 3377.0

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    388.062    122.589   3.166 0.001554 **
ZIPFFREQ                        32.802     25.389   1.292 0.196397
LANGUAGEspanish                212.219     61.137   3.471 0.000521 ***
GROUPheritage                   49.158      7.917   6.209 5.59e-10 ***
LENGTH                          73.987     23.798   3.109 0.001884 **
ZIPFFREQ:LANGUAGEspanish       -19.308     10.966  -1.761 0.078336 .
ZIPFFREQ:LENGTH                -14.672      4.973  -2.951 0.003182 **
LANGUAGEspanish:GROUPheritage -117.915     11.352 -10.387  < 2e-16 ***
LANGUAGEspanish:LENGTH          12.384      6.183   2.003 0.045235 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 249.6 on 7743 degrees of freedom
Multiple R-squared:  0.1007,    Adjusted R-squared:  0.09976
F-statistic: 108.4 on 8 and 7743 DF,  p-value: < 2.2e-16
anova(m_02, m_03, # compare m_02 to m_03
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
  Res.Df       RSS Df Sum of Sq     F Pr(>F)
1   7742 482445055
2   7743 482456397 -1    -11342 0.182 0.6697
drop1(m_03,  # drop each droppable predictor at a time from m_03 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
                  Df Sum of Sq       RSS   AIC  F value    Pr(>F)
<none>                         482456397 85590
ZIPFFREQ:LANGUAGE  1    193152 482649549 85591   3.0999  0.078336 .
ZIPFFREQ:LENGTH    1    542435 482998832 85597   8.7056  0.003182 **
LANGUAGE:GROUP     1   6722672 489179069 85695 107.8930 < 2.2e-16 ***
LANGUAGE:LENGTH    1    249929 482706326 85592   4.0111  0.045235 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(m_04 <- update(  # make m_04 an update of
   m_03, .~.             # m_03, namely all of it (.~.), but then
   - ZIPFFREQ:LANGUAGE)) # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP + LANGUAGE:LENGTH, data = d, na.action = na.exclude)

Residuals:
   Min     1Q Median     3Q    Max
-470.1 -139.7  -54.4   67.0 3375.2

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    379.376    122.506   3.097 0.001963 **
ZIPFFREQ                        35.405     25.349   1.397 0.162547
LANGUAGEspanish                121.311     32.743   3.705 0.000213 ***
GROUPheritage                   49.191      7.918   6.213 5.47e-10 ***
LENGTH                          85.144     22.942   3.711 0.000208 ***
ZIPFFREQ:LENGTH                -17.231      4.756  -3.623 0.000293 ***
LANGUAGEspanish:GROUPheritage -117.709     11.353 -10.368  < 2e-16 ***
LANGUAGEspanish:LENGTH          12.765      6.180   2.065 0.038918 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 249.7 on 7744 degrees of freedom
Multiple R-squared:  0.1003,    Adjusted R-squared:  0.09951
F-statistic: 123.4 on 7 and 7744 DF,  p-value: < 2.2e-16
anova(m_03, m_04, # compare m_03 to m_04
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP + LANGUAGE:LENGTH
  Res.Df       RSS Df Sum of Sq      F  Pr(>F)
1   7743 482456397
2   7744 482649549 -1   -193152 3.0999 0.07834 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
drop1(m_04,  # drop each droppable predictor at a time from m_04 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP + LANGUAGE:LENGTH
                Df Sum of Sq       RSS   AIC  F value    Pr(>F)
<none>                       482649549 85591
ZIPFFREQ:LENGTH  1    817963 483467512 85602  13.1240 0.0002934 ***
LANGUAGE:GROUP   1   6699968 489349517 85696 107.4994 < 2.2e-16 ***
LANGUAGE:LENGTH  1    265873 482915422 85593   4.2659 0.0389183 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

We’re done:

m_final <- m_04; rm(m_04); invisible(gc())

Normally, we would do some regression diagnostics now – I have to skip those now to discuss how to interpret the model, but we will return to this later! For now, we compute the confidence intervals …

Confint(m_final)
                                Estimate        2.5 %     97.5 %
(Intercept)                    379.37596  139.2301487 619.521780
ZIPFFREQ                        35.40482  -14.2863069  85.095939
LANGUAGEspanish                121.31146   57.1255255 185.497392
GROUPheritage                   49.19136   33.6703963  64.712318
LENGTH                          85.14358   40.1715383 130.115626
ZIPFFREQ:LENGTH                -17.23059  -26.5541635  -7.907015
LANGUAGEspanish:GROUPheritage -117.70937 -139.9641773 -95.454555
LANGUAGEspanish:LENGTH          12.76469    0.6497283  24.879644

… , add the predictions of the model to the original data frame:

d$PRED <-   # make d$PRED the
   predict( # predictions for all cases
   m_final) # based on m_final
head(d) # check the result
  CASE   RT LENGTH LANGUAGE    GROUP CORRECT FREQPMW ZIPFFREQ SITE     PRED
1    1  748      5  spanish heritage correct      19 4.278754    b 704.5720
2    2  408      5  spanish heritage correct      78 4.892095    b 673.4461
3    3  483      6  spanish heritage correct     233 5.367356    b 654.7530
4    4 1266      4  spanish heritage correct     133 5.123852    b 652.0636
5    5  636      6  spanish heritage correct      14 4.146128    b 737.7705
6    6  395      6  spanish heritage correct      67 4.826075    b 691.5486

…, and generate an often super-useful kind of data frame that I always call nd (for new data) that contains all combinations of all levels of all categorical predictors and useful values of all numeric predictors:

nd <- expand.grid(
   ZIPFFREQ=c(0, 1, mean(d$ZIPFFREQ)),
   LENGTH  =c(0, 1, mean(d$LENGTH)),
   GROUP   =levels(d$GROUP),
   LANGUAGE=levels(d$LANGUAGE))

To that, we add the predictions the model makes for all these combinations and give it some nice(r) names:

nd <- cbind(nd,
   predict(m_final, newdata=nd, interval="confidence"))
names(nd)[5:7] <- c("PRED", "PRED_LOW", "PRED_UPP")
head(nd)
  ZIPFFREQ LENGTH   GROUP LANGUAGE     PRED PRED_LOW PRED_UPP
1 0.000000      0 english  english 379.3760 139.2301 619.5218
2 1.000000      0 english  english 414.7808 222.9552 606.6064
3 4.609476      0 english  english 542.5736 492.1866 592.9607
4 0.000000      1 english  english 464.5195 268.2101 660.8290
5 1.000000      1 english  english 482.6938 325.8986 639.4890
6 4.609476      1 english  english 548.2932 507.4990 589.0874

4 Visual interpretation

4.1 LANGUAGE:GROUP

Let’s visualize the nature of the effect of LANGUAGE:GROUP based on the predictions from effects:

lagr_d <- data.frame(   # make lagr_d a data frame of
   lagr <- effect(      # the effect
      "LANGUAGE:GROUP", # of LANGUAGE:GROUP
      m_final))         # in m_final
plot(lagr,                       # plot the effect of the interaction
   ylim=range(d$RT, na.rm=TRUE), # w/ these y-axis limits
   grid=TRUE)                    # & w/ a grid

plot(lagr,                       # plot the effect of the interaction
   ylim=range(d$RT, na.rm=TRUE), # w/ these y-axis limits
   x.var="GROUP",                # w/ this predictor on the x-axis
   grid=TRUE)                    # & w/ a grid

And here’s a version (of the first of the two effects plots) with everything: observed values, predicted values, and the confidence intervals of the predictions; to save space etc. I only show the one with the middle 90% y-axis:

# split data & predictions up by levels of GROUP:
d_split <- split(d, d$GROUP)
lagr_d_split <- split(lagr_d, lagr_d$GROUP, drop=TRUE)
par(mfrow=c(1, 2))
stripchart(main="RT ~ LANGUAGE for GROUP='english'", # stripchart w/ main heading
   pch=16, col="#00000020",                          # filled semi-transparent grey circles
   d_split$english$RT ~ d_split$english$LANGUAGE,    # data: RT ~ LANG (for eng)
   xlab="RT (in ms)", ylab="Language",               # axis labels
   ylim=c(417, 1138), vertical=TRUE, # y-axis: the middle 90% only
   method="jitter"); grid()          # jitter observations, add grid
# add predicted means:
points(seq(lagr_d_split$english$fit), lagr_d_split$english$fit, pch="X", col=c("red", "blue"))
# add confidence intervals:
for (i in seq(lagr_d_split$english$fit)) {  # as many arrows as needed
   arrows(i, lagr_d_split$english$lower[i], # each from here
          i, lagr_d_split$english$upper[i], # to here
          code=3, angle=90, col=c("red", "blue")[i])      # both tips w/ 90 degrees
}
stripchart(main="RT ~ LANGUAGE for GROUP='heritage'", # stripchart w/ main heading
   pch=16, col="#00000020",                           # filled semi-transparent grey circles
   d_split$heritage$RT ~ d_split$heritage$LANGUAGE,   # data: RT ~ LANG (for her)
   xlab="RT (in ms)", ylab="Language",                # axis labels
   ylim=c(417, 1138), vertical=TRUE, # y-axis: the middle 90% only
   method="jitter"); grid()          # jitter observations, add grid
# add predicted means:
points(seq(lagr_d_split$heritage$fit), lagr_d_split$heritage$fit, pch="X", col=c("red", "blue"))
# add confidence intervals:
for (i in seq(lagr_d_split$heritage$fit)) {  # as many arrows as needed
   arrows(i, lagr_d_split$heritage$lower[i], # each from here
          i, lagr_d_split$heritage$upper[i], # to here
          code=3, angle=90, col=c("red", "blue")[i])      # both tips w/ 90 degrees
}
par(mfrow=c(1, 1))

4.2 LANGUAGE:LENGTH

Let’s visualize the nature of the effect of LANGUAGE:LENGTH based on the predictions from effects:

(lale_d <- data.frame(   # make lale_d a data frame of
   lale <- effect(       # the effect
      "LANGUAGE:LENGTH", # of LANGUAGE:LENGTH
      m_final,           # in m_final
      xlevels=list(      # for when
      LENGTH=3:9))))     # LENGTH is these values
   LANGUAGE LENGTH      fit        se    lower    upper
1   english      3 584.8548 10.688211 563.9030 605.8065
2   spanish      3 684.3453  9.355514 666.0059 702.6846
3   english      4 590.5743  6.291153 578.2420 602.9067
4   spanish      4 702.8296  6.417206 690.2501 715.4090
5   english      5 596.2939  3.975929 588.5000 604.0878
6   spanish      5 721.3138  4.339772 712.8067 729.8210
7   english      6 602.0135  6.551799 589.1702 614.8568
8   spanish      6 739.7981  4.526026 730.9259 748.6703
9   english      7 607.7331 10.996944 586.1761 629.2901
10  spanish      7 758.2824  6.792232 744.9678 771.5970
11  english      8 613.4527 15.805952 582.4688 644.4366
12  spanish      8 776.7667  9.786834 757.5818 795.9515
13  english      9 619.1723 20.727087 578.5416 659.8030
14  spanish      9 795.2509 13.016452 769.7352 820.7667
plot(lale,                       # plot the effect of the interaction
   ylim=range(d$RT, na.rm=TRUE), # w/ these y-axis limits
   grid=TRUE)                    # & w/ a grid

And here a version with everything: observed values, predicted values (red for LANGUAGE: english, blue for LANGUAGE: spanish), and the confidence intervals of the predictions (I’m building this up very stepwise, more so than I might do in actual research):

d_split <- split(d, d$LANGUAGE)
lale_d_split <- split(lale_d, lale_d$LANGUAGE, drop=TRUE)
plot(main="RT ~ LANGUAGE:LENGTH", type="n", # plot nothing
   xlab="Length"  , x=d$LENGTH,             # x-axis
   ylab="RT in ms", y=d$RT); grid()         # y-axis & grid
   # plot observed data points for LANGUAGE being eng:
   points(jitter(d_split$eng$LENGTH), d_split$eng$RT, pch=16, col="#FF000010")
   lines(lale_d_split$eng$LENGTH, lale_d_split$eng$fit, col="red")
   # plot observed data points for LANGUAGE being spa:
   points(jitter(d_split$spa$LENGTH), d_split$spa$RT, pch=16, col="#0000FF10")
   lines(lale_d_split$spa$LENGTH, lale_d_split$spa$fit, col="blue")
   # plot confidence band for LANGUAGE being eng:
   polygon(col="#FF000030", border="red", # semi-transparent grey, for once w/ border
      x=c(   lale_d_split$eng$LENGTH,   # x-coordinates left to right, then
         rev(lale_d_split$eng$LENGTH)), # the same values back (in reverse order)
      y=c(   lale_d_split$eng$lower,    # y-coordinates lower boundary
         rev(lale_d_split$eng$upper)))  # y-coordinates upper boundary
   # plot confidence band for LANGUAGE being spa:
   polygon(col="#0000FF30", border="blue", # semi-transparent grey, for once w/ border
      x=c(   lale_d_split$spa$LENGTH,   # x-coordinates left to right, then
         rev(lale_d_split$spa$LENGTH)), # the same values back (in reverse order)
      y=c(   lale_d_split$spa$lower,    # y-coordinates lower boundary
         rev(lale_d_split$spa$upper)))  # y-coordinates upper boundary
   # plot legend
   legend(title="Language", bty="n",
      x=7.5, xjust=0.5, y=3750, yjust=0.5, ncol=2,
      legend=levels(d$LANGUAGE), fill=c("red", "blue"))

4.3 ZIPFFREQ:LENGTH

Let’s visualize the nature of the effect of ZIPFFREQ:LENGTH based on the predictions from effects:

(zile_d <- data.frame(   # make zile_d a data frame of
   zile <- effect(       # the effect
      "ZIPFFREQ:LENGTH", # of ZIPFFREQ:LENGTH
      m_final,           # in m_final
      xlevels=list(      # for when
      ZIPFFREQ=seq(3, 6, 0.25), # ZIPFFREQ is these values
      LENGTH=seq(3, 9, 0.5))))) # LENGTH is these values
    ZIPFFREQ LENGTH      fit        se    lower    upper
1       3.00    3.0 659.5172 20.098368 620.1190 698.9155
2       3.25    3.0 655.4455 17.376402 621.3831 689.5079
3       3.50    3.0 651.3738 14.738126 622.4830 680.2645
4       3.75    3.0 647.3020 12.237786 623.3126 671.2914
5       4.00    3.0 643.2303  9.979606 623.6676 662.7930
6       4.25    3.0 639.1585  8.166988 623.1490 655.1680
7       4.50    3.0 635.0868  7.147371 621.0760 649.0976
8       4.75    3.0 631.0151  7.262793 616.7780 645.2521
9       5.00    3.0 626.9433  8.466964 610.3458 643.5409
10      5.25    3.0 622.8716 10.387918 602.5085 643.2347
11      5.50    3.0 618.7999 12.704576 593.8954 643.7043
12      5.75    3.0 614.7281 15.237510 584.8585 644.5978
13      6.00    3.0 610.6564 17.895117 575.5771 645.7356
14      3.00    3.5 679.3512 16.590211 646.8299 711.8725
15      3.25    3.5 673.1256 14.340541 645.0143 701.2369
16      3.50    3.5 666.9000 12.156743 643.0695 690.7305
17      3.75    3.5 660.6745 10.081715 640.9116 680.4374
18      4.00    3.5 654.4489  8.198463 638.3777 670.5201
19      4.25    3.5 648.2233  6.671425 635.1456 661.3011
20      4.50    3.5 641.9978  5.789671 630.6485 653.3471
21      4.75    3.5 635.7722  5.852516 624.2997 647.2447
22      5.00    3.5 629.5467  6.833950 616.1503 642.9431
23      5.25    3.5 623.3211  8.418614 606.8183 639.8239
24      5.50    3.5 617.0955 10.332596 596.8409 637.3502
25      5.75    3.5 610.8700 12.424624 586.5144 635.2256
26      6.00    3.5 604.6444 14.618458 575.9883 633.3005
27      3.00    4.0 699.1851 13.368652 672.9789 725.3913
28      3.25    4.0 690.8057 11.551593 668.1614 713.4499
29      3.50    4.0 682.4263  9.784280 663.2465 701.6061
30      3.75    4.0 674.0469  8.099342 658.1700 689.9238
31      4.00    4.0 665.6675  6.560559 652.8071 678.5280
32      4.25    4.0 657.2882  5.296877 646.9048 667.6715
33      4.50    4.0 648.9088  4.543924 640.0014 657.8161
34      4.75    4.0 640.5294  4.562013 631.5866 649.4722
35      5.00    4.0 632.1500  5.343320 621.6756 642.6243
36      5.25    4.0 623.7706  6.623031 610.7877 636.7535
37      5.50    4.0 615.3912  8.170214 599.3754 631.4071
38      5.75    4.0 607.0118  9.859748 587.6841 626.3396
39      6.00    4.0 598.6325 11.629756 575.8350 621.4299
40      3.00    4.5 719.0190 10.695869 698.0522 739.9858
41      3.25    4.5 708.4858  9.236011 690.3807 726.5909
42      3.50    4.5 697.9526  7.813421 682.6362 713.2690
43      3.75    4.5 687.4194  6.452794 674.7702 700.0686
44      4.00    4.5 676.8862  5.202973 666.6869 687.0854
45      4.25    4.5 666.3530  4.164933 658.1886 674.5174
46      4.50    4.5 655.8198  3.530689 648.8986 662.7409
47      4.75    4.5 645.2865  3.525370 638.3759 652.1972
48      5.00    4.5 634.7533  4.151392 626.6155 642.8912
49      5.25    4.5 624.2201  5.184906 614.0563 634.3839
50      5.50    4.5 613.6869  6.432403 601.0777 626.2962
51      5.75    4.5 603.1537  7.791773 587.8797 618.4277
52      6.00    4.5 592.6205  9.213631 574.5593 610.6817
53      3.00    5.0 738.8529  9.070688 721.0719 756.6339
54      3.25    5.0 726.1659  7.825912 710.8250 741.5068
55      3.50    5.0 713.4789  6.613518 700.5146 726.4431
56      3.75    5.0 700.7918  5.455143 690.0983 711.4854
57      4.00    5.0 688.1048  4.393721 679.4919 696.7177
58      4.25    5.0 675.4178  3.518125 668.5213 682.3142
59      4.50    5.0 662.7307  2.995974 656.8578 668.6037
60      4.75    5.0 650.0437  3.016744 644.1301 655.9573
61      5.00    5.0 637.3567  3.570974 630.3566 644.3567
62      5.25    5.0 624.6696  4.464214 615.9186 633.4207
63      5.50    5.0 611.9826  5.534689 601.1331 622.8321
64      5.75    5.0 599.2956  6.697954 586.1658 612.4254
65      6.00    5.0 586.6085  7.913193 571.0965 602.1205
66      3.00    5.5 758.6869  9.074590 740.8982 776.4755
67      3.25    5.5 743.8460  7.827023 728.5029 759.1891
68      3.50    5.5 729.0051  6.618110 716.0319 741.9784
69      3.75    5.5 714.1643  5.473523 703.4347 724.8939
70      4.00    5.5 699.3234  4.443253 690.6135 708.0334
71      4.25    5.5 684.4826  3.626089 677.3745 691.5907
72      4.50    5.5 669.6417  3.190223 663.3880 675.8954
73      4.75    5.5 654.8009  3.290821 648.3500 661.2518
74      5.00    5.5 639.9600  3.886446 632.3415 647.5785
75      5.25    5.5 625.1191  4.796081 615.7175 634.5208
76      5.50    5.5 610.2783  5.875654 598.7604 621.7962
77      5.75    5.5 595.4374  7.047497 581.6224 609.2524
78      6.00    5.5 580.5966  8.272489 564.3803 596.8129
79      3.00    6.0 778.5208 10.705793 757.5345 799.5070
80      3.25    6.0 761.5261  9.238837 743.4155 779.6367
81      3.50    6.0 744.5314  7.825076 729.1922 759.8707
82      3.75    6.0 727.5367  6.499318 714.7963 740.2772
83      4.00    6.0 710.5421  5.327670 700.0984 720.9857
84      4.25    6.0 693.5474  4.434028 684.8555 702.2393
85      4.50    6.0 676.5527  4.008834 668.6943 684.4111
86      4.75    6.0 659.5580  4.196964 651.3308 667.7852
87      5.00    6.0 642.5633  4.928678 632.9018 652.2249
88      5.25    6.0 625.5687  6.008563 613.7902 637.3471
89      5.50    6.0 608.5740  7.283365 594.2966 622.8513
90      5.75    6.0 591.5793  8.667502 574.5886 608.5699
91      6.00    6.0 574.5846 10.116194 554.7541 594.4151
92      3.00    6.5 798.3547 13.381884 772.1226 824.5868
93      3.25    6.5 779.2062 11.555359 756.5546 801.8578
94      3.50    6.5 760.0577  9.799792 740.8475 779.2679
95      3.75    6.5 740.9092  8.161106 724.9112 756.9072
96      4.00    6.5 721.7607  6.725288 708.5773 734.9441
97      4.25    6.5 702.6122  5.649200 691.5382 713.6861
98      4.50    6.5 683.4637  5.162901 673.3430 693.5844
99      4.75    6.5 664.3152  5.427315 653.6762 674.9542
100     5.00    6.5 645.1667  6.349336 632.7203 657.6131
101     5.25    6.5 626.0182  7.696136 610.9317 641.1047
102     5.50    6.5 606.8697  9.284668 588.6692 625.0701
103     5.75    6.5 587.7212 11.010801 566.1370 609.3053
104     6.00    6.5 568.5727 12.819069 543.4438 593.7015
105     3.00    7.0 818.1886 16.605140 785.6381 850.7392
106     3.25    7.0 796.8863 14.344788 768.7666 825.0060
107     3.50    7.0 775.5840 12.174223 751.7192 799.4487
108     3.75    7.0 754.2816 10.151207 734.3825 774.1808
109     4.00    7.0 732.9793  8.383245 716.5459 749.4127
110     4.25    7.0 711.6770  7.064496 697.8287 725.5253
111     4.50    7.0 690.3747  6.475486 677.6810 703.0684
112     4.75    7.0 669.0723  6.808322 655.7262 682.4185
113     5.00    7.0 647.7700  7.948024 632.1897 663.3503
114     5.25    7.0 626.4677  9.611734 607.6261 645.3093
115     5.50    7.0 605.1654 11.575677 582.4739 627.8568
116     5.75    7.0 583.8630 13.711443 556.9849 610.7412
117     6.00    7.0 562.5607 15.950158 531.2941 593.8273
118     3.00    7.5 838.0225 20.114212 798.5933 877.4518
119     3.25    7.5 814.5664 17.380908 780.4951 848.6377
120     3.50    7.5 791.1102 14.756665 762.1832 820.0373
121     3.75    7.5 767.6541 12.311424 743.5204 791.7878
122     4.00    7.5 744.1979 10.175065 724.2521 764.1438
123     4.25    7.5 720.7418  8.581463 703.9198 737.5638
124     4.50    7.5 697.2856  7.867646 681.8629 712.7084
125     4.75    7.5 673.8295  8.264804 657.6282 690.0307
126     5.00    7.5 650.3733  9.636540 631.4831 669.2636
127     5.25    7.5 626.9172 11.643350 604.0931 649.7413
128     5.50    7.5 603.4610 14.015030 575.9878 630.9343
129     5.75    7.5 580.0049 16.595883 547.4725 612.5373
130     6.00    7.5 556.5487 19.302186 518.7112 594.3862
131     3.00    8.0 857.8565 23.782923 811.2355 904.4774
132     3.25    8.0 832.2465 20.554693 791.9537 872.5393
133     3.50    8.0 806.6365 17.455037 772.4199 840.8531
134     3.75    8.0 781.0265 14.566268 752.4727 809.5804
135     4.00    8.0 755.4166 12.041136 731.8127 779.0205
136     4.25    8.0 729.8066 10.154639 709.9008 749.7124
137     4.50    8.0 704.1966  9.303728 685.9588 722.4344
138     4.75    8.0 678.5866  9.763041 659.4484 697.7248
139     5.00    8.0 652.9767 11.374951 630.6787 675.2747
140     5.25    8.0 627.3667 13.739614 600.4333 654.3001
141     5.50    8.0 601.7567 16.537213 569.3393 634.1741
142     5.75    8.0 576.1468 19.583074 537.7586 614.5349
143     6.00    8.0 550.5368 22.777820 505.8861 595.1875
144     3.00    8.5 877.6904 27.547564 823.6897 931.6911
145     3.25    8.5 849.9266 23.811157 803.2503 896.6029
146     3.50    8.5 822.1628 20.222985 782.5203 861.8053
147     3.75    8.5 794.3990 16.877855 761.3138 827.4842
148     4.00    8.5 766.6352 13.951696 739.2861 793.9843
149     4.25    8.5 738.8714 11.761490 715.8157 761.9271
150     4.50    8.5 711.1076 10.766171 690.0030 732.2122
151     4.75    8.5 683.3438 11.286416 661.2194 705.4682
152     5.00    8.5 655.5800 13.143475 629.8152 681.3448
153     5.25    8.5 627.8162 15.874937 596.6970 658.9354
154     5.50    8.5 600.0524 19.109456 562.5927 637.5121
155     5.75    8.5 572.2886 22.632366 527.9231 616.6542
156     6.00    8.5 544.5248 26.328152 492.9145 596.1351
157     3.00    9.0 897.5243 31.373623 836.0235 959.0251
158     3.25    9.0 867.6067 27.120536 814.4431 920.7703
159     3.50    9.0 837.6891 23.035442 792.5334 882.8448
160     3.75    9.0 807.7715 19.225729 770.0838 845.4591
161     4.00    9.0 777.8538 15.890707 746.7037 809.0039
162     4.25    9.0 747.9362 13.389898 721.6884 774.1840
163     4.50    9.0 718.0186 12.245533 694.0140 742.0231
164     4.75    9.0 688.1010 12.825976 662.9586 713.2433
165     5.00    9.0 658.1833 14.931415 628.9137 687.4530
166     5.25    9.0 628.2657 18.035452 592.9114 663.6201
167     5.50    9.0 598.3481 21.713977 555.7828 640.9134
168     5.75    9.0 568.4305 25.721682 518.0090 618.8519
169     6.00    9.0 538.5129 29.926610 479.8486 597.1771
plot(zile,                       # plot the effect of the interaction
   ylim=range(d$RT, na.rm=TRUE), # w/ these y-axis limits
   grid=TRUE)                    # & w/ a grid

Not that great … There are two main ways we could try and plot this better. One would be with an actual 3-D plot. The simplest version of this might use rgl::plot3D like this, which would open a new window with a rotatable 3-D plot (but this wouldn’t be rendered into this HTML properly, which is why I am not executing it here):

rgl::plot3d(zile_d$ZIPFFREQ, zile_d$LENGTH, zile_d$fit)

Another option would be to use plotly::plot_ly:

library(plotly)
fig <- plot_ly(zile_d,
   x=~ZIPFFREQ, y=~LENGTH, z=~fit, color=~fit, colors=c("#FFFFFF20", "#00000020"))
fig <- fig %>% add_markers()
fig <- fig %>% layout(scene = list(
   xaxis = list(title = 'Zipf frequency'),
   yaxis = list(title = 'Length'),
   zaxis = list(title = 'Predicted RT')))
fig

While this is nice, it’s also hard to publish in print. Let’s do this differently with a numeric heatmap kind of plot that I often use. For that, the numeric predictions are first cut into 9 ordinal bins with lower/higher bin numbers reflecting lower/higher numeric predictions …

zile_d$fitcat <- as.numeric( # create a numeric column fitcat in zile_d
   cut(zile_d$fit,           # by cutting the predicted values up
       breaks=seq(           # at the values ranging from
          min(zile_d$fit),   # the smallest obs/pred RT to
          max(zile_d$fit),   # the largest obs/pred RT
          length.out=10),    # into 9 groups
       include.lowest=TRUE,  # include the lowest, and
       labels=1:9))          # use the numbers 1:9 as labels

… and then we plot them into a coordinate system:

plot(main="RT ~ ZIPFFREQ:LENGTH", type="n", # plot nothing, just
   xlab="Zipf frequency", xlim=range(zile_d$ZIPFFREQ), x=0, # get the coordinate
   ylab="Length"        , ylim=range(zile_d$LENGTH)  , y=0) # system set up
text(                                  # plot text
   x=zile_d$ZIPFFREQ, y=zile_d$LENGTH, # at these coordinates, namely
   labels=zile_d$fitcat,               # the 'categorical/ordinal' predictions
   # make the physical size of the numbers dependent on the values
   cex=0.5+zile_d$fitcat/7,
   # make the darkness of the grey reflect the predicted RT
   col=grey(level=zile_d$fitcat/10)) # see ?grey

However, there’s one thing that’s possibly problematic about this – what is that? The problem is that we are binning with cut on the basis of zile_d$fit (check the above code), but the range of those predictions is much smaller than the range of the actually observed reaction times in d$RT:

range(zile_d$fit)
[1] 538.5129 897.5243
range(d$RT)
[1]  271 4130

That in turn means the binning steps based on zile_d$fit will be based on much smaller differences between predicted values (of only 40, check levels(cut(zile_d$fit, breaks=seq(min(zile_d$fit), max(zile_d$fit), length.out=10)))), meaning a difference of predictions of only 81 ms could lead to a difference in 3 bins – if we were to bin based on the actual value range, that would not happen, because there the binning steps are 429 ms wide (check levels(cut(d$RT, breaks=seq(min(d$RT), max(d$RT), length.out=10)))). Thus, the above plot exaggerates the effects somewhat.

Accordingly, we determine the range of the values to be plotted in a potentially more representative way, by using

  • as a minimum, the minimum of the predictions and the actual values combined;
  • as a maximum, the maximum of the predictions and the actual values combined:
# define the minimum of the range to be used for binning
min_of_obs_n_pred <- min(c(
   d$RT,
   zile_d$fit),
   na.rm=TRUE)
# define the maximum of the range to be used for binning
max_of_obs_n_pred <- max(c(
   d$RT,
   zile_d$fit),
   na.rm=TRUE)

We can see that the initial plot indeed exaggerated the range: the upper plot showed prediction bins from 1 to 9, but we now see that the predicctions are actually only in a really small range of values.

zile_d$fitcat2 <- as.numeric( # create a numeric column fitcat2 in zile_d
   cut(zile_d$fit,           # by cutting the predicted values up
       breaks=seq(           # at the values ranging from
          min_of_obs_n_pred, # the smallest obs/pred RT to   <---------
          max_of_obs_n_pred, # the largest obs/pred RT       <---------
          length.out=10),    # into 9 groups
       include.lowest=TRUE,  # include the lowest, and
       labels=1:9)) # use the numbers 1:9 as labels
table(zile_d$fitcat, zile_d$fitcat2)

     1  2
  1 10  0
  2 28  0
  3 45  0
  4 30  0
  5  2 18
  6  0 16
  7  0 10
  8  0  6
  9  0  4

Thus, we plot this again (with some additional info in the form of the decile-based grid), which here doesn’t help much:

plot(main="RT ~ ZIPFFREQ:LENGTH", type="n", # plot nothing, just
   xlab="Zipf frequency", xlim=range(zile_d$ZIPFFREQ), x=0, # get the coordinate
   ylab="Length"        , ylim=range(zile_d$LENGTH)  , y=0) # system set up
text(                                  # plot text
   x=zile_d$ZIPFFREQ, y=zile_d$LENGTH, # at these coordinates, namely
   labels=zile_d$fitcat2,              # the 'categorical/ordinal' predictions
   # make the physical size of the numbers dependent on the values
   cex=0.5+zile_d$fitcat2/7,
   # make the darkness of the grey reflect the predicted RT
   col=grey(level=zile_d$fitcat2/15)) # see ?grey
abline(h=quantile(d$LENGTH  , probs=seq(0, 1, 0.1)), lty=3, col="#00000020")
abline(v=quantile(d$ZIPFFREQ, probs=seq(0, 1, 0.1)), lty=3, col="#00000020")

5 What if we restrict RT?

Given that we already noticed early on that the long tail of RT might cause problems, how about we do not include all its values in the analysis? Here, we run the model selection process again, but now on a subset of the data in d, which is delimited like this:

shortest.densest.region(d$RT, target=0.9)[3:4] # will not run on your computer
$`Recommended range (based in Eucl)`
lower upper
364.5 967.5

$<NA>
NULL
keep <- d$RT>364.5 & d$RT<967.5
hist(d$RT , breaks="FD", xlim=c(0, 4250))
abline(v=c(364.5, 967.5), col="green")

5.1 Exploration & preparation

Let’s see how the above exploration results change when we use the restricted response variable; note the use of with(..., { ...}):

# the predictor(s) w/ the response
with(d[keep,], {
plot(
   main="RT in ms as a function of frequency per million words",
   pch=16, col="#00000030",
   xlab="Zipf frequency", xlim=c(  3,    6), x=ZIPFFREQ,
   ylab="RT (in ms)"    , ylim=c(250, 1000), y=RT); grid()
abline(lm(RT ~ ZIPFFREQ), col="green", lwd=2)
plot(
   main="RT in ms as a function of length in characters",
   pch=16, col="#00000020",
   xlab="Length"    , xlim=c(  3,    9), x=jitter(LENGTH),
   ylab="RT (in ms)", ylim=c(250, 1000), y=RT); grid()
   abline(lm(RT ~ LENGTH), col="green", lwd=2)
qwe <- boxplot(
   main="RT in ms as a function of stimulus language",
   RT ~ LANGUAGE, notch=TRUE, outline=FALSE,
   xlab="Stimulus language",
   ylab="RT (in ms)"       , ylim=c(250, 1000)); grid()
qwe <- boxplot(
   main="RT in ms as a function of speaker group",
   RT ~ GROUP, notch=TRUE, outline=FALSE,
   xlab="Speaker group",
   ylab="RT (in ms)"   , ylim=c(250, 1000)); grid()
})

This does look much better!

5.2 Modeling & numerical interpretation

Let’s see whether/how the modeling process changes if we only look at the data points we decided to keep:

summary(m_11 <- lm(         # make/summarize the linear model m_11:
   RT ~ 1 +                 # RT ~ an overall intercept (1)
   # & these predictors & their pairwise interactions
   (ZIPFFREQ+LANGUAGE+GROUP+LENGTH)^2,
   data=d, subset=keep,     # those vars, but now in this subset
   na.action=na.exclude))   # skip cases with NA/missing data (good habit)

Call:
lm(formula = RT ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2,
    data = d, subset = keep, na.action = na.exclude)

Residuals:
    Min      1Q  Median      3Q     Max
-285.16  -93.81  -21.20   78.97  417.85

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                   650.7500    69.0224   9.428  < 2e-16 ***
ZIPFFREQ                      -23.8054    14.2470  -1.671   0.0948 .
LANGUAGEspanish                58.4942    32.7149   1.788   0.0738 .
GROUPheritage                  35.6065    31.3927   1.134   0.2567
LENGTH                          4.5039    13.1998   0.341   0.7330
ZIPFFREQ:LANGUAGEspanish        3.7245     5.8845   0.633   0.5268
ZIPFFREQ:GROUPheritage         -5.7045     5.6376  -1.012   0.3116
ZIPFFREQ:LENGTH                -0.2982     2.7435  -0.109   0.9134
LANGUAGEspanish:GROUPheritage -47.4752     6.2346  -7.615 2.99e-14 ***
LANGUAGEspanish:LENGTH          2.6481     3.2737   0.809   0.4186
GROUPheritage:LENGTH            3.7714     3.0322   1.244   0.2136
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 126.3 on 6966 degrees of freedom
Multiple R-squared:  0.0909,    Adjusted R-squared:  0.0896
F-statistic: 69.65 on 10 and 6966 DF,  p-value: < 2.2e-16
drop1(m_11,  # drop each droppable predictor at a time from m_11 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2
                  Df Sum of Sq       RSS   AIC F value   Pr(>F)
<none>                         111164722 67532
ZIPFFREQ:LANGUAGE  1      6393 111171115 67531  0.4006   0.5268
ZIPFFREQ:GROUP     1     16339 111181061 67532  1.0239   0.3116
ZIPFFREQ:LENGTH    1       189 111164911 67531  0.0118   0.9134
LANGUAGE:GROUP     1    925334 112090056 67588 57.9849 2.99e-14 ***
LANGUAGE:LENGTH    1     10442 111175164 67531  0.6544   0.4186
GROUP:LENGTH       1     24688 111189410 67532  1.5470   0.2136
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 2nd model with the ns 2-way interaction ZIPFFREQ:LENGTH deleted:

summary(m_12 <- update( # make m_12 an update of
   m_11, .~.            # m_11, namely all of it (.~.), but then
   - ZIPFFREQ:LENGTH))  # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH,
    data = d, subset = keep, na.action = na.exclude)

Residuals:
    Min      1Q  Median      3Q     Max
-285.37  -93.76  -21.19   78.96  417.81

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    657.569     28.786  22.843  < 2e-16 ***
ZIPFFREQ                       -25.258      4.935  -5.118 3.17e-07 ***
LANGUAGEspanish                 58.957     32.434   1.818   0.0691 .
GROUPheritage                   35.575     31.389   1.133   0.2571
LENGTH                           3.107      3.023   1.028   0.3041
ZIPFFREQ:LANGUAGEspanish         3.546      5.650   0.628   0.5303
ZIPFFREQ:GROUPheritage          -5.700      5.637  -1.011   0.3120
LANGUAGEspanish:GROUPheritage  -47.486      6.233  -7.618 2.92e-14 ***
LANGUAGEspanish:LENGTH           2.716      3.213   0.846   0.3978
GROUPheritage:LENGTH             3.774      3.032   1.245   0.2133
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 126.3 on 6967 degrees of freedom
Multiple R-squared:  0.0909,    Adjusted R-squared:  0.08973
F-statistic:  77.4 on 9 and 6967 DF,  p-value: < 2.2e-16
anova(m_11, m_12, # compare m_11 to m_12
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1   6966 111164722
2   6967 111164911 -1   -188.53 0.0118 0.9134
drop1(m_12,  # drop each droppable predictor at a time from m_12 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
                  Df Sum of Sq       RSS   AIC F value   Pr(>F)
<none>                         111164911 67531
ZIPFFREQ:LANGUAGE  1      6285 111171195 67529  0.3939   0.5303
ZIPFFREQ:GROUP     1     16315 111181225 67530  1.0225   0.3120
LANGUAGE:GROUP     1    925952 112090863 67586 58.0319 2.92e-14 ***
LANGUAGE:LENGTH    1     11407 111176318 67529  0.7149   0.3978
GROUP:LENGTH       1     24722 111189633 67530  1.5494   0.2133
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 3rd model with the ns interaction ZIPFFREQ:LANGUAGE deleted:

summary(m_13 <- update(  # make m_13 an update of
   m_12, .~.             # m_12, namely all of it (.~.), but then
   - ZIPFFREQ:LANGUAGE)) # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH, data = d,
    subset = keep, na.action = na.exclude)

Residuals:
    Min      1Q  Median      3Q     Max
-287.90  -93.71  -21.28   79.06  416.70

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    648.723     25.098  25.848  < 2e-16 ***
ZIPFFREQ                       -23.490      4.052  -5.797 7.05e-09 ***
LANGUAGEspanish                 76.288     17.013   4.484 7.44e-06 ***
GROUPheritage                   34.502     31.341   1.101    0.271
LENGTH                           3.239      3.016   1.074    0.283
ZIPFFREQ:GROUPheritage          -5.508      5.628  -0.979    0.328
LANGUAGEspanish:GROUPheritage  -47.532      6.233  -7.626 2.74e-14 ***
LANGUAGEspanish:LENGTH           2.536      3.200   0.793    0.428
GROUPheritage:LENGTH             3.807      3.031   1.256    0.209
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 126.3 on 6968 degrees of freedom
Multiple R-squared:  0.09085,   Adjusted R-squared:  0.08981
F-statistic: 87.04 on 8 and 6968 DF,  p-value: < 2.2e-16
anova(m_12, m_13, # compare m_12 to m_13
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:GROUP + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1   6967 111164911
2   6968 111171195 -1   -6284.8 0.3939 0.5303
drop1(m_13,  # drop each droppable predictor at a time from m_13 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
                Df Sum of Sq       RSS   AIC F value    Pr(>F)
<none>                       111171195 67529
ZIPFFREQ:GROUP   1     15278 111186473 67528  0.9576    0.3278
LANGUAGE:GROUP   1    927877 112099072 67585 58.1576 2.741e-14 ***
LANGUAGE:LENGTH  1     10025 111181220 67528  0.6283    0.4280
GROUP:LENGTH     1     25166 111196362 67528  1.5774    0.2092
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 4th model with the ns interaction LANGUAGE:LENGTH deleted:

summary(m_14 <- update( # make m_14 an update of
   m_13, .~.            # m_13, namely all of it (.~.), but then
   - LANGUAGE:LENGTH))  # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + GROUP:LENGTH, data = d, subset = keep, na.action = na.exclude)

Residuals:
    Min      1Q  Median      3Q     Max
-288.00  -93.78  -21.14   79.11  416.59

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    640.118     22.628  28.289  < 2e-16 ***
ZIPFFREQ                       -23.397      4.050  -5.776 7.96e-09 ***
LANGUAGEspanish                 89.298      4.476  19.952  < 2e-16 ***
GROUPheritage                   33.897     31.331   1.082   0.2793
LENGTH                           4.883      2.189   2.231   0.0257 *
ZIPFFREQ:GROUPheritage          -5.458      5.628  -0.970   0.3322
LANGUAGEspanish:GROUPheritage  -47.595      6.232  -7.637 2.52e-14 ***
GROUPheritage:LENGTH             3.887      3.030   1.283   0.1996
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 126.3 on 6969 degrees of freedom
Multiple R-squared:  0.09077,   Adjusted R-squared:  0.08985
F-statistic: 99.39 on 7 and 6969 DF,  p-value: < 2.2e-16
anova(m_13, m_14, # compare m_13 to m_14
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + GROUP:LENGTH
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1   6968 111171195
2   6969 111181220 -1    -10025 0.6283  0.428
drop1(m_14,  # drop each droppable predictor at a time from m_14 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + GROUP:LENGTH
               Df Sum of Sq       RSS   AIC F value   Pr(>F)
<none>                      111181220 67528
ZIPFFREQ:GROUP  1     15002 111196222 67526  0.9403   0.3322
LANGUAGE:GROUP  1    930487 112111707 67584 58.3243 2.52e-14 ***
GROUP:LENGTH    1     26256 111207477 67527  1.6458   0.1996
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 5th model with the ns interaction ZIPFFREQ:GROUP deleted:

summary(m_15 <- update( # make m_15 an update of
   m_14, .~.            # m_14, namely all of it (.~.), but then
   - ZIPFFREQ:GROUP))   # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + LANGUAGE:GROUP +
    GROUP:LENGTH, data = d, subset = keep, na.action = na.exclude)

Residuals:
    Min      1Q  Median      3Q     Max
-283.78  -93.65  -21.27   79.27  418.37

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    653.832     17.664  37.015  < 2e-16 ***
ZIPFFREQ                       -26.223      2.812  -9.325  < 2e-16 ***
LANGUAGEspanish                 89.324      4.475  19.959  < 2e-16 ***
GROUPheritage                    7.521     15.551   0.484   0.6286
LENGTH                           4.760      2.185   2.178   0.0294 *
LANGUAGEspanish:GROUPheritage  -47.523      6.232  -7.626 2.74e-14 ***
GROUPheritage:LENGTH             4.099      3.022   1.357   0.1749
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 126.3 on 6970 degrees of freedom
Multiple R-squared:  0.09065,   Adjusted R-squared:  0.08986
F-statistic: 115.8 on 6 and 6970 DF,  p-value: < 2.2e-16
anova(m_14, m_15, # compare m_14 to m_15
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:GROUP +
    LANGUAGE:GROUP + GROUP:LENGTH
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + LANGUAGE:GROUP +
    GROUP:LENGTH
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1   6969 111181220
2   6970 111196222 -1    -15002 0.9403 0.3322
drop1(m_15,  # drop each droppable predictor at a time from m_15 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + LANGUAGE:GROUP +
    GROUP:LENGTH
               Df Sum of Sq       RSS   AIC F value    Pr(>F)
<none>                      111196222 67526
ZIPFFREQ        1   1387245 112583468 67611 86.9553 < 2.2e-16 ***
LANGUAGE:GROUP  1    927823 112124045 67582 58.1578 2.741e-14 ***
GROUP:LENGTH    1     29362 111225584 67526  1.8405    0.1749
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 5th model with the ns interaction GROUP:LENGTH deleted:

summary(m_16 <- update( # make m_16 an update of
   m_15, .~.            # m_15, namely all of it (.~.), but then
   - GROUP:LENGTH))     # remove this interaction

Call:
lm(formula = RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + LANGUAGE:GROUP,
    data = d, subset = keep, na.action = na.exclude)

Residuals:
    Min      1Q  Median      3Q     Max
-284.54  -93.37  -21.62   78.96  418.28

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                    643.047     15.775  40.764  < 2e-16 ***
ZIPFFREQ                       -26.188      2.812  -9.312  < 2e-16 ***
LANGUAGEspanish                 88.347      4.417  20.000  < 2e-16 ***
GROUPheritage                   27.860      4.132   6.743 1.68e-11 ***
LENGTH                           6.898      1.513   4.558 5.25e-06 ***
LANGUAGEspanish:GROUPheritage  -45.640      6.075  -7.512 6.54e-14 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 126.3 on 6971 degrees of freedom
Multiple R-squared:  0.09041,   Adjusted R-squared:  0.08975
F-statistic: 138.6 on 5 and 6971 DF,  p-value: < 2.2e-16
anova(m_15, m_16, # compare m_15 to m_16
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + LANGUAGE:GROUP +
    GROUP:LENGTH
Model 2: RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + LANGUAGE:GROUP
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1   6970 111196222
2   6971 111225584 -1    -29362 1.8405 0.1749
drop1(m_16,  # drop each droppable predictor at a time from m_16 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RT ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + LANGUAGE:GROUP
               Df Sum of Sq       RSS   AIC F value    Pr(>F)
<none>                      111225584 67526
ZIPFFREQ        1   1383671 112609255 67611  86.721 < 2.2e-16 ***
LENGTH          1    331496 111557080 67545  20.776 5.250e-06 ***
LANGUAGE:GROUP  1    900428 112126012 67581  56.434 6.538e-14 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
m_final <- m_16; rm(m_16); invisible(gc())

We’re done so let’s compute the confidence intervals and generate nd_2:

Confint(m_final)
                                Estimate      2.5 %     97.5 %
(Intercept)                   643.046685 612.123251 673.970118
ZIPFFREQ                      -26.188476 -31.701275 -20.675677
LANGUAGEspanish                88.346655  79.687371  97.005939
GROUPheritage                  27.859734  19.760288  35.959180
LENGTH                          6.897995   3.931375   9.864614
LANGUAGEspanish:GROUPheritage -45.639666 -57.549240 -33.730092
nd_2 <- expand.grid(
   ZIPFFREQ=c(0, 1, mean(d$ZIPFFREQ)),
   LENGTH  =c(0, 1, mean(d$LENGTH)),
   GROUP   =levels(d$GROUP),
   LANGUAGE=levels(d$LANGUAGE))
nd_2 <- cbind(nd_2,
   predict(m_final, newdata=nd_2, interval="confidence"))
names(nd_2)[5:7] <- c("PRED", "PRED_LOW", "PRED_UPP")
head(nd_2)
  ZIPFFREQ LENGTH   GROUP LANGUAGE     PRED PRED_LOW PRED_UPP
1 0.000000      0 english  english 643.0467 612.1233 673.9701
2 1.000000      0 english  english 616.8582 590.5277 643.1888
3 4.609476      0 english  english 522.3315 506.5269 538.1362
4 0.000000      1 english  english 649.9447 620.5042 679.3851
5 1.000000      1 english  english 623.7562 599.1366 648.3758
6 4.609476      1 english  english 529.2295 516.1500 542.3091

5.3 Visual interpretation

5.3.1 ZIPFFREQ

Let’s visualize the nature of the effect of ZIPFFREQ based on the predictions from effects:

zpf_d <- data.frame( # make zpf_d a data frame of
   zpf <- effect(    # the effect
      "ZIPFFREQ",    # of ZIPFFREQ
      m_final,       # in m_final
      xlevels=list(ZIPFFREQ=seq(3, 6.2, 0.2))))
plot(zpf,             # plot the effect of the main effect
   ylim=c(250, 1000), # w/ these y-axis limits
   grid=TRUE)         # & w/ a grid

And here a version with everything: observed values, predicted values, and the confidence intervals of the predictions; again, I use with:

with(d[keep,], {
   plot(                               # generate a plot
      xlab="Zipf frequency", x=jitter(ZIPFFREQ), # ZIPFFREQ on x-axis
      ylab="RT in ms"      , y=jitter(RT),       # RT on y-axis
      pch=16, col="#00000030"); grid() # w/ filled circles & a grid
   lines(               # draw a line
      x=zpf_d$ZIPFFREQ, # using these x-coordinates
      y=zpf_d$fit,      # & these y-coordinates
      col="green")
   polygon(col="#00FF0020", border=NA, # use a semi-transparent red & no border
      x=c(   zpf_d$ZIPFFREQ,   # x-coordinates left to right, then
         rev(zpf_d$ZIPFFREQ)), # the same values back (in reverse order)
      y=c(   zpf_d$lower,      # y-coordinates lower boundary
         rev(zpf_d$upper)))    # y-coordinates upper boundary
   # and lines for both variables' means
   abline(h=mean(RT, na.rm=TRUE), col="purple"); abline(v=mean(ZIPFFREQ, na.rm=TRUE), col="purple")
})

5.3.2 LENGTH

Let’s visualize the nature of the effect of LENGTH based on the predictions from effects:

le_d <- data.frame( # make le_d a data frame of
   le <- effect(    # the effect
      "LENGTH",     # of LENGTH
      m_final,      # in m_final
      xlevels=list(LENGTH=3:9)))
plot(le,              # plot the effect of the main effect
   ylim=c(250, 1000), # w/ these y-axis limits
   grid=TRUE)         # & w/ a grid

And here a version with everything: observed values, predicted values, and the confidence intervals of the predictions; again, I use with:

with(d[keep,], {
   plot(                                 # generate a plot
      xlab="Length"  , x=jitter(LENGTH), # LENGTH on x-axis
      ylab="RT in ms", y=jitter(RT),     # RT on y-axis
      pch=16, col="#00000030"); grid()   # w/ filled circles & a grid
   lines(            # draw a line
      x=le_d$LENGTH, # using these x-coordinates
      y=le_d$fit,    # & these y-coordinates
      col="green")
   polygon(col="#0000FF20", border=NA, # use a semi-transparent red & no border
      x=c(   le_d$LENGTH,   # x-coordinates left to right, then
         rev(le_d$LENGTH)), # the same values back (in reverse order)
      y=c(   le_d$lower,    # y-coordinates lower boundary
         rev(le_d$upper)))  # y-coordinates upper boundary
   # and lines for both variables' means
   abline(h=mean(RT, na.rm=TRUE), col="purple"); abline(v=mean(LENGTH, na.rm=TRUE), col="purple")
})

5.3.3 LANGUAGE:GROUP

Let’s visualize the nature of the effect of LANGUAGE:GROUP based on the predictions from effects:

lagr_d <- data.frame(   # make lagr_d a data frame of
   lagr <- effect(      # the effect
      "LANGUAGE:GROUP", # of LANGUAGE:GROUP
      m_final))         # in m_final
plot(lagr,            # plot the effect of the interaction
   ylim=c(250, 1000), # w/ these y-axis limits
   grid=TRUE)         # & w/ a grid

plot(lagr,            # plot the effect of the interaction
   ylim=c(250, 1000), # w/ these y-axis limits
   x.var="GROUP",     # w/ this predictor on the x-axis
   grid=TRUE)         # & w/ a grid

And here a version (of the second of the two effects plots) with everything: observed values, predicted values, and the confidence intervals of the predictions:

# split data & predictions up by levels of LANGUAGE:
d_split <- split(d, d$LANGUAGE)
lagr_d_split <- split(lagr_d, lagr_d$LANGUAGE, drop=TRUE)
par(mfrow=c(1, 2))
stripchart(main="RT ~ GROUP for LANGUAGE='eng'", # stripchart w/ main heading
           pch=16, col="#00000018",              # filled semi-transparent grey circles
           d_split$eng$RT ~ d_split$eng$GROUP,   # data: RT ~ GROUP (for eng)
           xlab="Group", ylab="RT (in ms)",      # axis labels
           ylim=c(250, 1000), vertical=TRUE, # make the chart have this vertical y-axis
           method="jitter"); grid()          # jitter observations, add grid
# add predicted means:
points(seq(lagr_d_split$eng$fit), lagr_d_split$eng$fit, pch="X", col="red")
# add confidence intervals:
for (i in seq(lagr_d_split$eng$fit)) {  # as many arrows as needed
   arrows(i, lagr_d_split$eng$lower[i], # each from here
          i, lagr_d_split$eng$upper[i], # to here
          code=3, angle=90, col="red")  # both tips w/ 90 degrees
}
stripchart(main="RT ~ GROUP for LANGUAGE='span'", # stripchart w/ main heading
           pch=16, col="#00000018",               # filled semi-transparent grey circles
           d_split$spa$RT ~ d_split$spa$GROUP,    # data: RT ~ GROUP (for spa)
           xlab="Group", ylab="RT (in ms)",       # axis labels
           ylim=c(250, 1000), vertical=TRUE, # make the chart have this vertical y-axis
           method="jitter"); grid()          # jitter observations, add grid
# add predicted means:
points(seq(lagr_d_split$spa$fit), lagr_d_split$spa$fit, pch="X", col="blue")
# add confidence intervals:
for (i in seq(lagr_d_split$spa$fit)) {  # as many arrows as needed
   arrows(i, lagr_d_split$spa$lower[i], # each from here
          i, lagr_d_split$spa$upper[i], # to here
          code=3, angle=90, col="blue") # both tips w/ 90 degrees
}
par(mfrow=c(1, 1))

6 What if we log-transform RT?

Given that we already noticed early on that the long tail of RT might cause problems, how about we do not use its raw values in the analysis? Here, we run the model selection process again, but now on a log-transformed version of RT:

summary(d$RTlog <- log2(d$RT))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
  8.082   8.980   9.217   9.289   9.516  12.012 

6.1 Exploration & preparation

Let’s see how the above exploration results change when we use the logged (to the base of 2) response variable RTlog:

# the predictor(s) w/ the response
plot(
   main="RT in ms (log2ged) as a function of freq. pmw",
   pch=16, col="#00000030",
   xlab="Zipf frequency"    , xlim=c(3,  6), x=d$ZIPFFREQ,
   ylab="RT in ms (log2ged)", ylim=c(8, 12), y=d$RTlog); grid()
abline(lm(d$RTlog ~ d$ZIPFFREQ), col="green", lwd=2)

plot(
   main="RT in ms (log2ged) as a function of length in characters",
   pch=16, col="#00000020",
   xlab="Length"            , xlim=c(3,  9), x=jitter(d$LENGTH),
   ylab="RT in ms (log2ged)", ylim=c(8, 12), y=d$RTlog); grid()
   abline(lm(d$RTlog ~ d$LENGTH), col="green", lwd=2)

qwe <- boxplot(
   main="RT in ms (log2ged) as a function of stimulus language",
   d$RTlog ~ d$LANGUAGE, notch=TRUE, outline=FALSE,
   xlab="Stimulus language",
   ylab="RT in ms (log2ged)", ylim=c(8, 12)); grid()

qwe <- boxplot(
   main="RT in ms (log2ged) as a function of speaker group",
   d$RTlog ~ d$GROUP, notch=TRUE, outline=FALSE,
   xlab="Speaker group",
   ylab="RT in ms (log2ged)", ylim=c(8, 12)); grid()

This also looks much better!

6.2 Modeling & numerical interpretation

Let’s see whether/how the modeling process changes …

summary(m_21 <- lm(         # make/summarize the linear model m_21:
   RTlog ~ 1 +              # RTlog ~ an overall intercept (1)
   # & these predictors & their pairwise interactions
   (ZIPFFREQ+LANGUAGE+GROUP+LENGTH)^2,
   data=d,                  # those vars are in d now!
   na.action=na.exclude))   # skip cases with NA/missing data (good habit)

Call:
lm(formula = RTlog ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2,
    data = d, na.action = na.exclude)

Residuals:
     Min       1Q   Median       3Q      Max
-1.25135 -0.27954 -0.06143  0.20982  2.55535

Coefficients:
                               Estimate Std. Error t value Pr(>|t|)
(Intercept)                    9.007342   0.213938  42.103  < 2e-16 ***
ZIPFFREQ                       0.006682   0.043985   0.152  0.87925
LANGUAGEspanish                0.308147   0.103832   2.968  0.00301 **
GROUPheritage                  0.102615   0.098841   1.038  0.29922
LENGTH                         0.106338   0.040699   2.613  0.00900 **
ZIPFFREQ:LANGUAGEspanish      -0.010640   0.018610  -0.572  0.56751
ZIPFFREQ:GROUPheritage        -0.001619   0.017674  -0.092  0.92702
ZIPFFREQ:LENGTH               -0.019935   0.008436  -2.363  0.01814 *
LANGUAGEspanish:GROUPheritage -0.206348   0.019821 -10.410  < 2e-16 ***
LANGUAGEspanish:LENGTH         0.018155   0.010490   1.731  0.08355 .
GROUPheritage:LENGTH           0.001545   0.009436   0.164  0.86999
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.4234 on 7741 degrees of freedom
Multiple R-squared:  0.1227,    Adjusted R-squared:  0.1216
F-statistic: 108.3 on 10 and 7741 DF,  p-value: < 2.2e-16
drop1(m_21,  # drop each droppable predictor at a time from m_21 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RTlog ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2
                  Df Sum of Sq    RSS    AIC  F value  Pr(>F)
<none>                         1387.8 -13313
ZIPFFREQ:LANGUAGE  1    0.0586 1387.9 -13315   0.3269 0.56751
ZIPFFREQ:GROUP     1    0.0015 1387.8 -13315   0.0084 0.92702
ZIPFFREQ:LENGTH    1    1.0013 1388.8 -13310   5.5850 0.01814 *
LANGUAGE:GROUP     1   19.4296 1407.2 -13207 108.3753 < 2e-16 ***
LANGUAGE:LENGTH    1    0.5370 1388.3 -13312   2.9953 0.08355 .
GROUP:LENGTH       1    0.0048 1387.8 -13315   0.0268 0.86999
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 2nd model with the ns 2-way interaction ZIPFFREQ:GROUP deleted:

summary(m_22 <- update( # make m_22 an update of
   m_21, .~.            # m_21, namely all of it (.~.), but then
   - ZIPFFREQ:GROUP))   # remove this interaction

Call:
lm(formula = RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH,
    data = d, na.action = na.exclude)

Residuals:
     Min       1Q   Median       3Q      Max
-1.25164 -0.27940 -0.06149  0.20977  2.55564

Coefficients:
                               Estimate Std. Error t value Pr(>|t|)
(Intercept)                    9.011315   0.209483  43.017  < 2e-16 ***
ZIPFFREQ                       0.005864   0.043065   0.136  0.89170
LANGUAGEspanish                0.308391   0.103791   2.971  0.00297 **
GROUPheritage                  0.094727   0.048511   1.953  0.05089 .
LENGTH                         0.106293   0.040694   2.612  0.00902 **
ZIPFFREQ:LANGUAGEspanish      -0.010686   0.018602  -0.574  0.56567
ZIPFFREQ:LENGTH               -0.019933   0.008435  -2.363  0.01814 *
LANGUAGEspanish:GROUPheritage -0.206289   0.019810 -10.414  < 2e-16 ***
LANGUAGEspanish:LENGTH         0.018146   0.010489   1.730  0.08368 .
GROUPheritage:LENGTH           0.001621   0.009399   0.172  0.86311
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.4234 on 7742 degrees of freedom
Multiple R-squared:  0.1227,    Adjusted R-squared:  0.1217
F-statistic: 120.3 on 9 and 7742 DF,  p-value: < 2.2e-16
anova(m_21, m_22, # compare m_21 to m_22
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RTlog ~ 1 + (ZIPFFREQ + LANGUAGE + GROUP + LENGTH)^2
Model 2: RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
  Res.Df    RSS Df  Sum of Sq      F Pr(>F)
1   7741 1387.8
2   7742 1387.8 -1 -0.0015043 0.0084  0.927
drop1(m_22,  # drop each droppable predictor at a time from m_22 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
                  Df Sum of Sq    RSS    AIC  F value  Pr(>F)
<none>                         1387.8 -13315
ZIPFFREQ:LANGUAGE  1    0.0592 1387.9 -13317   0.3300 0.56567
ZIPFFREQ:LENGTH    1    1.0011 1388.8 -13312   5.5846 0.01814 *
LANGUAGE:GROUP     1   19.4391 1407.3 -13209 108.4418 < 2e-16 ***
LANGUAGE:LENGTH    1    0.5365 1388.3 -13314   2.9928 0.08368 .
GROUP:LENGTH       1    0.0053 1387.8 -13317   0.0297 0.86311
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 3rd model with the ns interaction GROUP:LENGTH deleted:

summary(m_23 <- update( # make m_23 an update of
   m_22, .~.            # m_22, namely all of it (.~.), but then
   - GROUP:LENGTH))     # remove this interaction

Call:
lm(formula = RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH, data = d,
    na.action = na.exclude)

Residuals:
     Min       1Q   Median       3Q      Max
-1.25124 -0.27896 -0.06132  0.20991  2.55518

Coefficients:
                               Estimate Std. Error t value Pr(>|t|)
(Intercept)                    9.006924   0.207917  43.320  < 2e-16 ***
ZIPFFREQ                       0.005935   0.043061   0.138  0.89038
LANGUAGEspanish                0.307637   0.103692   2.967  0.00302 **
GROUPheritage                  0.102765   0.013427   7.654 2.19e-14 ***
LENGTH                         0.107184   0.040362   2.656  0.00793 **
ZIPFFREQ:LANGUAGEspanish      -0.010645   0.018600  -0.572  0.56711
ZIPFFREQ:LENGTH               -0.019949   0.008434  -2.365  0.01804 *
LANGUAGEspanish:GROUPheritage -0.205486   0.019254 -10.673  < 2e-16 ***
LANGUAGEspanish:LENGTH         0.018175   0.010487   1.733  0.08313 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.4234 on 7743 degrees of freedom
Multiple R-squared:  0.1227,    Adjusted R-squared:  0.1218
F-statistic: 135.4 on 8 and 7743 DF,  p-value: < 2.2e-16
anova(m_22, m_23, # compare m_22 to m_23
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH + GROUP:LENGTH
Model 2: RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
  Res.Df    RSS Df  Sum of Sq      F Pr(>F)
1   7742 1387.8
2   7743 1387.8 -1 -0.0053296 0.0297 0.8631
drop1(m_23,  # drop each droppable predictor at a time from m_23 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
                  Df Sum of Sq    RSS    AIC  F value  Pr(>F)
<none>                         1387.8 -13317
ZIPFFREQ:LANGUAGE  1    0.0587 1387.9 -13319   0.3276 0.56711
ZIPFFREQ:LENGTH    1    1.0028 1388.8 -13314   5.5948 0.01804 *
LANGUAGE:GROUP     1   20.4159 1408.2 -13206 113.9055 < 2e-16 ***
LANGUAGE:LENGTH    1    0.5383 1388.4 -13316   3.0035 0.08313 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 4th model with the ns interaction ZIPFFREQ:LANGUAGE deleted:

summary(m_24 <- update(  # make m_24 an update of
   m_23, .~.             # m_23, namely all of it (.~.), but then
   - ZIPFFREQ:LANGUAGE)) # remove this interaction

Call:
lm(formula = RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP + LANGUAGE:LENGTH, data = d, na.action = na.exclude)

Residuals:
     Min       1Q   Median       3Q      Max
-1.24964 -0.27871 -0.06224  0.21043  2.55420

Coefficients:
                               Estimate Std. Error t value Pr(>|t|)
(Intercept)                    9.002136   0.207740  43.334  < 2e-16 ***
ZIPFFREQ                       0.007370   0.042986   0.171  0.86387
LANGUAGEspanish                0.257516   0.055524   4.638 3.58e-06 ***
GROUPheritage                  0.102783   0.013426   7.655 2.16e-14 ***
LENGTH                         0.113334   0.038903   2.913  0.00359 **
ZIPFFREQ:LENGTH               -0.021360   0.008065  -2.648  0.00811 **
LANGUAGEspanish:GROUPheritage -0.205373   0.019252 -10.668  < 2e-16 ***
LANGUAGEspanish:LENGTH         0.018385   0.010480   1.754  0.07943 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.4233 on 7744 degrees of freedom
Multiple R-squared:  0.1227,    Adjusted R-squared:  0.1219
F-statistic: 154.7 on 7 and 7744 DF,  p-value: < 2.2e-16
anova(m_23, m_24, # compare m_23 to m_24
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LANGUAGE +
    ZIPFFREQ:LENGTH + LANGUAGE:GROUP + LANGUAGE:LENGTH
Model 2: RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP + LANGUAGE:LENGTH
  Res.Df    RSS Df Sum of Sq      F Pr(>F)
1   7743 1387.8
2   7744 1387.9 -1 -0.058712 0.3276 0.5671
drop1(m_24,  # drop each droppable predictor at a time from m_24 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP + LANGUAGE:LENGTH
                Df Sum of Sq    RSS    AIC  F value    Pr(>F)
<none>                       1387.9 -13319
ZIPFFREQ:LENGTH  1    1.2570 1389.1 -13314   7.0135  0.008106 **
LANGUAGE:GROUP   1   20.3956 1408.3 -13208 113.8018 < 2.2e-16 ***
LANGUAGE:LENGTH  1    0.5515 1388.4 -13318   3.0773  0.079429 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Let’s fit the 5th model with the ns interaction LANGUAGE:LENGTH deleted:

summary(m_25 <- update( # make m_25 an update of
   m_24, .~.            # m_24, namely all of it (.~.), but then
   - LANGUAGE:LENGTH))  # remove this interaction

Call:
lm(formula = RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP, data = d, na.action = na.exclude)

Residuals:
     Min       1Q   Median       3Q      Max
-1.25176 -0.28001 -0.06169  0.20985  2.55813

Coefficients:
                               Estimate Std. Error t value Pr(>|t|)
(Intercept)                    8.858655   0.190986  46.384  < 2e-16 ***
ZIPFFREQ                       0.025065   0.041791   0.600  0.54868
LANGUAGEspanish                0.351787   0.013969  25.183  < 2e-16 ***
GROUPheritage                  0.102916   0.013428   7.664 2.02e-14 ***
LENGTH                         0.140802   0.035618   3.953 7.78e-05 ***
ZIPFFREQ:LENGTH               -0.024622   0.007849  -3.137  0.00171 **
LANGUAGEspanish:GROUPheritage -0.205548   0.019254 -10.676  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.4234 on 7745 degrees of freedom
Multiple R-squared:  0.1223,    Adjusted R-squared:  0.1216
F-statistic: 179.9 on 6 and 7745 DF,  p-value: < 2.2e-16
anova(m_24, m_25, # compare m_24 to m_25
   test="F")      # using an F-test
Analysis of Variance Table

Model 1: RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP + LANGUAGE:LENGTH
Model 2: RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP
  Res.Df    RSS Df Sum of Sq      F  Pr(>F)
1   7744 1387.9
2   7745 1388.4 -1  -0.55152 3.0773 0.07943 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
drop1(m_25,  # drop each droppable predictor at a time from m_25 &
   test="F") # test its significance w/ an F-test
Single term deletions

Model:
RTlog ~ ZIPFFREQ + LANGUAGE + GROUP + LENGTH + ZIPFFREQ:LENGTH +
    LANGUAGE:GROUP
                Df Sum of Sq    RSS    AIC  F value    Pr(>F)
<none>                       1388.4 -13318
ZIPFFREQ:LENGTH  1    1.7641 1390.2 -13310   9.8407  0.001713 **
LANGUAGE:GROUP   1   20.4310 1408.9 -13206 113.9686 < 2.2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
m_final <- m_25; rm(m_25); invisible(gc())

We’re done so let’s compute the confidence intervals and generate nd_3:

Confint(m_final)
                                 Estimate       2.5 %       97.5 %
(Intercept)                    8.85865512  8.48427034  9.233039908
ZIPFFREQ                       0.02506458 -0.05685703  0.106986178
LANGUAGEspanish                0.35178701  0.32440355  0.379170478
GROUPheritage                  0.10291597  0.07659329  0.129238645
LENGTH                         0.14080230  0.07098157  0.210623023
ZIPFFREQ:LENGTH               -0.02462245 -0.04000876 -0.009236148
LANGUAGEspanish:GROUPheritage -0.20554794 -0.24329094 -0.167804940
nd_3 <- expand.grid(
   ZIPFFREQ=c(0, 1, mean(d$ZIPFFREQ)),
   LENGTH  =c(0, 1, mean(d$LENGTH)),
   GROUP   =levels(d$GROUP),
   LANGUAGE=levels(d$LANGUAGE))
nd_3 <- cbind(nd_3,
   predict(m_final, newdata=nd, interval="confidence"))
names(nd_3)[5:7] <- c("PRED", "PRED_LOW", "PRED_UPP")
head(nd_3)
  ZIPFFREQ LENGTH   GROUP LANGUAGE     PRED PRED_LOW PRED_UPP
1 0.000000      0 english  english 8.858655 8.484270 9.233040
2 1.000000      0 english  english 8.883720 8.590329 9.177110
3 4.609476      0 english  english 8.974190 8.924049 9.024331
4 0.000000      1 english  english 8.999457 8.692806 9.306109
5 1.000000      1 english  english 8.999900 8.759431 9.240368
6 4.609476      1 english  english 9.001495 8.959931 9.043060

6.3 Visual interpretation

6.3.1 ZIPFFREQ:LENGTH

Let’s visualize the nature of the effect of ZIPFFREQ:LENGTH based on the predictions from effects.

(zile_d <- data.frame(   # make zile_d a data frame of
   zile <- effect(       # the effect
      "ZIPFFREQ:LENGTH", # of ZIPFFREQ:LENGTH
      m_final,           # in m_final
      xlevels=list(      # for when
      ZIPFFREQ=seq(3, 6, 0.25), # ZIPFFREQ is these values
      LENGTH=seq(3, 9, 0.5))))) # LENGTH is these values
    ZIPFFREQ LENGTH      fit          se    lower    upper
1       3.00    3.0 9.307404 0.032453063 9.243787 9.371021
2       3.25    3.0 9.295203 0.027964751 9.240385 9.350022
3       3.50    3.0 9.283003 0.023622250 9.236697 9.329309
4       3.75    3.0 9.270802 0.019523099 9.232531 9.309072
5       4.00    3.0 9.258601 0.015857159 9.227517 9.289686
6       4.25    3.0 9.246400 0.012996343 9.220924 9.271877
7       4.50    3.0 9.234200 0.011554987 9.211549 9.256851
8       4.75    3.0 9.221999 0.012053542 9.198371 9.245627
9       5.00    3.0 9.209798 0.014290394 9.181785 9.237811
10      5.25    3.0 9.197598 0.017615340 9.163067 9.232129
11      5.50    3.0 9.185397 0.021530035 9.143192 9.227602
12      5.75    3.0 9.173196 0.025767072 9.122686 9.223707
13      6.00    3.0 9.160996 0.030191040 9.101813 9.220178
14      3.00    3.5 9.340871 0.026993880 9.287956 9.393787
15      3.25    3.5 9.325593 0.023279198 9.279959 9.371226
16      3.50    3.5 9.310314 0.019679459 9.271737 9.348892
17      3.75    3.5 9.295036 0.016271132 9.263140 9.326932
18      4.00    3.5 9.279757 0.013203290 9.253875 9.305639
19      4.25    3.5 9.264479 0.010770912 9.243365 9.285593
20      4.50    3.5 9.249200 0.009476644 9.230624 9.267777
21      4.75    3.5 9.233922 0.009783126 9.214744 9.253099
22      5.00    3.5 9.218643 0.011563774 9.195975 9.241312
23      5.25    3.5 9.203365 0.014277281 9.175378 9.231352
24      5.50    3.5 9.188086 0.017494832 9.153792 9.222381
25      5.75    3.5 9.172808 0.020985855 9.131670 9.213946
26      6.00    3.5 9.157529 0.024634358 9.109239 9.205819
27      3.00    4.0 9.374339 0.022008271 9.331197 9.417481
28      3.25    4.0 9.355983 0.018997235 9.318743 9.393222
29      3.50    4.0 9.337626 0.016072837 9.306119 9.369133
30      3.75    4.0 9.319270 0.013292386 9.293213 9.345327
31      4.00    4.0 9.300914 0.010767969 9.279806 9.322022
32      4.25    4.0 9.282557 0.008724737 9.265455 9.299660
33      4.50    4.0 9.264201 0.007563276 9.249375 9.279027
34      4.75    4.0 9.245845 0.007693860 9.230763 9.260927
35      5.00    4.0 9.227488 0.009060797 9.209727 9.245250
36      5.25    4.0 9.209132 0.011221000 9.187136 9.231128
37      5.50    4.0 9.190776 0.013807023 9.163710 9.217841
38      5.75    4.0 9.172419 0.016621286 9.139837 9.205002
39      6.00    4.0 9.154063 0.019565547 9.115709 9.192417
40      3.00    4.5 9.407806 0.017896495 9.372724 9.442888
41      3.25    4.5 9.386372 0.015457971 9.356070 9.416674
42      3.50    4.5 9.364938 0.013083410 9.339291 9.390585
43      3.75    4.5 9.343504 0.010815026 9.322304 9.364704
44      4.00    4.5 9.322070 0.008735925 9.304945 9.339195
45      4.25    4.5 9.300636 0.007016436 9.286882 9.314390
46      4.50    4.5 9.279202 0.005975516 9.267488 9.290915
47      4.75    4.5 9.257768 0.005978764 9.246048 9.269488
48      5.00    4.5 9.236333 0.007024733 9.222563 9.250104
49      5.25    4.5 9.214899 0.008747030 9.197753 9.232046
50      5.50    4.5 9.193465 0.010827586 9.172240 9.214690
51      5.75    4.5 9.172031 0.013096759 9.146358 9.197704
52      6.00    4.5 9.150597 0.015471781 9.120268 9.180926
53      3.00    5.0 9.441274 0.015376322 9.411132 9.471416
54      3.25    5.0 9.416762 0.013269624 9.390750 9.442774
55      3.50    5.0 9.392250 0.011216046 9.370264 9.414236
56      3.75    5.0 9.367738 0.009251030 9.349604 9.385873
57      4.00    5.0 9.343226 0.007445033 9.328632 9.357820
58      4.25    5.0 9.318714 0.005944794 9.307061 9.330368
59      4.50    5.0 9.294202 0.005031673 9.284339 9.304066
60      4.75    5.0 9.269690 0.005035870 9.259819 9.279562
61      5.00    5.0 9.245178 0.005955446 9.233504 9.256853
62      5.25    5.0 9.220667 0.007459209 9.206044 9.235289
63      5.50    5.0 9.196155 0.009267003 9.177989 9.214320
64      5.75    5.0 9.171643 0.011232987 9.149623 9.193662
65      6.00    5.0 9.147131 0.013287127 9.121084 9.173177
66      3.00    5.5 9.474741 0.015257920 9.444832 9.504651
67      3.25    5.5 9.447152 0.013126198 9.421421 9.472882
68      3.50    5.5 9.419562 0.011054786 9.397891 9.441232
69      3.75    5.5 9.391972 0.009085031 9.374163 9.409781
70      4.00    5.5 9.364382 0.007299696 9.350073 9.378692
71      4.25    5.5 9.336793 0.005869550 9.325287 9.348299
72      4.50    5.5 9.309203 0.005102544 9.299201 9.319205
73      4.75    5.5 9.281613 0.005295146 9.271233 9.291993
74      5.00    5.5 9.254023 0.006360776 9.241555 9.266492
75      5.25    5.5 9.226434 0.007956044 9.210838 9.242030
76      5.50    5.5 9.198844 0.009826296 9.179582 9.218106
77      5.75    5.5 9.171254 0.011841952 9.148041 9.194468
78      6.00    5.5 9.143665 0.013940081 9.116338 9.170991
79      3.00    6.0 9.508209 0.017589860 9.473728 9.542690
80      3.25    6.0 9.477541 0.015086130 9.447968 9.507114
81      3.50    6.0 9.446874 0.012664969 9.422047 9.471700
82      3.75    6.0 9.416206 0.010384291 9.395850 9.436562
83      4.00    6.0 9.385539 0.008359877 9.369151 9.401926
84      4.25    6.0 9.354871 0.006823748 9.341495 9.368247
85      4.50    6.0 9.324204 0.006153168 9.312142 9.336265
86      4.75    6.0 9.293536 0.006616750 9.280565 9.306507
87      5.00    6.0 9.262868 0.008020178 9.247147 9.278590
88      5.25    6.0 9.232201 0.009974321 9.212649 9.251753
89      5.50    6.0 9.201533 0.012217727 9.177583 9.225483
90      5.75    6.0 9.170866 0.014617818 9.142211 9.199521
91      6.00    6.0 9.140198 0.017108778 9.106660 9.173736
92      3.00    6.5 9.541676 0.021592321 9.499349 9.584003
93      3.25    6.5 9.507931 0.018492314 9.471681 9.544181
94      3.50    6.5 9.474186 0.015504165 9.443793 9.504578
95      3.75    6.5 9.440440 0.012707034 9.415531 9.465349
96      4.00    6.5 9.406695 0.010258384 9.386586 9.426804
97      4.25    6.5 9.372950 0.008466185 9.356354 9.389546
98      4.50    6.5 9.339204 0.007797069 9.323920 9.354489
99      4.75    6.5 9.305459 0.008519884 9.288758 9.322160
100     5.00    6.5 9.271713 0.010346917 9.251431 9.291996
101     5.25    6.5 9.237968 0.012814253 9.212849 9.263088
102     5.50    6.5 9.204223 0.015621383 9.173601 9.234845
103     5.75    6.5 9.170477 0.018615216 9.133987 9.206968
104     6.00    6.5 9.136732 0.021718680 9.094158 9.179307
105     3.00    7.0 9.575144 0.026519421 9.523158 9.627129
106     3.25    7.0 9.538321 0.022702865 9.493817 9.582824
107     3.50    7.0 9.501497 0.019030012 9.464193 9.538801
108     3.75    7.0 9.464674 0.015602673 9.434089 9.495260
109     4.00    7.0 9.427851 0.012622450 9.403108 9.452595
110     4.25    7.0 9.391028 0.010478068 9.370488 9.411568
111     4.50    7.0 9.354205 0.009738295 9.335115 9.373294
112     4.75    7.0 9.317382 0.010698583 9.296410 9.338354
113     5.00    7.0 9.280559 0.012987139 9.255100 9.306017
114     5.25    7.0 9.243735 0.016045332 9.212282 9.275189
115     5.50    7.0 9.206912 0.019514620 9.168658 9.245166
116     5.75    7.0 9.170089 0.023211396 9.124588 9.215590
117     6.00    7.0 9.133266 0.027042526 9.080255 9.186277
118     3.00    7.5 9.608611 0.031946160 9.545988 9.671234
119     3.25    7.5 9.568710 0.027348759 9.515099 9.622321
120     3.50    7.5 9.528809 0.022927889 9.483864 9.573754
121     3.75    7.5 9.488908 0.018808444 9.452039 9.525778
122     4.00    7.5 9.449007 0.015236896 9.419139 9.478876
123     4.25    7.5 9.409106 0.012684810 9.384241 9.433972
124     4.50    7.5 9.369205 0.011831390 9.346013 9.392398
125     4.75    7.5 9.329304 0.013015189 9.303791 9.354818
126     5.00    7.5 9.289404 0.015784312 9.258462 9.320345
127     5.25    7.5 9.249503 0.019473824 9.211329 9.287677
128     5.50    7.5 9.209602 0.023656947 9.163228 9.255976
129     5.75    7.5 9.169701 0.028114203 9.114589 9.224812
130     6.00    7.5 9.129800 0.032733801 9.065633 9.193967
131     3.00    8.0 9.642079 0.037657148 9.568260 9.715897
132     3.25    8.0 9.599100 0.032242352 9.535896 9.662304
133     3.50    8.0 9.556121 0.027037371 9.503121 9.609122
134     3.75    8.0 9.513142 0.022190340 9.469643 9.556641
135     4.00    8.0 9.470164 0.017992905 9.434893 9.505435
136     4.25    8.0 9.427185 0.015000658 9.397779 9.456590
137     4.50    8.0 9.384206 0.014008443 9.356746 9.411666
138     4.75    8.0 9.341227 0.015407623 9.311024 9.371430
139     5.00    8.0 9.298249 0.018668018 9.261654 9.334843
140     5.25    8.0 9.255270 0.023011662 9.210161 9.300379
141     5.50    8.0 9.212291 0.027937787 9.157525 9.267057
142     5.75    8.0 9.169312 0.033188024 9.104255 9.234370
143     6.00    8.0 9.126333 0.038630448 9.050607 9.202060
144     3.00    8.5 9.675546 0.043540677 9.590195 9.760898
145     3.25    8.5 9.629490 0.037286244 9.556398 9.702581
146     3.50    8.5 9.583433 0.031275154 9.522125 9.644741
147     3.75    8.5 9.537376 0.025678869 9.487039 9.587714
148     4.00    8.5 9.491320 0.020834376 9.450479 9.532161
149     4.25    8.5 9.445263 0.017382057 9.411190 9.479337
150     4.50    8.5 9.399207 0.016235715 9.367380 9.431033
151     4.75    8.5 9.353150 0.017845414 9.318168 9.388132
152     5.00    8.5 9.307094 0.021603633 9.264745 9.349442
153     5.25    8.5 9.261037 0.026615276 9.208864 9.313210
154     5.50    8.5 9.214980 0.032302116 9.151660 9.278301
155     5.75    8.5 9.168924 0.038365066 9.093718 9.244130
156     6.00    8.5 9.122867 0.044651178 9.035339 9.210396
157     3.00    9.0 9.709014 0.049535306 9.611911 9.806116
158     3.25    9.0 9.659879 0.042426864 9.576711 9.743047
159     3.50    9.0 9.610745 0.035595444 9.540968 9.680522
160     3.75    9.0 9.561610 0.029235884 9.504300 9.618921
161     4.00    9.0 9.512476 0.023730628 9.465958 9.558995
162     4.25    9.0 9.463342 0.019805377 9.424518 9.502166
163     4.50    9.0 9.414207 0.018495073 9.377952 9.450463
164     4.75    9.0 9.365073 0.020312236 9.325255 9.404890
165     5.00    9.0 9.315939 0.024572559 9.267770 9.364107
166     5.25    9.0 9.266804 0.030261177 9.207484 9.326124
167     5.50    9.0 9.217670 0.036720178 9.145688 9.289651
168     5.75    9.0 9.168535 0.043608585 9.083051 9.254020
169     6.00    9.0 9.119401 0.050751850 9.019914 9.218888

First, with the 3-D plot:

fig <- plot_ly(zile_d,
   x=~ZIPFFREQ, y=~LENGTH, z=~fit, color=~fit, colors=c("#FFFFFF20", "#00000020"))
fig <- fig %>% add_markers()
fig <- fig %>% layout(scene = list(
   xaxis = list(title = 'Zipf frequency'),
   yaxis = list(title = 'Length'),
   zaxis = list(title = 'Predicted RT')))
fig

Second, with the numeric heatmap:

# define the minimum of the range to be used for binning
min_of_obs_n_pred <- min(c(d$RTlog, zile_d$fit), na.rm=TRUE) # <---------
# define the maximum of the range to be used for binning
max_of_obs_n_pred <- max(c(d$RTlog, zile_d$fit), na.rm=TRUE) # <---------
zile_d$fitcat2 <- as.numeric( # create a numeric column fitcat2 in zile_d
   cut(zile_d$fit,           # by cutting the predicted values up
       breaks=seq(           # at the values ranging from
          min_of_obs_n_pred, # the smallest obs/pred RT to
          max_of_obs_n_pred, # the largest obs/pred RT
          length.out=10),    # into 9 groups
       include.lowest=TRUE,  # include the lowest, and
       labels=1:9)) # use the numbers 1:9 as labels
plot(main="RT ~ ZIPFFREQ:LENGTH", type="n", # plot nothing, just
   xlab="Zipf frequency", xlim=range(zile_d$ZIPFFREQ), x=0, # get the coordinate
   ylab="Length"        , ylim=range(zile_d$LENGTH)  , y=0) # system set up
text(                                  # plot text
   x=zile_d$ZIPFFREQ, y=zile_d$LENGTH, # at these coordinates, namely
   labels=zile_d$fitcat2,              # the 'categorical/ordinal' predictions
   # make the physical size of the numbers dependent on the values
   cex=0.5+zile_d$fitcat2/7,
   # make the darkness of the grey reflect the predicted RT
   col=grey(level=zile_d$fitcat2/15)) # see ?grey
abline(h=quantile(d$LENGTH  , probs=seq(0, 1, 0.1)), lty=3, col="#00000020")
abline(v=quantile(d$ZIPFFREQ, probs=seq(0, 1, 0.1)), lty=3, col="#00000020")

6.3.2 LANGUAGE:GROUP

Let’s visualize the nature of the effect of LANGUAGE:GROUP based on the predictions from effects:

lagr_d <- data.frame(   # make lagr_d a data frame of
   lagr <- effect(      # the effect
      "LANGUAGE:GROUP", # of LANGUAGE:GROUP
      m_final))         # in m_final
plot(lagr,        # plot the effect of the interaction
   ylim=range(d$RTlog, na.rm=TRUE), # w/ these y-axis limits
   grid=TRUE)     # & w/ a grid

plot(lagr,        # plot the effect of the interaction
   ylim=range(d$RTlog, na.rm=TRUE), # w/ these y-axis limits
   x.var="GROUP", # w/ this predictor on the x-axis
   grid=TRUE)     # & w/ a grid

And here a version (of the second of the two effects plots) with everything: observed values, predicted values, and the confidence intervals of the predictions:

# split data & predictions up by levels of LANGUAGE:
d_split <- split(d, d$LANGUAGE)
lagr_d_split <- split(lagr_d, lagr_d$LANGUAGE, drop=TRUE)
par(mfrow=c(1, 2))
stripchart(main="RTlog ~ GROUP for LANGUAGE='eng'", # stripchart w/ main heading
   pch=16, col="#00000018",                         # filled semi-transparent grey circles
   d_split$eng$RTlog ~ d_split$eng$GROUP,           # data: RT ~ GROUP (for eng)
   xlab="Group", ylab="RT (in ms, log2ged)",        # axis labels
   ylim=c(8, 12), vertical=TRUE, # make the chart have this vertical y-axis
   method="jitter"); grid()      # jitter observations, add grid
# add predicted means:
points(seq(lagr_d_split$eng$fit), lagr_d_split$eng$fit, pch="X", col="red")
# add confidence intervals:
for (i in seq(lagr_d_split$eng$fit)) {  # as many arrows as needed
   arrows(i, lagr_d_split$eng$lower[i], # each from here
          i, lagr_d_split$eng$upper[i], # to here
          code=3, angle=90, col="red")  # both tips w 90 degrees
}
stripchart(main="RTlog ~ GROUP for LANGUAGE='span'", # stripchart w/ main heading
   pch=16, col="#00000018",                          # filled semi-transparent grey circles
   d_split$spa$RTlog ~ d_split$spa$GROUP,            # data: RT ~ GROUP (for spa)
   xlab="Group", ylab="RT (in ms, log2ged)",         # axis labels
   ylim=c(8, 12), vertical=TRUE, # make the chart have this vertical y-axis
   method="jitter"); grid()      # jitter observations, add grid
# add predicted means:
points(seq(lagr_d_split$spa$fit), lagr_d_split$spa$fit, pch="X", col="blue")
# add confidence intervals:
for (i in seq(lagr_d_split$spa$fit)) {  # as many arrows as needed
   arrows(i, lagr_d_split$spa$lower[i], # each from here
          i, lagr_d_split$spa$upper[i], # to here
          code=3, angle=90, col="blue")  # both tips w/ 90 degrees
}

par(mfrow=c(1, 1))

7 Write-up

To determine whether the reaction time to a word (in ms) varies as a function of

  • the Zipf frequency of that word (ZIPFFREQ);
  • the length of that word (LENGTH);
  • the language that word was presented in (LANGUAGE: english vs. spanish);
  • the speaker group that words was presented to (GROUP: english vs. heritage);
  • any pairwise interaction of these predictors?

a linear model selection process was undertaken. In order to deal with the very long right tail of the response variable observed in a first model selection process (using backwards stepwise model selection of all predictors and controls based on significance testing), the data set was reduced to 6977 (90%) of its original size (7752, after omitting 248 incomplete cases) by eliminating most of the long right tail (all reaction times >967.5) and some on the left tail (all reaction times <364.5).1 In this second analogous model selection process, an initial linear model was fit with the now reduced RT as the response variable and the above as predictors. The final model resulting from the elimination of ns predictors contained the effects of ZIPFFREQ and LANGUAGE:GROUP and was highly significant (F=167.5, df1=4, df2=6972, p<0.0001), but it explains only little of the response variable (mult. R2=0.088, adj. R2=0.087). The summary table of the model is shown here.

Estimate 95%-CI se t p2-tailed
Intercept (ZIPFFREQ=0, LENGTH=0, LANGUAGE=english, GROUP=english) 643.05 [612.12, 673.97] 15.78 40.76 <0.001
ZIPFFREQ0→1 -26.19 [-31.7, -20.68] 2.81 -9.31 <0.001
LANGUAGEenglishspanish 88.35 [79.69, 97.01] 4.42 20 <0.001
GROUPenglishheritage 27.86 [19.76, 35.96] 4.13 6.74 <0.001
LENGTH0→1 6.9 [3.93, 9.86] 1.51 4.56 <0.001
LANGUAGEenglishspanish + GROUPenglishheritage -45.64 [-57.55, -33.73] 6.08 -7.51 <0.001

The results indicate that

  • the main effect of ZIPFFREQ is negatively correlated with RT [show plot];
  • the main effect of LENGTH is positively correlated with RT [show plot];
  • the interaction LANGUAGE:GROUP has the following effects [show at least one plot]:
    • reaction times are always higher when LANGUAGE is spanish than when LANGUAGE is english, but that difference is higher when GROUP is english than when GROUP is spanish;
    • when LANGUAGE is english, heritage speakers need more time than learners, but when LANGUAGE is spanish, heritage speakers need less time than learners; all 4 means of LANGUAGE:GROUP are significantly different from each other (non-overlapping CIs).

A separate analysis that was conducted on the full data set with the response being log-transformed to the base of 2 yielded results that were conceptually similar to the analysis with the restricted response variable (and yielded a slightly higher R2): we found the same LANGUAGE:GROUP interaction, but then also a significant interaction of ZIPFFREQ:LENGTH, which showed that

  • the effect of LENGTH is stronger when ZIPFFREQ is low than when it is high;
  • the effect of ZIPFFREQ is stronger when LENGTH is low than when it is high.
sessionInfo()
R version 4.3.3 (2024-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
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] plotly_4.10.4   ggplot2_3.5.0   multcomp_1.4-25 TH.data_1.1-2
 [5] MASS_7.3-60.0.1 survival_3.5-8  mvtnorm_1.2-4   effects_4.2-2
 [9] car_3.1-2       carData_3.0-5   STGmisc_1.0     Rcpp_1.0.12
[13] magrittr_2.0.3

loaded via a namespace (and not attached):
 [1] gtable_0.3.4      xfun_0.43         htmlwidgets_1.6.4 insight_0.19.10
 [5] lattice_0.22-6    vctrs_0.6.5       tools_4.3.3       crosstalk_1.2.1
 [9] generics_0.1.3    sandwich_3.1-0    tibble_3.2.1      fansi_1.0.6
[13] pkgconfig_2.0.3   Matrix_1.6-5      data.table_1.15.4 lifecycle_1.0.4
[17] farver_2.1.1      munsell_0.5.1     mitools_2.4       codetools_0.2-20
[21] survey_4.4-2      htmltools_0.5.8.1 yaml_2.3.8        lazyeval_0.2.2
[25] pillar_1.9.0      nloptr_2.0.3      tidyr_1.3.1       boot_1.3-30
[29] abind_1.4-5       nlme_3.1-164      tidyselect_1.2.1  digest_0.6.35
[33] dplyr_1.1.4       purrr_1.0.2       splines_4.3.3     fastmap_1.1.1
[37] grid_4.3.3        colorspace_2.1-0  cli_3.6.2         utf8_1.2.4
[41] withr_3.0.0       scales_1.3.0      estimability_1.5  rmarkdown_2.26
[45] httr_1.4.7        nnet_7.3-19       lme4_1.1-35.2     zoo_1.8-12
[49] evaluate_0.23     knitr_1.46        viridisLite_0.4.2 rlang_1.1.3
[53] glue_1.7.0        DBI_1.2.2         rstudioapi_0.16.0 minqa_1.2.6
[57] jsonlite_1.8.8    R6_2.5.1         

Footnotes

  1. These boundary values were computed in such a way as to find the shortest interval of values that covers the largest number of values.↩︎