6. Repeated-Measures Workflows

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)
#> Correlation summary
#>   output      : matrix
#>   dimensions  : 3 x 3
#>   retained_pairs: 6
#>   threshold   : 0.0000
#>   diag        : included
#>   estimate    : -0.3995 to 1.0000
#> 
#>  item1 item2 estimate n_complete fisher_z
#>  x     y     0.9397   56         1.7356  
#>  y     z     -0.3995  56         -0.4230 
#>  x     z     -0.3843  56         -0.4051 
#> 
#> Strongest pairs by |estimate|
#> 
#>  item1 item2 estimate   n_complete fisher_z  
#>  x     y      0.9397160 56          1.7356154
#>  y     z     -0.3994915 56         -0.4230437
#>  x     z     -0.3843429 56         -0.4051453

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)
#> 
#> Bland-Altman (two methods) (95% CI)
#> 
#> Agreement estimates
#> 
#>  item1    item2    n_obs bias  sd_loa loa_low loa_up width
#>  method 1 method 2 56    0.654 0.651  -0.622  1.93   2.552
#> 
#> Confidence intervals
#> 
#>  bias_lwr bias_upr lo_lwr lo_upr up_lwr up_upr
#>  0.483    0.824    -0.793 -0.452 1.759  2.101 
#> 
#> Model details
#> 
#>  sigma2_subject sigma2_resid residual_model
#>  0              0.424        iid

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)
#> Lin's concordance summary
#>   method      : Repeated-measures Lin's concordance
#>   dimensions  : 2 x 2
#>   pairs       : 1
#>   estimate    : 0.7180
#>   most_negative: A-B (0.7180)
#>   most_positive: A-B (0.7180)
#> 
#> Strongest pairs by |estimate|
#> 
#>  item1 item2 estimate
#>  A     B     0.718
summary(fit_ccc_reml)
#> 
#> Repeated-measures concordance (REML)
#> 
#> Concordance estimates
#> 
#>  item1 item2 estimate n_subjects n_obs SB     se_ccc
#>  A     B     0.6542   14         112   0.2047 0.0607
#> 
#> Variance components
#> 
#>  sigma2_subject sigma2_subject_method sigma2_subject_time sigma2_error
#>  0.4785         0.0997                0.5943              0.1085      
#> 
#> AR(1) diagnostics
#> 
#>  ar1_rho ar1_rho_lag1 ar1_rho_mom ar1_pairs ar1_pval use_ar1 ar1_recommend
#>  -0.3941 -0.3941      -0.3941     84        3e-04    FALSE   FALSE

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)
#> 
#> Repeated-measures intraclass correlation (REML) (95% CI)
#> 
#> ICC estimates
#> 
#>  item1 item2 estimate lwr  upr  n_subjects n_obs se_icc residual_model
#>  A     B     0.6347   0.63 0.64 14         112   0.0022 iid           
#> 
#> Variance components
#> 
#>  sigma2_subject sigma2_subject_method sigma2_subject_time sigma2_error SB    
#>  0.4785         0.0997                0.5943              0.1085       0.2047
#> 
#> AR(1) diagnostics
#> 
#>  ar1_rho_lag1 ar1_rho_mom ar1_pairs ar1_pval use_ar1 ar1_recommend
#>  -0.3941      -0.3941     84        3e-04    FALSE   FALSE
summary(fit_icc_agr)
#> 
#> Repeated-measures intraclass correlation (REML)
#> 
#> ICC estimates
#> 
#>  item1 item2 estimate n_subjects n_obs se_icc residual_model
#>  A     B     0.4992   14         112   0.0463 iid           
#> 
#> Variance components
#> 
#>  sigma2_subject sigma2_subject_method sigma2_subject_time sigma2_error SB    
#>  0.4785         0.0997                0.5943              0.1085       0.2047
#> 
#> AR(1) diagnostics
#> 
#>  ar1_rho_lag1 ar1_rho_mom ar1_pairs ar1_pval use_ar1 ar1_recommend
#>  -0.3941      -0.3941     84        3e-04    FALSE   FALSE

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]
  )
)
#>                           method  estimate
#> 1            Repeated CCC (REML) 0.6541643
#> 2 Repeated ICC (agreement, REML) 0.4991620

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.