Area Under the Repeated Measures Curve

Zachary R. McCaw
Updated: 2026-10-06

Description

AURMC analyzes outcomes measured repeatedly across time when follow-up may be censored or end prematurely because of a terminal event such as death. It summarizes each group’s outcome experience via the area under its repeated-measures curve, capturing both the magnitude and duration of the outcome. This approach is useful when cumulative experience is more meaningful than the outcome at one time point. Examples may include days alive and out of hospital in heart failure, cumulative glycemic exposure based on serial blood glucose or HbA1c measurements in diabetes, and time alive in tumor response based on response status at successive oncology assessments. For methodological details and an application to days alive and out of hospital, see McCaw ZR, et al. (2026), “Using Days Alive and Out of Hospital as the Study Endpoint in Cardiovascular Heart Failure Clinical Trials”, JACC: Heart Failure, 14(7):103013.

Installation

devtools::install_github(repo = 'zrmacc/AURMC')

Methodological details

See the Supplemental Proofs.

Data

The function GenData simulates example data in the format expected by this package.

library(AURMC)
set.seed(101)
data <- AURMC::GenData(
  censoring_rate = 0.5,
  death_rate = 0.5,
  n = 25,
  tau = 4
)
head(data)
##   idx      time status      value
## 1   1 0.0000000      1  0.5524619
## 2   1 1.0000000      1 -0.6749438
## 3   1 2.0000000      1  0.2143595
## 4   1 2.3638814      0  0.3107692
## 5   2 0.0000000      1  0.9170283
## 6   2 0.6309311      2 -0.2232594

The columns are:

Note that:

Interpolation

The function Interpolate optionally performs linear interpolation between measurements to enable more precise area estimation. This step can be skipped if interpolation is not meaningful for the outcome of interest, for example the value remains constant between observations.

# Simple example data.
data <- data.frame(
  idx = c(1, 1, 2, 2),
  status = c(1, 0, 1, 2),
  time = c(0, 1, 0, 1),
  value = c(0, 1, 0, -1)
)
show(data)
##   idx status time value
## 1   1      1    0     0
## 2   1      0    1     1
## 3   2      1    0     0
## 4   2      2    1    -1
grid <- c(0, 0.5, 1.0)
interpolated <- AURMC::Interpolate(
  data = data,
  grid = grid
)
show(interpolated)
##   idx status time value
## 1   1      1  0.0   0.0
## 2   1      1  0.5   0.5
## 3   1      0  1.0   1.0
## 4   2      1  0.0   0.0
## 5   2      1  0.5  -0.5
## 6   2      2  1.0  -1.0

One-sample Problem

Case where the expected area is zero:

set.seed(101)
data <- AURMC::GenData(
  censoring_rate = 0.5,
  death_rate = 0.5,
  n = 25,
  tau = 4
)
AURMC::AURMC(data, tau = 2.0)
##       method tau         auc        se      lower     upper         p
## 1 asymptotic   2 0.009034877 0.2307179 -0.4431639 0.4612337 0.9687629

Case where the expected area is non-zero:

set.seed(101)
data <- AURMC::GenData(
  censoring_rate = 0.5,
  death_rate = 0.5,
  n = 25,
  tau = 4,
  value_mean = 1.0
)
AURMC::AURMC(data, tau = 2.0)
##       method tau      auc        se     lower    upper          p
## 1 asymptotic   2 1.234052 0.2420008 0.7597394 1.708365 3.4078e-07

Two-sample Problem

Case of no true difference:

set.seed(102)

# Reference arm.
data0 <- AURMC::GenData(
  censoring_rate = 0.5,
  death_rate = 0.5,
  n = 25,
  tau = 4,
  value_mean = 1.0
)
data0$arm <- 0

# Treatment arm.
data1 <- AURMC::GenData(
  censoring_rate = 0.5,
  death_rate = 0.5,
  n = 25,
  tau = 4,
  value_mean = 1.0
)
data1$arm <- 1
data1$idx <- max(data0$idx) + data1$idx

# Overall data set.
data <- rbind(data0, data1)

AURMC::CompareAURMCs(data)
## Arm 0:
##       method arm  tau  auc    se lower upper        p
## 1 asymptotic   0 2.69 1.02 0.239 0.552  1.49 1.95e-05
## 
## 
## Arm 1:
##       method arm  tau  auc    se lower upper       p
## 1 asymptotic   1 2.69 1.32 0.263   0.8  1.83 5.6e-07
## 
## 
## Contrast:
##       method  stat   est    se  lower upper     p
## 1 asymptotic A1-A0 0.295 0.355 -0.402 0.991 0.407
## 2 asymptotic A1/A0 1.290 0.397  0.705 2.360 0.410

Case of a true difference:

set.seed(102)

# Reference arm.
data0 <- AURMC::GenData(
  censoring_rate = 0.5,
  death_rate = 0.5,
  n = 25,
  tau = 4,
  value_mean = 1.0
)
data0$arm <- 0

# Treatment arm.
data1 <- AURMC::GenData(
  censoring_rate = 0.5,
  death_rate = 0.5,
  n = 25,
  tau = 4,
  value_mean = 2.0
)
data1$arm <- 1
data1$idx <- max(data0$idx) + data1$idx

# Overall data set.
data <- rbind(data0, data1)

AURMC::CompareAURMCs(data)
## Arm 0:
##       method arm  tau  auc    se lower upper        p
## 1 asymptotic   0 2.69 1.02 0.239 0.552  1.49 1.95e-05
## 
## 
## Arm 1:
##       method arm  tau  auc    se lower upper        p
## 1 asymptotic   1 2.69 2.43 0.413  1.62  3.24 3.99e-09
## 
## 
## Contrast:
##       method  stat  est    se lower upper       p
## 1 asymptotic A1-A0 1.41 0.477 0.473  2.34 0.00316
## 2 asymptotic A1/A0 2.38 0.688 1.350  4.19 0.00274