Eye–Voice Span in Rapid Automatized Naming: Pan et al. (2013)
Authors
Reinhold Kliegl
Jinger Pan
Published
2026-08-28
After working through this page you will be able to:
reproduce a covariate-based LMM analysis (gaze duration and eye–voice span as predictors of psychometric RAN) from a published study;
complexify a fixed-effect structure with continuous-by-categorical interactions and prune it with likelihood-ratio tests;
visualize higher-order interactions three ways — with partial effects (MixedModelsExtras.partial_fitted, the Julia analogue of R’s remef()), with marginal effects (Effects.jl), and with model diagnostics (MixedModelsMakie.jl).
Data used:pylsk13.rda, distributed alongside this page (examples/eyevoicespan/). The original R script (pylsk13_v4.R) and the paper (Pan_etal.DevSci.2013.pdf) are in the same folder.
1 Background
Pan et al. (2013) measured the eye movements of Chinese dyslexic and control children during rapid automatized naming (RAN) of digits (alphanumeric) and dice surfaces (symbolic). Both stimulus types require identical oral responses, which controls for effects associated with speech production.
The question addressed on this page is a psychometric one: how well do two eye-movement measures obtained from the computerized RAN task — gaze duration (gaze, the summed fixation time on an item) and the eye–voice span (evs, the distance in characters between the currently fixated item and the item currently being named) — predict naming speed (ran, seconds to name a card of items), and does that prediction differ between the two Conditions (digit/dice) and the two Groups (control/dyslexic)?
The analysis follows pylsk13_v4.R:
Setup — read the data, check whether the response needs a transformation, center the covariates.
Model building — start from an additive covariate model, add the Condition × Group interaction, then the full Condition × Group × evs × gaze factorial, and prune it back with likelihood-ratio tests.
Visualization — partial-effect plots (Figures 1 and 2 of the paper), marginal-effect plots, and residual diagnostics.
2 Packages
Code
usingAlgebraOfGraphicsusingAlgebraOfGraphics: density, linearusingBoxCoxusingCairoMakieusingCategoricalArraysusingDataFramesusingEffectsusingMixedModelsusingMixedModelsMakieusingMixedModelsExtras# partial_fitted(): the Julia analogue of remef()usingRData# to read the .rda fileusingStatisticsusingStatsBaseusingSMLP2026: fit_or_restoreconst progress =falseset_aog_theme!()
3 Read and prepare the data
pylsk13.rda contains a single data frame dat with one row per child × condition (56 children × 2 conditions = 112 rows).
dat =DataFrame(load(joinpath(@__DIR__, "pylsk13.rda"))["dat"])
112×8 DataFrame
87 rows omitted
Row
subj
group
condition
ran
gaze
evs
accuracy
fluency
Int32
Int32
Int32
Float64
Int32
Float64
Int32
Int32
1
101
1
1
13.79
242
1.33
130
1192
2
101
1
2
19.61
397
0.97
130
1192
3
102
1
1
13.28
318
0.84
119
1696
4
102
1
2
17.43
372
1.06
119
1696
5
103
1
1
22.66
403
0.83
132
1233
6
103
1
2
27.47
487
0.8
132
1233
7
104
1
1
13.85
295
1.28
127
1124
8
104
1
2
25.05
442
0.95
127
1124
9
105
1
1
12.75
281
1.15
103
922
10
105
1
2
26.17
412
0.89
103
922
11
107
1
1
16.36
303
1.13
124
1140
12
107
1
2
28.14
441
0.87
124
1140
13
108
1
1
15.33
323
1.25
139
1005
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
101
231
2
1
16.21
371
1.05
83
317
102
231
2
2
30.27
511
0.75
83
317
103
232
2
1
24.96
312
1.15
75
344
104
232
2
2
28.24
411
1.05
75
344
105
233
2
1
22.55
420
0.18
92
583
106
233
2
2
24.79
528
0.67
92
583
107
234
2
1
25.52
435
0.73
90
610
108
234
2
2
29.59
557
0.77
90
610
109
235
2
1
17.39
382
0.84
93
527
110
235
2
2
28.1
520
0.68
93
527
111
236
2
1
19.48
326
0.97
94
752
112
236
2
2
28.89
495
0.54
94
752
We follow the general recommendation to code the levels of grouping variables as strings (here S101, S102, …), to give factors meaningful level names, and to fix a sensible level order. ran is log-transformed (justified below) and the two covariates are centered (not scaled) so that the intercept and the lower-order terms are interpretable at the average gaze and evs.
The R script uses MASS::boxcox() to justify a log transform of ran (but not of gaze or evs). The Julia equivalent fits a BoxCoxTransformation to a mixed model of the untransformed response.
Figure 2: Mean log(RAN) by Condition and Group. Naming dice is slower than naming digits in both groups, and the group difference is larger for digits.
Figure 3: Comparative density of log(RAN) by Condition.
4 Linear mixed models
The two categorical predictors use treatment (dummy) contrasts with the same reference levels as the published analysis (digit and control), so the coefficients reproduce Table 3 of Pan et al. (2013).
Tip
If the intercept should estimate the grand mean rather than the digit/control cell — usually preferable in factorial designs — swap DummyCoding for EffectsCoding. The omnibus tests below are unchanged; only the meaning of the lower-order coefficients changes.
All models keep the maximal grouping structure supported by this design: a single by-child random intercept ((1 | Subj)). With only two observations per child there is no room for by-child random slopes.
m10 is the full Condition × Group × evs × gaze factorial. The three highest-order terms that involve the evs × gaze product are not significant, so we drop them: the Condition × Group × evs × gaze four-way, the Group × evs × gaze three-way, and the Condition × evs × gaze three-way. What remains (m09) is the model whose coefficients correspond to Table 3 of Pan et al. (2013). (The z statistics here differ slightly from the published t values because of software differences — ML vs. REML, contrast parameterization — but the substantive pattern is the same.)
m09 =fit_or_restore("eyevoicespan_m09.json", MixedModel,@formula(lran ~1+Condition+ Group + evs_c + gaze_c +Condition& Group +Condition& evs_c +Condition& gaze_c + Group & evs_c + Group & gaze_c + evs_c & gaze_c +Condition& Group & evs_c +Condition& Group & gaze_c + (1| Subj)), dat; contrasts, progress)
lran ~ 1 + Condition + Group + evs_c + gaze_c + Condition & Group + (1 | Subj)
8
144
10
1
0.0013
lran ~ 1 + Condition + Group + evs_c + gaze_c + Condition & Group + Condition & evs_c + Condition & gaze_c + Group & evs_c + Group & gaze_c + evs_c & gaze_c + Condition & Group & evs_c + Condition & Group & gaze_c + (1 | Subj)
15
160
16
7
0.0258
lran ~ 1 + Condition + Group + evs_c + gaze_c + Condition & Group + Condition & evs_c + Group & evs_c + Condition & gaze_c + Group & gaze_c + evs_c & gaze_c + Condition & Group & evs_c + Condition & Group & gaze_c + Condition & evs_c & gaze_c + Group & evs_c & gaze_c + Condition & Group & evs_c & gaze_c + (1 | Subj)
18
164
4
3
0.2197
Adding Condition × Group (m05) and the covariate interactions retained in m09 each improve the fit reliably; the extra terms in m10 do not. m09 is the preferred model.
4.2 The preferred model
VarCorr(m09)
Column
Variance
Std.Dev
Subj
(Intercept)
0.0096367
0.0981667
Residual
0.0074142
0.0861058
m09
Est.
SE
z
p
σ_Subj
(Intercept)
2.9984
0.0683
43.88
<1e-99
0.0982
Condition: dice
0.0770
0.0760
1.01
0.3108
Group: dyslexic
0.0117
0.0789
0.15
0.8825
evs_c
-0.6308
0.1440
-4.38
<1e-04
gaze_c
0.0036
0.0009
4.17
<1e-04
Condition: dice & Group: dyslexic
0.1299
0.0971
1.34
0.1807
Condition: dice & evs_c
0.6085
0.1998
3.05
0.0023
Condition: dice & gaze_c
-0.0013
0.0009
-1.41
0.1598
Group: dyslexic & evs_c
0.6163
0.1842
3.35
0.0008
Group: dyslexic & gaze_c
-0.0020
0.0011
-1.90
0.0569
evs_c & gaze_c
-0.0051
0.0015
-3.46
0.0006
Condition: dice & Group: dyslexic & evs_c
-0.5512
0.2010
-2.74
0.0061
Condition: dice & Group: dyslexic & gaze_c
0.0002
0.0011
0.18
0.8570
Residual
0.0861
The coefficients of interest:
evs_c (−0.63) — within digit/control, a larger eye–voice span predicts faster (lower log) naming.
Condition: dice & Group: dyslexic & evs_c (−0.55, p ≈ .006) — the three-way interaction: the EVS benefit is distributed differently across cells (Figure 1 of the paper).
evs_c & gaze_c (−0.005, p ≈ .001) — the two covariates interact.
Figure 5: Normal quantile plot of the m09 residuals.
Code
caterpillar(m09, :Subj)
Figure 6: Prediction intervals on the by-child random intercepts (m09).
6 Partial-effect plots (the remef() analogue)
The R script visualizes the higher-order interactions with partial effects computed by remef(): it takes the observed response, removes the contribution of the nuisance terms (here everything involving gaze and the random intercept), and keeps the terms of interest plus the residual. The result is an “adjusted” response that isolates one slice of the model.
MixedModelsExtras.partial_fitted is the Julia counterpart. It returns the fitted values for a chosen set of coefficients; adding the residuals back reproduces remef(..., keep=TRUE).
# keep every fixed effect that does NOT involve gaze; drop the by-child interceptkeep_evs =filter(c -> !occursin("gaze_c", c), coefnames(m09))# keep every fixed effect that does NOT involve evs; drop the by-child interceptkeep_gaze =filter(c -> !occursin("evs_c", c), coefnames(m09))dp =select(dat, :Subj, :Group, :Condition, :evs, :gaze, :lran)dp.partial_evs =partial_fitted(m09, keep_evs, Dict(:Subj =>String[]); mode=:include) .+residuals(m09)dp.partial_gaze =partial_fitted(m09, keep_gaze, Dict(:Subj =>String[]); mode=:include) .+residuals(m09)dp.cg =categorical(string.(dp.Condition, " / ", dp.Group))first(dp, 6)
6×9 DataFrame
Row
Subj
Group
Condition
evs
gaze
lran
partial_evs
partial_gaze
cg
Cat…
Cat…
Cat…
Float64
Int32
Float64
Float64
Float64
Cat…
1
S101
control
digit
1.33
242
2.62394
2.81295
2.52721
digit / control
2
S101
control
dice
0.97
397
2.97604
2.97662
2.96624
dice / control
3
S102
control
digit
0.84
318
2.58626
3.00638
2.68389
digit / control
4
S102
control
dice
1.06
372
2.85819
3.00322
2.93693
dice / control
5
S103
control
digit
0.83
403
3.1206
3.08125
3.05705
digit / control
6
S103
control
dice
0.8
487
3.31309
3.049
3.2433
dice / control
For plotting we stack the observed response and each partial response into long form, so that “observed vs. adjusted” becomes a facet column with a single shared legend.
remef(m09, fix = c(1, "condition:group:evs_c"), keep = TRUE, grouping = TRUE, ran = NULL) keeps the intercept, the three-way condition:group:evs_c term, and (because grouping = TRUE) every lower-order term built from condition, group, and evs_c; it removes everything involving gaze_c and all random effects. filter(c -> !occursin("gaze_c", c), coefnames(m09)) selects exactly that set of fixed-effect coefficients, and Dict(:Subj => String[]) drops the random intercept.
Figure 7: Left: observed log(RAN) vs. eye–voice span. Right: partial effect from m09 (gaze contribution and random intercept removed).
The partial plot sharpens the pattern noted in the paper: the EVS benefit (negative slope) is present for digit naming in the control group but is weak or absent in the dice / dyslexic cell.
Figure 8: Left: observed log(RAN) vs. gaze duration. Right: partial effect from m09 (EVS contribution and random intercept removed).
7 Marginal-effect plots with Effects.jl
partial_fitted adjusts observed data points. Effects.jl instead evaluates the model’s prediction on a regular grid, holding the variables that are not on the grid at a typical value (the mean; for the centered covariates that is 0). This is the Julia analogue of R’s effects / emmeans.
The Emotikon Project — model complexification and PCA of the random effects on a large dataset.
R’s remef and the partial_fitted docstring in MixedModelsExtras.
9 References
Pan, J., Yan, M., Laubrock, J., Shu, H., & Kliegl, R. (2013). Eye–voice span during rapid automatized naming of digits and dice in Chinese normal and dyslexic children. Developmental Science, 16(6), 967–979. https://doi.org/10.1111/desc.12075
9.1 Exercises
Contrast coding. Refit m09 with EffectsCoding for Condition and Group. Which coefficients change, which stay the same, and what does the intercept estimate now?
NoteSolution
The omnibus likelihood-ratio tests, VarCorr, the residual standard deviation, and the fitted values are all unchanged — the model space is the same. The intercept now estimates the grand mean (the unweighted average over the four Condition × Group cells) instead of the digit/control cell, and every lower-order Condition/Group coefficient becomes a deviation from that grand mean rather than a simple difference from a reference level. The highest-order interaction coefficient is unchanged up to a scale factor.
Partial vs. marginal.Figure 7 (partial effects) and Figure 9 (marginal effects) both show the EVS slopes by cell. What does each one add that the other does not?
NoteSolution
The partial-effect plot keeps the residuals, so it shows the scatter of individual observations around the adjusted regression line — useful for spotting influential points and for judging how much of the variance the term actually explains. The marginal-effect plot shows the model’s prediction with a proper confidence band and no leftover noise from other terms, which makes the estimated slopes and their uncertainty directly comparable across cells. Use partial effects to inspect the data given the model; use marginal effects to communicate the model.
Why center? The covariates were centered before fitting. What would change in the m09 coefficient table if evs and gaze entered on their raw scales, and would any of the omnibus tests change?
NoteSolution
Only the interpretation of the lower-order terms shifts. With raw covariates, Condition: dice would estimate the digit-vs-dice difference at evs = 0 and gaze = 0 — an extrapolation far outside the data — inflating its standard error and making it hard to interpret. The intercept would likewise refer to evs = gaze = 0. The higher-order interaction coefficients, the variance components, the fitted values, and every likelihood-ratio test are invariant to centering.
This page was rendered from git revision 29e8d33
using Quarto 1.10.18 and Julia 1.12.7.