hce package introThe purpose of the package is simulate and analyze hierarchical composite endpoints (HCEs). Win odds is the main analysis method, but other win statistics (win ratio, net benefit) are implemented as well in case of no censoring. The power and sample size calculation are based on Brunner-Munzel theorem (Brunner and Munzel 2000) with the default value being based on the Noether’s formula (Noether 1987). For the review of the ideas of designing HCEs in clinical trials see Samvel B. Gasparyan et al. (2022), also Samvel B. Gasparyan et al. (2023) and Little et al. (2023). The Basic Data Structure conforming to the Analysis Data Model (Consortium 2009) principles for hierarchical composite endpoints is described in Samvel B. Gasparyan et al. (2024). For visualization of the HCE the maraca plot (Karpefors, Lindholm, and Gasparyan 2023) can be used.
Load the package hce and check the version
For citing the package run citation("hce") (Samvel B. Gasparyan 2024).
List the functions and the datasets in the package
ls("package:hce")
#> [1] "ADET" "ADLB" "ADSL" "COVID19" "COVID19b" "HCE1"
#> [7] "HCE2" "HCE3" "HCE4" "KHCE" "as_hce" "calcWINS"
#> [13] "calcWO" "hce" "minWO" "powerWO" "propWINS" "regWO"
#> [19] "simHCE" "sizeWO" "sizeWR" "stratWO" "summaryWO"In brief, the package contains the following:
data(package = "hce") for the list of all
datasets included in the package.Simulated datasets - HCE1 - HCE4 that contain two
treatment groups and analysis values AVAL of an
hierarchical composite endpoint.
The datasets COVID19, COVID19b of
COVID-19 ordinal scale outcomes (Beigel et al.
2020).
The datasets ADET (event-time), ADLB
(laboratory), ADSL (subject-level baseline characteristics)
datasets, correspondingly, for kidney events and their timing, kidney
related laboratory measurements of eGFR (estimated glomerular filtration
rate), and, based on this, derived kidney hierarchical composite
endpoint dataset KHCE for the same patients (Heerspink et al. 2023).
hce objects -
hce(), as_hce(), simHCE() (see Samvel B. Gasparyan et al. (2024)).calcWO(), summaryWO()
(see Samvel B. Gasparyan, Folkvaljon, et al.
(2021), Samvel B. Gasparyan, Kowalewski,
et al. (2021)).gamma) and their confidence intervals calculation function
- calcWINS(). Back-calculation of number of wins, losses,
and ties given the win odds and win ratio using the function
propWINS().regWO().stratWO().powerWO(), sizeWO(), minWO() (see Samvel B. Gasparyan, Kowalewski, et al. (2021),
Samvel B. Gasparyan, Kowalewski, and Koch
(2022)). Win ratio sample size calculation formula
sizeWR() (Yu and Ganju 2022).
By default uses the Noether’s formula (Noether
1987).hce_results objects,
generated by functions powerWO(), sizeWO(), minWO().hce objectshce() functionThe main objects in the package are called hce objects
which are data frames with a specific structure, matching the design of
hierarchical composite endpoints (HCE), which are complex
endpoints combining into a composite events of different clinical
importance using a hierarchy for prioritizing in the analysis the
clinically most important event of a patient. These endpoints are
implemented in clinical trials in different therapeutic areas. See for
example, Samvel B. Gasparyan et al. (2022)
for an implementation in COVID-19 setting and some practical
considerations for constructing hierarchical composite endpoints, for
the Chronic Kidney Disease (CKD) setting see (Little et al. 2023; Heerspink et al. 2023).
HCE are ordinal endpoints that can be thought of as having ‘greater’, ‘less’ or ‘equal’ defined for them but without having the definition by how much greater or less. In this sense the ordinal outcomes can be represented as numeric vectors as long as numeric operations (e.g. sum or division) are not performed on them.
hce objects can be constructed using the helper function
hce() which has three arguments
args("hce")
#> function (GROUP = character(), TRTP = character(), AVAL0 = 0,
#> ORD = sort(unique(GROUP)))
#> NULLWe see that the required arguments are GROUP, which
specifies clinically the most important event of a patient to be
included in the analysis and TRTP which specifies the
(planned) treatment group of a patient (exactly two treatment groups
should be present). Note that
hce structure assumes that only one event per
patient is present for the analysis, meaning that the resulting
hce object created by the hce() function is a
patient-level dataset. The function hce() does not do a
selection of the clinically most important event of the patient but
requires it to be already done when calling it.
argument TRTP should have exactly two
levels.
Consider the following example of ordinal outcomes ‘I’, ‘II’, and ‘III’:
set.seed(2022)
n <- 100
dat <- hce(GROUP = rep(x = c("I", "II", "III"), each = 100),
TRTP = sample(x = c("Active", "Control"), size = n*3, replace = TRUE))
class(dat)
#> [1] "hce" "data.frame"This dataset has the appropriate structure of hce
objects, but its class inherits from an object of class
data.frame. Meaning that all functions available for data
frame can be applied to hce objects, for example the
function head()
head(dat)
#> TRTP GROUP GROUPN AVAL AVAL0 ORD
#> 1 Control I 0 0 0 1
#> 2 Active I 0 0 0 1
#> 3 Control I 0 0 0 1
#> 4 Active I 0 0 0 1
#> 5 Active I 0 0 0 1
#> 6 Control I 0 0 0 1We see that the dataset has a very specific structure. The column
GROUPN shows how the function hce() generated
the order of given events (it uses usual alphabetic order for the unique
values in GROUP column to determine the clinical importance
of events)
In the class hce the higher values for the ordering mean
clinically less important event. For example, death which is the most
important event always should get the lowest ordinal value. If there is
a need to specify the order of outcomes, then the argument
ORD can be used
set.seed(2022)
n <- 100
dat <- hce(GROUP = rep(x = c("I", "II", "III"), each = 100),
TRTP = sample(x = c("A", "P"), size = n*3, replace = TRUE), ORD = c("III", "II", "I"))
unique(dat[, c("GROUP", "GROUPN")])
#> GROUP GROUPN
#> 1 I 2
#> 101 II 1
#> 201 III 0This means that the clinically most important event is ‘III’ instead
of ‘I’. The argument AVAL0 is meant to help in the cases
where we want to introduce sub-ordering within each GROUP
category. For example, if two events in the group ‘I’ can be compared
based on other parameters, then AVAL0 argument can be
specified to take that into account.
Below we use the built-in data frame HCE1 to construct
an hce object. Before specifying the order of events it is
a good idea to check what are the unique events included in the
GROUP column
Therefore, we can construct the following hce object
HCE <- hce(GROUP = HCE1$GROUP, TRTP = HCE1$TRTP, AVAL0 = HCE1$AVAL0,
ORD = c("TTE1", "TTE2", "TTE3", "TTE4", "C"))
class(HCE)
#> [1] "hce" "data.frame"
head(HCE)
#> TRTP GROUP GROUPN AVAL AVAL0 ORD
#> 1 A C 40000 39997.79 -2.21 10000
#> 2 P C 40000 40017.06 17.06 10000
#> 3 A TTE4 30000 30966.00 966.00 10000
#> 4 P TTE4 30000 30352.00 352.00 10000
#> 5 A C 40000 39987.55 -12.45 10000
#> 6 P C 40000 40044.62 44.62 10000hce object from a data frameConsider the dataset HCE1 which is part of the package
hce
data(HCE1, package = "hce")
class(HCE1)
#> [1] "data.frame"
head(HCE1)
#> SUBJID GROUP GROUPN AVAL0 AVAL TRTP
#> 1 1 C 40000 -2.21 39997.79 A
#> 2 2 C 40000 17.06 40017.06 P
#> 3 3 TTE4 30000 966.00 30966.00 A
#> 4 4 TTE4 30000 352.00 30352.00 P
#> 5 5 C 40000 -12.45 39987.55 A
#> 6 6 C 40000 44.62 40044.62 PThis dataset has the appropriate structure of hce
objects, but its class is data.frame. A generic way of
coercing data structures to an hce object is to use the
function as_hce() which, for given data structure, will
perform checks (using internal validator function) and create an
hce object from it (using an internal constructor
function), if possible or will throw an error explaining the issue.
dat1 <- as_hce(HCE2)
str(dat1)
#> Classes 'hce' and 'data.frame': 1000 obs. of 6 variables:
#> $ SUBJID: int 1 2 3 4 5 6 7 8 9 10 ...
#> $ GROUP : chr "TTE1" "C" "C" "TTE1" ...
#> $ GROUPN: int 0 40000 40000 0 10000 20000 40000 10000 0 40000 ...
#> $ AVAL0 : num 120 3.35 22.8 577 782 ...
#> $ AVAL : num 120 40003 40023 577 10782 ...
#> $ TRTP : chr "A" "P" "A" "P" ...hce objects using simHCE()To simulate values from a hierarchical composite endpoint we use the
function simHCE(), which has the following arguments
args("simHCE")
#> function (n, n0 = n, TTE_A, TTE_P, CM_A, CM_P, CSD_A = 1, CSD_P = 1,
#> fixedfy = 1, yeardays = 360, pat = 100, shape = 1, logC = FALSE,
#> seed = NULL)
#> NULLThe vector arguments TTE_A and TTE_P
show the event rates per year for time-to-event outcomes in the active
and control groups, respectively. The function assumes Weibull survival
function with the same shape parameter for simulating all time-to-event
outcomes in both treatment groups (by default shape = 1
hence assumes exponential survival function). These two vectors should
have the same length and their length shows the number of time-to-event
outcomes, which can be arbitrary.
By default, the event rates are presented per 100 patient-years
(pat = 100), which can be changed using the argument
pat. The function simulates event times in days, hence
yeardays = 360 argument can be used to change the number of
days in a year (for example, 365 or 365.25 can be used).
The function simulates events during a fixed-follow-up time only,
hence argument fixedfy can be used to change the length of
the follow-up (in years).
The function simulates the continuous outcome from a normal
(default) or log-normal (if logC = TRUE) distribution with
given means and standard deviations for two treatment groups.
Rates_A <- c(1.72, 1.74, 0.58, 1.5, 1)
Rates_P <- c(2.47, 2.24, 2.9, 4, 6)
dat3 <- simHCE(n = 2500, n0 = 1500, TTE_A = Rates_A,
TTE_P = Rates_P,
CM_A = -3, CM_P = -6,
CSD_A = 16, CSD_P = 15,
fixedfy = 3, seed = 2023)class(dat3)
#> [1] "hce" "data.frame"
head(dat3)
#> ID TRTP GROUP GROUPN AVALT AVAL0 AVAL seed PADY
#> 1 1 A C 6480 1080 24.14 6564.04 2023 1080
#> 2 2 A C 6480 1080 0.08 6539.98 2023 1080
#> 3 3 A C 6480 1080 -16.89 6523.01 2023 1080
#> 4 4 A C 6480 1080 -6.86 6533.04 2023 1080
#> 5 5 A TTE2 2160 390 390.00 2550.00 2023 1080
#> 6 6 A C 6480 1080 6.32 6546.22 2023 1080hce objectsAs we see, it creates an object of type hce which
inherits from the built-in class data.frame. We can check
all implemented methods for this new class as follows:
methods(class = "hce")
#> [1] calcWINS calcWO summaryWO
#> see '?methods' for accessing help and source codeThe function calcWO() calculates the win odds and its
confidence interval, while summaryWO() provides more
detailed calculation of win odds, providing also the number of wins,
losses, and ties by GROUP categories.
HCE <- hce(GROUP = HCE3$GROUP, TRTP = HCE3$TRTP,
ORD = c("TTE1", "TTE2", "TTE3", "TTE4", "C"))
calcWO(HCE)
#> WO LCL UCL SE WOnull alpha Pvalue WP
#> 1 1.333635 1.160574 1.532502 0.07091636 1 0.05 3.852522e-05 0.571484
#> SE_WP SD_WP N
#> 1 0.01736671 0.5491836 1000
summaryWO(HCE)
#> $summary
#> TRTP WIN LOSS TIE TOTAL WR WO
#> 1 A 110655 74913 64432 250000 1.4771135 1.3336352
#> 2 P 74913 110655 64432 250000 0.6769961 0.7498303
#>
#> $summary_by_GROUP
#> TRTP GROUP WIN LOSS TIE TOTAL
#> 1 A C 87780 0 45220 133000
#> 2 P C 39780 0 45220 85000
#> 3 A TTE1 0 36540 5460 42000
#> 4 P TTE1 0 27040 5460 32500
#> 5 A TTE2 3835 18703 6962 29500
#> 6 P TTE2 9912 42126 6962 59000
#> 7 A TTE3 10980 14400 4620 30000
#> 8 P TTE3 11011 22869 4620 38500
#> 9 A TTE4 8060 5270 2170 15500
#> 10 P TTE4 14210 18620 2170 35000
#>
#> $WO
#> WO SE WP SE_WP
#> 1 1.333635 0.07091636 0.571484 0.01736671