From 1b7ebe9480f84ae2c9c39b42f0843f63697839f6 Mon Sep 17 00:00:00 2001 From: "Zhao, Yujie" Date: Tue, 10 Sep 2024 15:40:09 -0400 Subject: [PATCH 1/7] add attributes to `sim_pw_surv` --- R/sim_pw_surv.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/R/sim_pw_surv.R b/R/sim_pw_surv.R index d0e1730c..c3f2c41d 100644 --- a/R/sim_pw_surv.R +++ b/R/sim_pw_surv.R @@ -191,5 +191,9 @@ sim_pw_surv <- function( ans[, fail := (fail_time <= dropout_time) * 1] setDF(ans) + + attr(ans, "ratio") <- sum(block == "experimental") / sum(block == "control") + attr(ans, "generate_by_simpwsurv") <- "yes" + return(ans) } From afeebe97570eb8278285c5bdbf40f34bcdb61117 Mon Sep 17 00:00:00 2001 From: "Zhao, Yujie" Date: Tue, 10 Sep 2024 15:40:34 -0400 Subject: [PATCH 2/7] add ratio attributes to 2 cutting functions --- R/cut_data_by_date.R | 3 +++ R/cut_data_by_event.R | 2 ++ 2 files changed, 5 insertions(+) diff --git a/R/cut_data_by_date.R b/R/cut_data_by_date.R index 3052b57b..4e51238a 100644 --- a/R/cut_data_by_date.R +++ b/R/cut_data_by_date.R @@ -40,6 +40,9 @@ cut_data_by_date <- function(x, cut_date) { ans <- ans[, c("tte", "event", "stratum", "treatment")] setDF(ans) + + attr(ans, "ratio") <- attributes(x)$ratio + class(ans) <- c("tte_data", class(ans)) return(ans) } diff --git a/R/cut_data_by_event.R b/R/cut_data_by_event.R index aa0c3029..616a3368 100644 --- a/R/cut_data_by_event.R +++ b/R/cut_data_by_event.R @@ -36,6 +36,8 @@ cut_data_by_event <- function(x, event) { cut_date <- get_cut_date_by_event(x, event) ans <- x |> cut_data_by_date(cut_date = cut_date) + + attr(ans, "ratio") <- attributes(x)$ratio class(ans) <- c("tte_data", class(ans)) return(ans) } From cf85f838a874ee475d5e12222ee58e9011b1528e Mon Sep 17 00:00:00 2001 From: "Zhao, Yujie" Date: Tue, 10 Sep 2024 15:41:27 -0400 Subject: [PATCH 3/7] add ratio attributes to counting process --- R/counting_process.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/R/counting_process.R b/R/counting_process.R index cc2c293a..e5132dcf 100644 --- a/R/counting_process.R +++ b/R/counting_process.R @@ -147,10 +147,8 @@ counting_process <- function(x, arm) { setDF(ans) class(ans) <- c("counting_process", class(ans)) - # Record number of control and experimental treatments, which is required for - # downstream test function wlr() - attr(ans, "n_ctrl") <- sum(x$treatment == "control") - attr(ans, "n_exp") <- sum(x$treatment == "experimental") + + attr(ans, "ratio") <- attributes(x)$ratio return(ans) } From 9562575c1a466e02bb2626edf34c0f7aa1511553 Mon Sep 17 00:00:00 2001 From: "Zhao, Yujie" Date: Tue, 10 Sep 2024 15:42:05 -0400 Subject: [PATCH 4/7] get ratio from attributes in wlr --- R/wlr.R | 55 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/R/wlr.R b/R/wlr.R index 2e04f2be..6c247bc5 100644 --- a/R/wlr.R +++ b/R/wlr.R @@ -75,18 +75,24 @@ #' \deqn{z = \sum_i X_i/\sqrt{\sum_i V_i}.} #' #' @examples +#' # ---------------------- # +#' # Example 1 # +#' # Use dataset generated # +#' # by simtrial # +#' # ---------------------- # #' x <- sim_pw_surv(n = 200) |> cut_data_by_event(100) #' -#' # Example 1: WLR test with FH wights +#' # Example 1A: WLR test with FH wights #' x |> wlr(weight = fh(rho = 0, gamma = 0.5)) #' x |> wlr(weight = fh(rho = 0, gamma = 0.5), return_variance = TRUE) #' -#' # Example 2: WLR test with MB wights +#' # Example 1B: WLR test with MB wights #' x |> wlr(weight = mb(delay = 4, w_max = 2)) #' -#' # Example 3: WLR test with early zero wights +#' # Example 1C: WLR test with early zero wights #' x |> wlr(weight = early_zero(early_period = 4)) #' +#' # Example 1D #' # For increased computational speed when running many WLR tests, you can #' # pre-compute the counting_process() step first, and then pass the result of #' # counting_process() directly to wlr() @@ -94,25 +100,54 @@ #' x |> wlr(weight = fh(rho = 0, gamma = 1)) #' x |> wlr(weight = mb(delay = 4, w_max = 2)) #' x |> wlr(weight = early_zero(early_period = 4)) -wlr <- function(data, weight, return_variance = FALSE) { +#' +#' # ---------------------- # +#' # Example 2 # +#' # Use cumsum dataset # +#' # ---------------------- # +#' x <- data.frame(treatment = ifelse(ex1_delayed_effect$trt == 1, "experimental", "control"), +#' stratum = rep("All", nrow(ex1_delayed_effect)), +#' tte = ex1_delayed_effect$month, +#' event = ex1_delayed_effect$evntd) +#' class(x) <- c("tte_data", class(x)) +#' +#' # Users can specify the randomization ratio to calculate the statistical information under H0 +#' x |> wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 2) +#' +#' # If users don't provide the randomization ratio, we will calculate the emperical ratio +#' x |> wlr(weight = fh(rho = 0, gamma = 0.5)) +#' +#' x |> +#' counting_process(arm = "experimental") |> +#' wlr(weight = fh(rho = 0, gamma = 0.5)) +wlr <- function(data, weight, return_variance = FALSE, ratio = NULL) { UseMethod("wlr", data) } #' @rdname wlr #' @export -wlr.tte_data <- function(data, weight, return_variance = FALSE) { +wlr.tte_data <- function(data, weight, return_variance = FALSE, ratio = NULL) { + # if the `data` is NOT generated by sim_pw_surv + # - if user input the randomization ratio, we will directly take its values + # - otherwise, we calculate the emperical ratio + if (!"generate_by_simpwsurv" %in% names(attributes(data))) { + if (is.null(ratio)) { + ratio <- sum(data$treatment == "experimental") / sum(data$treatment == "control") + } + } else { + # if the `data` is generated by sim_pw_surv, the take the ratio from the attributes of `data` + ratio <- attributes(data)$ratio + } + x <- data |> counting_process(arm = "experimental") - wlr.counting_process(x, weight, return_variance = FALSE) + wlr.counting_process(x, weight, return_variance = FALSE, ratio = ratio) } #' @rdname wlr #' @export -wlr.counting_process <- function(data, weight, return_variance = FALSE) { +wlr.counting_process <- function(data, weight, return_variance = FALSE, ratio = NULL) { x <- data - # calculate the sample size and randomization ratio - n <- nrow(data) - ratio <- attr(data, "n_exp") / attr(data, "n_ctrl") q_e <- ratio / (1 + ratio) q_c <- 1 - q_e From 09e1848e36024bf1552fa8e3afff5a2197f4cb24 Mon Sep 17 00:00:00 2001 From: "Zhao, Yujie" Date: Tue, 10 Sep 2024 15:42:30 -0400 Subject: [PATCH 5/7] add examples as new feature testing --- man/wlr.Rd | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/man/wlr.Rd b/man/wlr.Rd index af57f80e..37d8243e 100644 --- a/man/wlr.Rd +++ b/man/wlr.Rd @@ -6,11 +6,11 @@ \alias{wlr.counting_process} \title{Weighted logrank test} \usage{ -wlr(data, weight, return_variance = FALSE) +wlr(data, weight, return_variance = FALSE, ratio = NULL) -\method{wlr}{tte_data}(data, weight, return_variance = FALSE) +\method{wlr}{tte_data}(data, weight, return_variance = FALSE, ratio = NULL) -\method{wlr}{counting_process}(data, weight, return_variance = FALSE) +\method{wlr}{counting_process}(data, weight, return_variance = FALSE, ratio = NULL) } \arguments{ \item{data}{Dataset (generated by \code{\link[=sim_pw_surv]{sim_pw_surv()}}) that has been cut by @@ -74,18 +74,24 @@ The stratified Fleming-Harrington weighted logrank test is then computed as: } } \examples{ +# ---------------------- # +# Example 1 # +# Use dataset generated # +# by simtrial # +# ---------------------- # x <- sim_pw_surv(n = 200) |> cut_data_by_event(100) -# Example 1: WLR test with FH wights +# Example 1A: WLR test with FH wights x |> wlr(weight = fh(rho = 0, gamma = 0.5)) x |> wlr(weight = fh(rho = 0, gamma = 0.5), return_variance = TRUE) -# Example 2: WLR test with MB wights +# Example 1B: WLR test with MB wights x |> wlr(weight = mb(delay = 4, w_max = 2)) -# Example 3: WLR test with early zero wights +# Example 1C: WLR test with early zero wights x |> wlr(weight = early_zero(early_period = 4)) +# Example 1D # For increased computational speed when running many WLR tests, you can # pre-compute the counting_process() step first, and then pass the result of # counting_process() directly to wlr() @@ -93,4 +99,24 @@ x <- x |> counting_process(arm = "experimental") x |> wlr(weight = fh(rho = 0, gamma = 1)) x |> wlr(weight = mb(delay = 4, w_max = 2)) x |> wlr(weight = early_zero(early_period = 4)) + +# ---------------------- # +# Example 2 # +# Use cumsum dataset # +# ---------------------- # +x <- data.frame(treatment = c("experimental", "experimental", "control", "control"), + stratum = rep("All", 4), + tte = c(20, 30, 15, 25), + event = c(0, 1, 0, 1)) +class(x) <- c("tte_data", class(x)) + +# Users can specify the randomization ratio to calculate the statistical information under H0 +x |> wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 1) + +# If users don't provide the randomization ratio, we will calculate the emperical ratio +x |> wlr(weight = fh(rho = 0, gamma = 0.5)) + +x |> + counting_process(arm = "experimental") |> + wlr(weight = fh(rho = 0, gamma = 0.5)) } From c9620d3df08d630420781f366302eab29de87a0b Mon Sep 17 00:00:00 2001 From: "Zhao, Yujie" Date: Tue, 10 Sep 2024 16:25:46 -0400 Subject: [PATCH 6/7] update `wlr.counting_process` following the unsuccessful test in `test-unvalidated_wlr` --- R/counting_process.R | 9 ++++++++- R/wlr.R | 9 ++++++++- man/wlr.Rd | 14 +++++++++----- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/R/counting_process.R b/R/counting_process.R index e5132dcf..5a54cba8 100644 --- a/R/counting_process.R +++ b/R/counting_process.R @@ -148,7 +148,14 @@ counting_process <- function(x, arm) { setDF(ans) class(ans) <- c("counting_process", class(ans)) - attr(ans, "ratio") <- attributes(x)$ratio + # if `x` is generated by sim_pw_surv + if ("generate_by_simpwsurv" %in% names(attributes(x))) { + ratio <- attributes(x)$ratio + } else { + # if not, calcualte the emperical ratio + ratio <- sum(x$treatment == arm) / sum(x$treatment != arm) + } + attr(ans, "ratio") <- ratio return(ans) } diff --git a/R/wlr.R b/R/wlr.R index 6c247bc5..8c459daa 100644 --- a/R/wlr.R +++ b/R/wlr.R @@ -114,6 +114,10 @@ #' # Users can specify the randomization ratio to calculate the statistical information under H0 #' x |> wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 2) #' +#' x |> +#' counting_process(arm = "experimental") |> +#' wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 2) +#' #' # If users don't provide the randomization ratio, we will calculate the emperical ratio #' x |> wlr(weight = fh(rho = 0, gamma = 0.5)) #' @@ -129,7 +133,7 @@ wlr <- function(data, weight, return_variance = FALSE, ratio = NULL) { wlr.tte_data <- function(data, weight, return_variance = FALSE, ratio = NULL) { # if the `data` is NOT generated by sim_pw_surv # - if user input the randomization ratio, we will directly take its values - # - otherwise, we calculate the emperical ratio + # - otherwise, we calculate the empirical ratio if (!"generate_by_simpwsurv" %in% names(attributes(data))) { if (is.null(ratio)) { ratio <- sum(data$treatment == "experimental") / sum(data$treatment == "control") @@ -148,6 +152,9 @@ wlr.tte_data <- function(data, weight, return_variance = FALSE, ratio = NULL) { wlr.counting_process <- function(data, weight, return_variance = FALSE, ratio = NULL) { x <- data + if (is.null(ratio)) { + ratio <- attributes(data)$ratio + } q_e <- ratio / (1 + ratio) q_c <- 1 - q_e diff --git a/man/wlr.Rd b/man/wlr.Rd index 37d8243e..b9249cee 100644 --- a/man/wlr.Rd +++ b/man/wlr.Rd @@ -104,14 +104,18 @@ x |> wlr(weight = early_zero(early_period = 4)) # Example 2 # # Use cumsum dataset # # ---------------------- # -x <- data.frame(treatment = c("experimental", "experimental", "control", "control"), - stratum = rep("All", 4), - tte = c(20, 30, 15, 25), - event = c(0, 1, 0, 1)) +x <- data.frame(treatment = ifelse(ex1_delayed_effect$trt == 1, "experimental", "control"), + stratum = rep("All", nrow(ex1_delayed_effect)), + tte = ex1_delayed_effect$month, + event = ex1_delayed_effect$evntd) class(x) <- c("tte_data", class(x)) # Users can specify the randomization ratio to calculate the statistical information under H0 -x |> wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 1) +x |> wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 2) + +x |> + counting_process(arm = "experimental") |> + wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 2) # If users don't provide the randomization ratio, we will calculate the emperical ratio x |> wlr(weight = fh(rho = 0, gamma = 0.5)) From c61aecdb53ce3d41dd0d606278cdcdf2cde336bb Mon Sep 17 00:00:00 2001 From: "Zhao, Yujie" Date: Tue, 10 Sep 2024 16:40:01 -0400 Subject: [PATCH 7/7] fix warnings of missing @param --- R/wlr.R | 11 +++++++++++ man/wlr.Rd | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/R/wlr.R b/R/wlr.R index 8c459daa..9d2087f7 100644 --- a/R/wlr.R +++ b/R/wlr.R @@ -25,6 +25,17 @@ #' @param return_variance A logical flag that, if `TRUE`, adds columns #' estimated variance for weighted sum of observed minus expected; #' see details; Default: `FALSE`. +#' @param ratio randomization ratio (experimental:control). +#' - If the `data` is generated by simtrial, such as +#' + `data = sim_pw_surv(...) |> cut_data_by_date(...)` +#' + `data = sim_pw_surv(...) |> cut_data_by_event(...)` +#' + `data = sim_pw_surv(...) |> cut_data_by_date(...) |> counting_process(...)` +#' + `data = sim_pw_surv(...) |> cut_data_by_event(...) |> counting_process(...)` +#' there is no need to input the `ratio`, as simtrial gets the `ratio` via the +#' `block` arguments in [sim_pw_surv()]. +#' - If the `data` is a custom dataset (see Example 2) below, +#' + Users are suggested to input the planned randomization ratio to `ratio`; +#' + If not, simtrial takes the empirical randomization ratio. #' #' @return A list containing the test method (`method`), #' parameters of this test method (`parameter`), diff --git a/man/wlr.Rd b/man/wlr.Rd index b9249cee..163f8ed9 100644 --- a/man/wlr.Rd +++ b/man/wlr.Rd @@ -22,6 +22,24 @@ wlr(data, weight, return_variance = FALSE, ratio = NULL) \item{return_variance}{A logical flag that, if \code{TRUE}, adds columns estimated variance for weighted sum of observed minus expected; see details; Default: \code{FALSE}.} + +\item{ratio}{randomization ratio (experimental:control). +\itemize{ +\item If the \code{data} is generated by simtrial, such as +\itemize{ +\item \code{data = sim_pw_surv(...) |> cut_data_by_date(...)} +\item \code{data = sim_pw_surv(...) |> cut_data_by_event(...)} +\item \code{data = sim_pw_surv(...) |> cut_data_by_date(...) |> counting_process(...)} +\item \code{data = sim_pw_surv(...) |> cut_data_by_event(...) |> counting_process(...)} +there is no need to input the \code{ratio}, as simtrial gets the \code{ratio} via the +\code{block} arguments in \code{\link[=sim_pw_surv]{sim_pw_surv()}}. +} +\item If the \code{data} is a custom dataset (see Example 2) below, +\itemize{ +\item Users are suggested to input the planned randomization ratio to \code{ratio}; +\item If not, simtrial takes the empirical randomization ratio. +} +}} } \value{ A list containing the test method (\code{method}),