6. Repeated-Measures Workflows

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  warning = FALSE,
  message = FALSE
)

Scope

Repeated-measures analysis requires a different data layout and, usually, a different estimand. The wide-data matrix workflow is not enough because the analysis must account for the repeated structure within subject.

This vignette covers:

Repeated-measures functions use a subject argument for the subject identifier role, including rmcorr(), ba_rm(), ccc_rm_ustat(), ccc_rm_reml(), and icc_rm_reml().

A common long-format dataset

library(matrixCorr)

set.seed(50)
n_id <- 14
n_time <- 4

dat <- expand.grid(
  id = factor(seq_len(n_id)),
  time = factor(seq_len(n_time)),
  method = factor(c("A", "B"))
)

dat$time_index <- as.integer(dat$time)

subj <- rnorm(n_id, sd = 1.0)[dat$id]
subject_method <- rnorm(n_id * 2, sd = 0.25)
sm <- subject_method[(as.integer(dat$id) - 1L) * 2L + as.integer(dat$method)]
subject_time <- rnorm(n_id * n_time, sd = 0.75)
st <- subject_time[(as.integer(dat$id) - 1L) * n_time + as.integer(dat$time)]

dat$y <- subj + sm + st + 0.35 * (dat$method == "B") +
  rnorm(nrow(dat), sd = 0.35)

Repeated-measures correlation

rmcorr() targets within-subject association, not agreement. It is the right function when the question is whether two responses vary together within subjects after removing subject-level offsets.

set.seed(51)
dat_rmcorr <- data.frame(
  id = rep(seq_len(n_id), each = n_time),
  x = rnorm(n_id * n_time),
  y = rnorm(n_id * n_time),
  z = rnorm(n_id * n_time)
)

dat_rmcorr$y <- 0.7 * dat_rmcorr$x +
  rnorm(n_id, sd = 1)[dat_rmcorr$id] +
  rnorm(nrow(dat_rmcorr), sd = 0.3)

fit_rmcorr <- rmcorr(dat_rmcorr, response = c("x", "y", "z"), subject = "id")
summary(fit_rmcorr)

Repeated-measures agreement

Agreement methods require method labels and, for paired repeated analysis, a time or replicate key.

ba_rm() is the repeated-measures Bland-Altman route. It models subject-time matched paired differences and returns bias and limits of agreement.

fit_ba_rm <- ba_rm(
  dat,
  response = "y",
  subject = "id",
  method = "method",
  time = "time_index"
)

summary(fit_ba_rm)

Repeated CCC

The package provides two repeated-measures CCC routes.

fit_ccc_ustat <- ccc_rm_ustat(
  dat,
  response = "y",
  subject = "id",
  method = "method",
  time = "time_index"
)

fit_ccc_reml <- ccc_rm_reml(
  dat,
  response = "y",
  subject = "id",
  method = "method",
  time = "time_index",
  ci = FALSE
)

summary(fit_ccc_ustat)
summary(fit_ccc_reml)

The two functions are related, but they are not interchangeable. The U-statistic route is useful when its design assumptions are appropriate. The REML route is the more flexible model-based path when variance components and residual structure need to be handled explicitly.

Repeated ICC

icc_rm_reml() uses the same REML and Woodbury backend as repeated CCC, but it targets a different reliability quantity.

fit_icc_cons <- icc_rm_reml(
  dat,
  response = "y",
  subject = "id",
  method = "method",
  time = "time_index",
  type = "consistency",
  ci = TRUE
)

fit_icc_agr <- icc_rm_reml(
  dat,
  response = "y",
  subject = "id",
  method = "method",
  time = "time_index",
  type = "agreement",
  ci = FALSE
)

summary(fit_icc_cons)
summary(fit_icc_agr)

The simulation above gives the subject-time component a visibly non-trivial variance contribution. That makes the CCC-versus-ICC distinction easier to see:

data.frame(
  method = c("Repeated CCC (REML)", "Repeated ICC (agreement, REML)"),
  estimate = c(
    fit_ccc_reml[1, 2],
    fit_icc_agr[1, 2]
  )
)

The most important distinction between repeated CCC and repeated ICC is the numerator. Repeated ICC uses only the stable between-subject variance in the numerator. Repeated CCC also credits the time-averaged subject-time component. As a result, the two summaries can differ materially even when they are fitted through the same backend.

Choosing among repeated-measures methods

The method choice should follow the scientific question.

The shared long-format interface is intentional. The statistical targets are different, but the package keeps the surrounding workflow stable.



Try the matrixCorr package in your browser

Any scripts or data that you put into this service are public.

matrixCorr documentation built on April 18, 2026, 5:06 p.m.