The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

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.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.