diff --git a/DESCRIPTION b/DESCRIPTION index 61f07a8c..9b95638e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -58,9 +58,10 @@ Suggests: knitr, rmarkdown, survMisc, + survRM2, testthat, tidyr LinkingTo: Rcpp Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 diff --git a/NAMESPACE b/NAMESPACE index c2eb91d5..e4fbdc43 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,6 +11,7 @@ export(get_cut_date_by_event) export(mb_weight) export(pvalue_maxcombo) export(randomize_by_fixed_block) +export(rmst) export(rpwexp) export(rpwexp_enroll) export(sim_fixed_n) @@ -37,4 +38,5 @@ importFrom(mvtnorm,GenzBretz) importFrom(mvtnorm,pmvnorm) importFrom(survival,Surv) importFrom(survival,is.Surv) +importFrom(survival,survfit) useDynLib(simtrial, .registration = TRUE) diff --git a/R/check_args.R b/R/check_args.R new file mode 100644 index 00000000..c1e3ef5c --- /dev/null +++ b/R/check_args.R @@ -0,0 +1,83 @@ +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# All rights reserved. +# +# This file is part of the simtrial program. +# +# simtrial is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#' Check argument types, length, or dimension +#' +#' @param arg An argument to be checked. +#' @param type A character vector of candidate argument type. +#' @param length A numeric value of argument length or `NULL`. +#' @param dim A numeric vector of argument dimension or `NULL`. +#' +#' @details If `type`, `length` or `dim` is `NULL`, the corresponding check will not be executed. +#' +#' @section Specification: +#' \if{latex}{ +#' \itemize{ +#' \item Check if arg is NULL. +#' \item Extract the type, length and dim information from arg. +#' \item Compare with target values and report error message if it does not match. +#' } +#' } +#' \if{html}{The contents of this section are shown in PDF user manual only.} +#' +#' @return Check failure detailed error message. +#' +#' @keywords internal +#' +#' @examples +#' \dontrun{ +#' tbl <- as.data.frame(matrix(1:9, nrow = 3)) +#' simtrial:::check_args(arg = tbl, type = c("data.frame")) +#' +#' vec <- c("a", "b", "c") +#' simtrial:::check_args(arg = vec, type = c("character"), length = 3) +#' } +check_args <- function(arg, type, length = NULL, dim = NULL) { + if (is.null(arg)) { + return(NULL) + } + + arg <- as.vector(arg) + + check <- list() + message <- list() + + if (!is.null(type)) { + check[["type"]] <- any(class(arg) %in% type) & (!is.null(class(arg))) + message[["type"]] <- paste("The argument type did not match:", paste(type, collapse = "/")) + } + + if (!is.null(length)) { + check[["length"]] <- all(length(arg) == length) & (!is.null(length(arg))) + message[["length"]] <- paste("The argument length is not", length) + } + + if (!is.null(dim)) { + check[["dim"]] <- all(dim(arg) == dim) & (!is.null(dim(arg))) + message[["dim"]] <- paste("The argument dimension is not", paste(dim, collapse = ",")) + } + + check <- unlist(check) + message <- unlist(message) + + if (!all(unlist(check))) { + stop(paste(message[!check], collapse = "\n")) + } else { + return(NULL) + } +} diff --git a/R/rmst.R b/R/rmst.R new file mode 100644 index 00000000..f01bde29 --- /dev/null +++ b/R/rmst.R @@ -0,0 +1,278 @@ +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# All rights reserved. +# +# This file is part of the simtrial program. +# +# simtrial is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#' RMST difference of 2 arms +#' +#' @param data A time-to-event dataset with a column `tte` indicating the +#' survival time and a column of `event` indicating whether it is +#' event or censor. +#' @param tau RMST analysis time. +#' @param var_label_tte Column name of the TTE variable. +#' @param var_label_event Column name of the event variable. +#' @param var_label_group Column name of the grouping variable. +#' @param reference A group label indicating the reference group. +#' @param alpha Type I error. +#' +#' @return The z statistics. +#' +#' @export +#' +#' @examples +#' data(ex1_delayed_effect) +#' rmst( +#' data = ex1_delayed_effect, +#' var_label_tte = "month", +#' var_label_event = "evntd", +#' var_label_group = "trt", +#' tau = 10, +#' reference = "0" +#' ) +rmst <- function( + data, + tau = 10, + var_label_tte = "tte", + var_label_event = "event", + var_label_group = "treatment", + reference = "control", + alpha = 0.05) { + res <- rmst_two_arm( + time_var = data[[var_label_tte]], + event_var = data[[var_label_event]], + group_var = data[[var_label_group]], + trunc_time = tau, + reference = reference, + alpha = alpha + ) + + ans <- data.frame( + rmst_arm1 = res$rmst_per_arm$rmst[res$rmst_per_arm$group != reference], + rmst_arm0 = res$rmst_per_arm$rmst[res$rmst_per_arm$group == reference], + rmst_diff = res$rmst_diff$rmst_diff, + z = res$rmst_diff$rmst_diff / res$rmst_diff$std + ) + + return(ans) +} + +#' Calculate RMST difference +#' +#' @inheritParams rmst_single_arm +#' @param group_var A vector of treatment groups. +#' @param trunc_time A numeric vector of pre-defined cut-off time point(s). +#' @param reference Group name of reference group for RMST comparison. +#' Default is the first group name by alphabetical order. +#' +#' @return +#' A list of 2 data frames of RMST calculations: +#' - `rmst_per_arm`: the calculation results per group. +#' - `rmst_diff`: the calculation results of RMST differences. +#' +#' @keywords internal +#' +#' @examples +#' data(ex1_delayed_effect) +#' with( +#' ex1_delayed_effect, +#' simtrial:::rmst_two_arm( +#' time_var = month, +#' event_var = evntd, +#' group_var = trt, +#' trunc_time = 6, +#' reference = "0", +#' alpha = 0.05 +#' ) +#' ) +rmst_two_arm <- function( + time_var, + event_var, + group_var, + trunc_time, + reference = sort(unique(group_var))[1], + alpha = 0.05) { + # Input type check + check_args(time_var, type = c("integer", "numeric")) + check_args(event_var, type = c("integer", "numeric")) + check_args(trunc_time, type = c("integer", "numeric")) + check_args(reference, type = c("character")) + check_args(alpha, type = c("integer", "numeric")) + + # Input value check + stopifnot(time_var >= 0) + stopifnot(event_var %in% c(0, 1)) + stopifnot(trunc_time >= 0) + stopifnot(0 <= alpha & alpha <= 1) + + # Check truncation time + if (any(min(tapply(time_var, group_var, max)) < trunc_time)) { + stop(paste0( + "The truncation time must be shorter than the minimum of the largest observed time in each group: ", + sprintf("%.3f", min(tapply(time_var, group_var, max))) + )) + } + + g_label <- sort(unique(as.character(group_var))) + + # Calculate RMST for each group by rmst_single() + one_rmst <- function(x) { + indx <- group_var == x + rmst_single_arm( + time_var = time_var[indx], + event_var = event_var[indx], + tau = trunc_time, + group_label = x, + alpha = alpha + ) + } + + op_single <- do.call(rbind, lapply(g_label, one_rmst)) + + # Calculate RMST difference and corresponding confidence intervals between each group with reference group + diff_rmst <- function(x) { + df_rf <- op_single[op_single$group == reference, ] + df2 <- op_single[op_single$group == x, ] + cutoff_time <- trunc_time + group <- paste(unique(df2$group), "-", unique(df_rf$group)) + rmst_diff <- df2$rmst - df_rf$rmst + variance <- df2$variance + df_rf$variance + std <- sqrt(variance) + lcl <- rmst_diff - stats::qnorm(1 - alpha / 2) * std + ucl <- rmst_diff + stats::qnorm(1 - alpha / 2) * std + data.frame(cutoff_time, group, rmst_diff, variance, std, lcl, ucl, stringsAsFactors = FALSE) + } + + op_diff <- do.call(rbind, lapply(setdiff(g_label, reference), diff_rmst)) + + ans <- list(rmst_per_arm = op_single, rmst_diff = op_diff) + + return(ans) +} + +#' Calculate RMST for a single cut-off time point +#' +#' @param time_var A numeric vector of follow up time. +#' @param event_var A numeric or integer vector of the status indicator; +#' 0=alive 1=event. +#' @param tau A value of pre-defined cut-off time point. +#' @param group_label A character of customized treatment group name. +#' @param alpha A numeric value of the significant level for RMST +#' confidence interval. Default is 0.05. +#' +#' @return +#' A data frame of +#' - Cutoff time: same as \code{tau}; +#' - Group label: same as \code{group_label}; +#' - Estimated RMST; +#' - Variance, standard error, and CIs of the estimated RMST; +#' - Number of events. +#' +#' @importFrom survival survfit Surv +#' +#' @keywords internal +#' +#' @examples +#' data(ex1_delayed_effect) +#' data_single_arm <- ex1_delayed_effect[ex1_delayed_effect$trt == 1, ] +#' simtrial:::rmst_single_arm( +#' time_var = data_single_arm$month, +#' event_var = data_single_arm$evntd, +#' tau = 10, +#' group_label = "Treatment 1", +#' alpha = 0.05 +#' ) +rmst_single_arm <- function( + time_var, + event_var, + tau, + group_label = "Single Group", + alpha = 0.05) { + # Input type check + check_args(time_var, type = c("integer", "numeric")) + check_args(event_var, type = c("integer", "numeric")) + check_args(tau, type = c("integer", "numeric"), length = 1) + check_args(group_label, type = c("character", "factor"), length = 1) + check_args(alpha, type = c("integer", "numeric")) + + # Input value check + stopifnot(time_var >= 0) + stopifnot(event_var %in% c(0, 1)) + stopifnot(tau >= 0) + stopifnot(0 <= alpha & alpha <= 1) + + # Fit a single Kaplan-Meier curve + fit <- survival::survfit(survival::Surv(time_var, event_var) ~ 1) + + # Extract survival probability, number of event, number at risk, + # and number of censored along with observed time from the fitted model + # as a new data frame. + df <- data.frame( + time = fit$time, + n_risk = fit$n.risk, + n_event = fit$n.event, + n_censor = fit$n.censor, + surv = fit$surv, + stringsAsFactors = FALSE + ) + + # Filter df by survival time less or equal to the pre-specified cut-off time point tau + df_fit1 <- df[df$time <= tau, ] + + # Add initial value of (time, survival) = (0,1) for calculating time difference + df_fit2 <- rbind(df_fit1, c(0, NA, NA, NA, 1)) + + # Add cut-off time if no records observed on the pre-specified time point + if (max(df_fit1$time) != tau) df_fit2 <- rbind(df_fit2, c(tau, NA, NA, NA, NA)) + + # Sort the data frame by time + df_fit2 <- df_fit2[order(df_fit2$time), ] + n_event <- df_fit2$n_event + n_risk <- df_fit2$n_risk + + # Calculate the time difference and set the last value as NA + time_diff <- c(diff((sort(df_fit2$time))), NA) + + # Calculate the area under the curve per time interval + area <- time_diff * df_fit2$surv + + # Calculate the inverse cumulated area under the curve per observed time point A_i + big_a <- rev(c(0, cumsum(rev(area)[-1]))) + + # Calculation of dev refers to di / Yi * (Yi - di) + dev <- (n_event / (n_risk * (n_risk - n_event))) * (big_a^2) + + # Based on the calculation, create a data frame with below items: + # cutoff_time is the input of pre-defined cut-off time point + cutoff_time <- tau + # group is the input group name + group <- group_label + # rmst is the estimated RMST + rmst <- sum(area, na.rm = TRUE) + # std is the standard error of the estimated RMST + variance <- sum(dev, na.rm = TRUE) * sum(n_event, na.rm = TRUE) / (sum(n_event, na.rm = TRUE) - 1) + std <- sqrt(variance) + # lcl and ucl are lower/upper control limit of CIs for RMST + lcl <- rmst - stats::qnorm(1 - alpha / 2) * std + ucl <- rmst + stats::qnorm(1 - alpha / 2) * std + event <- sum(n_event, na.rm = TRUE) + + ans <- data.frame( + cutoff_time, group, rmst, variance, std, lcl, ucl, event, + stringsAsFactors = FALSE + ) + + return(ans) +} diff --git a/_pkgdown.yml b/_pkgdown.yml index c7e828f1..ebc94ccf 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -47,6 +47,7 @@ reference: contents: - counting_process - pvalue_maxcombo + - rmst - title: "Randomization algorithms" contents: @@ -78,6 +79,7 @@ articles: - arbitrary-hazard - modest-wlrt - pvalue-maxcombo + - rmst - title: "Best practices" contents: - parallel diff --git a/man/check_args.Rd b/man/check_args.Rd new file mode 100644 index 00000000..3d466f3d --- /dev/null +++ b/man/check_args.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check_args.R +\name{check_args} +\alias{check_args} +\title{Check argument types, length, or dimension} +\usage{ +check_args(arg, type, length = NULL, dim = NULL) +} +\arguments{ +\item{arg}{An argument to be checked.} + +\item{type}{A character vector of candidate argument type.} + +\item{length}{A numeric value of argument length or \code{NULL}.} + +\item{dim}{A numeric vector of argument dimension or \code{NULL}.} +} +\value{ +Check failure detailed error message. +} +\description{ +Check argument types, length, or dimension +} +\details{ +If \code{type}, \code{length} or \code{dim} is \code{NULL}, the corresponding check will not be executed. +} +\section{Specification}{ + +\if{latex}{ + \itemize{ + \item Check if arg is NULL. + \item Extract the type, length and dim information from arg. + \item Compare with target values and report error message if it does not match. + } + } +\if{html}{The contents of this section are shown in PDF user manual only.} +} + +\examples{ +\dontrun{ +tbl <- as.data.frame(matrix(1:9, nrow = 3)) +simtrial:::check_args(arg = tbl, type = c("data.frame")) + +vec <- c("a", "b", "c") +simtrial:::check_args(arg = vec, type = c("character"), length = 3) +} +} +\keyword{internal} diff --git a/man/rmst.Rd b/man/rmst.Rd new file mode 100644 index 00000000..b6822fdd --- /dev/null +++ b/man/rmst.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rmst.R +\name{rmst} +\alias{rmst} +\title{RMST difference of 2 arms} +\usage{ +rmst( + data, + tau = 10, + var_label_tte = "tte", + var_label_event = "event", + var_label_group = "treatment", + reference = "control", + alpha = 0.05 +) +} +\arguments{ +\item{data}{A time-to-event dataset with a column \code{tte} indicating the +survival time and a column of \code{event} indicating whether it is +event or censor.} + +\item{tau}{RMST analysis time.} + +\item{var_label_tte}{Column name of the TTE variable.} + +\item{var_label_event}{Column name of the event variable.} + +\item{var_label_group}{Column name of the grouping variable.} + +\item{reference}{A group label indicating the reference group.} + +\item{alpha}{Type I error.} +} +\value{ +The z statistics. +} +\description{ +RMST difference of 2 arms +} +\examples{ +data(ex1_delayed_effect) +rmst( + data = ex1_delayed_effect, + var_label_tte = "month", + var_label_event = "evntd", + var_label_group = "trt", + tau = 10, + reference = "0" +) +} diff --git a/man/rmst_single_arm.Rd b/man/rmst_single_arm.Rd new file mode 100644 index 00000000..4c9e841a --- /dev/null +++ b/man/rmst_single_arm.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rmst.R +\name{rmst_single_arm} +\alias{rmst_single_arm} +\title{Calculate RMST for a single cut-off time point} +\usage{ +rmst_single_arm( + time_var, + event_var, + tau, + group_label = "Single Group", + alpha = 0.05 +) +} +\arguments{ +\item{time_var}{A numeric vector of follow up time.} + +\item{event_var}{A numeric or integer vector of the status indicator; +0=alive 1=event.} + +\item{tau}{A value of pre-defined cut-off time point.} + +\item{group_label}{A character of customized treatment group name.} + +\item{alpha}{A numeric value of the significant level for RMST +confidence interval. Default is 0.05.} +} +\value{ +A data frame of +\itemize{ +\item Cutoff time: same as \code{tau}; +\item Group label: same as \code{group_label}; +\item Estimated RMST; +\item Variance, standard error, and CIs of the estimated RMST; +\item Number of events. +} +} +\description{ +Calculate RMST for a single cut-off time point +} +\examples{ +data(ex1_delayed_effect) +data_single_arm <- ex1_delayed_effect[ex1_delayed_effect$trt == 1, ] +simtrial:::rmst_single_arm( + time_var = data_single_arm$month, + event_var = data_single_arm$evntd, + tau = 10, + group_label = "Treatment 1", + alpha = 0.05 +) +} +\keyword{internal} diff --git a/man/rmst_two_arm.Rd b/man/rmst_two_arm.Rd new file mode 100644 index 00000000..ac71d75d --- /dev/null +++ b/man/rmst_two_arm.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rmst.R +\name{rmst_two_arm} +\alias{rmst_two_arm} +\title{Calculate RMST difference} +\usage{ +rmst_two_arm( + time_var, + event_var, + group_var, + trunc_time, + reference = sort(unique(group_var))[1], + alpha = 0.05 +) +} +\arguments{ +\item{time_var}{A numeric vector of follow up time.} + +\item{event_var}{A numeric or integer vector of the status indicator; +0=alive 1=event.} + +\item{group_var}{A vector of treatment groups.} + +\item{trunc_time}{A numeric vector of pre-defined cut-off time point(s).} + +\item{reference}{Group name of reference group for RMST comparison. +Default is the first group name by alphabetical order.} + +\item{alpha}{A numeric value of the significant level for RMST +confidence interval. Default is 0.05.} +} +\value{ +A list of 2 data frames of RMST calculations: +\itemize{ +\item \code{rmst_per_arm}: the calculation results per group. +\item \code{rmst_diff}: the calculation results of RMST differences. +} +} +\description{ +Calculate RMST difference +} +\examples{ +data(ex1_delayed_effect) +with( + ex1_delayed_effect, + simtrial:::rmst_two_arm( + time_var = month, + event_var = evntd, + group_var = trt, + trunc_time = 6, + reference = "0", + alpha = 0.05 + ) +) +} +\keyword{internal} diff --git a/vignettes/rmst.Rmd b/vignettes/rmst.Rmd new file mode 100644 index 00000000..7d98e1c9 --- /dev/null +++ b/vignettes/rmst.Rmd @@ -0,0 +1,155 @@ +--- +title: "Restricted mean survival time (RMST)" +output: rmarkdown::html_vignette +bibliography: simtrial.bib +vignette: > + %\VignetteIndexEntry{Restricted mean survival time (RMST)} + %\VignetteEngine{knitr::rmarkdown} +--- + +```{r, include=FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +# Introduction + +Restricted mean survival time (RMST) is defined as the area under the survival curve up to a specific time point. It can be interpreted as the average survival time during a defined time period ranging from time 0 to a specific follow-up time point, which is a straightforward and clinically meaningful way to interpret the contrast in survival between groups. + +The RMST may provide valuable information for comparing two survival curves when the proportional hazards assumption is not met, such as in cases of *crossing* or *delayed separation* of survival curves. + +# RMST vs. logrank + +The log-rank test calculates the test statistics using the survival rate at each time point, and then summarizes them to test the equality of the survival curves as a whole for the entire follow-up period. + +A comparison of the RMST between two survival curves provides an estimate of the duration of time gained or lost associated with the exposure. + +Although RMST has an advantage over the hazard ratio, a previous study showed that the difference in RMST often has operating characteristics similar to the log-rank test under the proportional hazards assumption [@royston2013restricted]. However, in the case of crossing survival curves, the efficacy of an intervention may be demonstrated by showing a difference in RMST between the two curves, although the log-rank test may fail to detect a difference because of the occurrence of non-proportional hazards. + +# Estimation of RMST in a single arm at a single time point + +Assume the event time as $T$, and the survival function as $S(t) = Pr(T>t)$. The restricted mean survival time at a pre-specified cutoff time point $\tau$ is +$$ + \text{RMST}(\tau) = E[\min (T, \tau)] = \int_{0}^{\tau} S(u) d u. +$$ +Suppose there are $D$ events, with distinct observed event times at $t_1 < t_2 < \ldots gt() +``` + +## Estimation of RMST differences in 2 arms at a single time point + +Let $\text{RMST}_{1}(\tau)$ and $\text{RMST}_{2}(\tau)$ be the RMST of treatment group 1 and 2 at predefined time $\tau$, the RMST difference between the 2 treatment groups ($\theta$) can be defined as +$$ + \theta = \text{RMST}_1(\tau) - \text{RMST}_2(\tau). +$$ +The expected value of $\theta$ is $E(\theta) = E[RMST_{1}(\tau)] - E[RMST_{2}(\tau)]$. If two treatment groups are independent, the variance of $\theta$ is +$$ + \text{Var}(\theta) = \sigma_{1}^{2} + \sigma_{2}^{2} +$$ + +Similarly, the $(1-\alpha)$ confidence interval for $\text{RMST}$ difference between 2 groups can be calculated as: +$$ + \left[ + \hat{\theta} - z_{\alpha/2}\sqrt{\hat{\sigma}_1^2 + \hat{\sigma}_2^2}, + \;\; + \hat{\theta} + z_{\alpha/2}\sqrt{\hat{\sigma}_1^2 + \hat{\sigma}_2^2} + \right]. +$$ + +```{r} +tau <- 10 + +data(ex1_delayed_effect) + +ex1_delayed_effect |> + rmst( + var_label_tte = "month", + var_label_event = "evntd", + var_label_group = "trt", + tau = 10, + reference = "0" + ) +``` + +The R package survRM2 [@uno2022survrm2] performs two-sample comparisons using RMST as a summary measure of the survival time distribution. Three kinds of between-group contrast metrics (i.e., the difference in RMST, the ratio of RMST and the ratio of the restricted mean time lost (RMTL)) are computed. It performs an ANCOVA-type covariate adjustment as well as unadjusted analyses for those measures. We use this R package as validation of `simtrial::rmst()`. + +```{r} +verify <- survRM2::rmst2( + time = ex1_delayed_effect$month, + status = ex1_delayed_effect$evntd, + arm = ex1_delayed_effect$trt, + tau = tau, + alpha = 0.05 +) + +verify$RMST.arm1$rmst[1] - verify$RMST.arm0$rmst[1] +``` + +## References diff --git a/vignettes/simtrial.bib b/vignettes/simtrial.bib index 2eba68c4..d6afb79d 100644 --- a/vignettes/simtrial.bib +++ b/vignettes/simtrial.bib @@ -187,6 +187,17 @@ @article{NPHWGSimulation publisher = {Taylor \& Francis} } +@article{royston2013restricted, + title = {Restricted mean survival time: an alternative to the hazard ratio for the design and analysis of randomized trials with a time-to-event outcome}, + author = {Royston, Patrick and Parmar, Mahesh KB}, + journal = {BMC Medical Research Methodology}, + volume = {13}, + number = {1}, + pages = {1--15}, + year = {2013}, + publisher = {BioMed Central} +} + @article{Schemper2009, title = {The estimation of average hazard ratios by weighted Cox regression}, author = {Schemper, Michael and Wakounig, Samo and Heinze, Georg}, @@ -206,6 +217,14 @@ @article{Tsiatis year = {1982} } +@article{uno2022survrm2, + title = {{survRM2}: Comparing Restricted Mean Survival Time}, + author = {Hajime Uno and Lu Tian and Miki Horiguchi and Angel Cronin and Chakib Battioui and James Bell}, + year = {2022}, + note = {R package version 1.0-4}, + url = {https://CRAN.R-project.org/package=survRM2} +} + @article{Xi2017, title = {A unified framework for weighted parametric multiple test procedures}, author = {Xi, Dong and Glimm, Ekkehard and Maurer, Willi and Bretz, Frank}, @@ -215,4 +234,4 @@ @article{Xi2017 pages = {918--931}, year = {2017}, publisher = {Wiley Online Library} -} \ No newline at end of file +}