Most of the time you never think about the engine, the software
that does the fitting. The default just works, and every example in the
other articles uses it. This article is for when you want to know what
that default is doing, or you have a reason to switch. The
engine argument of icc() chooses the engine.
The engine estimates the variance components, each a
share of the total variation traced to one source. Some engine choices
are purely computational: the same estimator, a different solver. Others
compute a different estimator of the same estimand, the true quantity you are
trying to estimate. Those estimators are asymptotically equivalent: each
converges to the same answer as the sample grows. A third kind of choice
moves to a fully Bayesian fit. This article covers the mixed-model
engines (glmmTMB, lme4), the structural-equation engine (lavaan), and
the Bayesian engine (brms). It also says when the distinction matters.
Any unfamiliar term is defined in the Glossary.
The mixed-model engines: glmmTMB and lme4
By default icc() fits the variance components with
glmmTMB. You can instead request lme4
with engine = "lme4" for the random two-way design, where
each rater is tracked across the subjects they score. The lme4 package
itself arrives with the installation, because glmmTMB names lme4 in its
own Imports:. But the engine needs one piece more:
merDeriv. It supplies the parameter covariance behind
the default Monte-Carlo
interval, an interval built by drawing parameter values from the
fitted model’s uncertainty. Every lme4 fit checks for merDeriv on entry,
whatever interval method you ask for. merDeriv sits in
Suggests: and is fetched only on request. So a plain
install leaves you with the lme4 package but not the lme4 engine. Both
engines are REML mixed-model fits of
the same model. REML, restricted maximum likelihood, is a way to
estimate variances that corrects maximum likelihood’s downward bias. On
a given dataset the two engines therefore return the same coefficients
to numerical tolerance. The choice is about the fitting backend, not the
estimand.
glmmtmb <- tidy(icc(ratings, score, subject, rater, engine = "glmmTMB", seed = 1))
lme4 <- tidy(icc(ratings, score, subject, rater, engine = "lme4", seed = 1))| Point estimates on the ratings data by engine | ||
| Coefficient | glmmTMB | lme4 |
|---|---|---|
| ICC(A,1) | 0.2898 | 0.2898 |
| ICC(A,k) | 0.6201 | 0.6201 |
| ICC(C,1) | 0.7148 | 0.7148 |
| ICC(C,k) | 0.9093 | 0.9093 |
The two point estimates agree to well within rounding, and their
Monte-Carlo intervals coincide to about 0.01. The lme4
interval is built from the parameter covariance the
merDeriv package supplies. It is transformed onto the
same log scale glmmTMB uses, which is boundary-aware: an
estimate can land exactly at zero, and the interval still behaves.
glmmTMB remains the recommended default. It is the engine this package
declares in Imports:, and it is robust when a variance
component sits exactly at that zero boundary. At the boundary the lme4
route cannot form an interval, and directs you back to glmmTMB. lme4
otherwise has full design parity with glmmTMB. It covers the fixed-rater
design, where the observed raters are the whole population of interest,
and every multilevel design. It does so on balanced and
incomplete or ragged data. It degrades to glmmTMB only at that variance
boundary.
A structural-equation engine (lavaan)
engine = "lavaan" fits the same design as a
structural equation model (SEM), for the random two-way
design. The model is a common-factor generalizability model in the sense
of Jorgensen (2021). Unlike lme4, this is not only a different backend
for the same estimator. It matters which coefficient you ask
for.
glmmtmb <- tidy(icc(ratings, score, subject, rater, engine = "glmmTMB", seed = 1))
lavaan <- tidy(icc(ratings, score, subject, rater, engine = "lavaan", seed = 1))| Point estimates on the ratings data: mixed model and SEM | ||
| Coefficient | glmmTMB (mixed model) | lavaan (SEM) |
|---|---|---|
| ICC(A,1) | 0.2898 | 0.2843 |
| ICC(A,k) | 0.6201 | 0.6137 |
| ICC(C,1) | 0.7148 | 0.7148 |
| ICC(C,k) | 0.9093 | 0.9093 |
Consistency coefficients, where raters agree apart
from a constant offset per rater, are a ratio of the subject and
residual variances. So the SEM returns them identically to the mixed
model. Absolute agreement, where raters give the same
score, is different. The SEM has no random rater effect to estimate. A
rater is a single column, so its effect lives in the column
means. Following Jorgensen (2021), the rater variance is
recovered from the mean structure as the variance of the estimated
indicator intercepts. This indicator-mean
estimator reads the rater variance from the estimated
column means. It is a different estimator of the rater variance than the
mixed model’s random effect. The two are asymptotically equivalent: they
converge to the same answer as the sample grows. They match conventional
generalizability-theory software (GENOVA, and formerly the
gtheory package) closely on real data [Vispoel et
al. 2022]. But on a small design they differ by a modest amount. Here
ICC(A,1) is about 0.284 from lavaan versus
0.290 from the mixed model. The gap here comes from the raw
variance of only four estimated rater means. That variance carries
small-sample noise the mixed model shrinks away.
Which is “right”? Neither is wrong: they are two defensible
estimators of the same population quantity. Use "glmmTMB"
(the default) if you want the mixed-model random-rater estimate and its
wider, generalize-to-new-raters interval. Reach for
"lavaan" if you work inside an SEM generalizability-theory
workflow and want results comparable to that literature. The SEM engine
covers the random and fixed-rater two-way design. It
covers complete and incomplete data, the latter by FIML, which uses every observed value
rather than dropping incomplete cases. The parametric bootstrap,
which refits the model on simulated data many times, is available on
complete data, and Monte-Carlo throughout. It also covers the crossed
(Design 1) multilevel design as a two-level SEM. There
the subject level is reliability within a cluster, and the cluster level
is reliability of cluster means. That fit reports the subject-level ICC
and the cluster-level
ICC off one five-component fit. That route takes random raters, on
balanced data and on incomplete or unbalanced data alike, and gives a
Monte-Carlo interval. Two-level SEM estimation is full-information ML
with no REML analog. So with few clusters its cluster-level components
sit slightly below the REML estimates, and its agreement rater term
slightly above. Both are documented differences that shrink as the
cluster count grows. Consistency ICCs agree essentially exactly. One-way
designs are still directed to the mixed-model engines.
A Bayesian engine (brms)
engine = "brms" fits the variance components in a fully
Bayesian framework (Stan, via the brms
package). This is the approach ten Hove, Jorgensen & van der Ark
(2020) developed for interrater reliability. Instead of a single REML
point with a Monte-Carlo interval, it samples the posterior of every
variance component and reads the ICC off the draws. The point estimate
is the posterior
mode (MAP), the peak of the posterior distribution.
The interval is a credible
interval, which holds the share of the posterior probability that
the confidence level sets. The Confidence-interval
methods article covers it. Because it samples a Stan model,
this engine is slower than the others. It also needs the
brms package, an optional Suggests dependency.
And it places a prior, the
distribution placed on a parameter before seeing the data, on each
random-effect standard deviation. The last section of this article
covers that prior.
The examples below are shown with pre-computed output. Fitting a Stan
model needs a toolchain not available when this site is built, so these
chunks are not evaluated at knit time. What they show is
icc()’s own printed output. On a design this small the
sampler may also report divergent transitions. Whether it does depends
on the seed and the platform, so those warnings are not reproduced
here.
bayes <- icc(ratings, score, subject, rater, engine = "brms", type = "agreement", seed = 1)
bayes#> ── Intraclass correlation: two-way random, absolute agreement ──────────────────
#> Subjects: 6 | Raters: 4 (random) | Observations: 24 of 24 cells (complete)
#> Engine: brms (MCMC) | CI: 95% posterior credible (4000 draws)
#>
#> index estimate 95% CI
#> ICC(A,1) 0.241 [0.066, 0.649]
#> ICC(A,k) 0.679 [0.221, 0.881]
#>
#> Variance components: subject 1.522, rater 2.653, residual 0.962
#> Shrout & Fleiss equivalent: ICC(A,1) = ICC(2,1), ICC(A,k) = ICC(2,k)
The header names a brms (MCMC) engine and a
posterior credible interval, and
ci_method = "posterior" is automatic. On this tiny
six-subject design the MAP ICC(A,1) is about
0.24, a little below the glmmTMB REML value of
0.29. The MAP is the mode of a wide, right-skewed
posterior, which the small sample pulls down. That pull is what ten Hove
et al. (2020) note at small rater counts. Sampler settings pass through
brm_args: chains, iterations, backend, and parallel
cores, as in
brm_args = list(chains = 4, cores = 4).
The prior, and overriding it
By default the engine places a weakly-informative
half-t(4, 0, 1) prior on every random-effect
standard deviation. That is the sourced prior (ten Hove et
al. 2020, §3.3/§4.1), and every coverage result in this package depends
on it. You can supply your own prior for prior-sensitivity or
method-comparison work with the prior argument, which takes
any brms prior object. But icc() warns loudly. Leaving the
sourced prior voids the coverage guarantees, and a
poorly chosen SD prior can worsen the small-sample boundary
behavior:
library(brms)
icc(ratings, score, subject, rater, engine = "brms",
prior = set_prior("normal(0, 0.1)", class = "sd"), seed = 1)
#> Warning message:
#> Using a custom `prior` instead of the sourced half-t(4, 0, 1).
#> ! This VOIDS the package's coverage guarantees: the credible-interval coverage
#> results (ten Hove et al. 2020) hold only for the sourced prior.
#> ℹ A vague or flat SD prior can WORSEN small-`k` boundary bias. The half-t is
#> weakly informative on purpose (Principle #3's regime).
#> ℹ Leave `prior` unset for the sourced default unless you are running
#> prior-sensitivity or method-comparison work.Here the deliberately over-tight normal(0, 0.1) prior
squeezes every standard deviation toward zero. The ICC collapses to
nearly nothing. It is a vivid reminder that the prior is load-bearing,
not a casual knob. Leave prior unset unless you
specifically intend to depart from the sourced default. The brms engine
covers the same design family as the mixed-model engines: two-way random
and fixed, one-way, and the multilevel designs. It does so on balanced
and incomplete data.
