Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get ratio as an attributes of sim_pw_surv and pass it to wlr #281

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions R/counting_process.R
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,15 @@ 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")

# 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)
}
3 changes: 3 additions & 0 deletions R/cut_data_by_date.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
2 changes: 2 additions & 0 deletions R/cut_data_by_event.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
4 changes: 4 additions & 0 deletions R/sim_pw_surv.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
73 changes: 63 additions & 10 deletions R/wlr.R
Original file line number Diff line number Diff line change
Expand Up @@ -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`),
Expand Down Expand Up @@ -75,44 +86,86 @@
#' \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()
#' 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))
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)
#'
#' 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))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we please convert this example to an automated test so that we can confirm that manually providing ratio = 2 returns a different result compared to using the empirical ratio via ratio = NULL?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @jdblischak, I appreciate you bringing this to my attention. I completely agree with you that adding tests is valuable and advantageous. But I tentatively prefer to keep it as it is. My reasons are listed as follows.

First of all, since there is no equivalence between the one with ratio=2 and ratio=NULL, it's challenging to compare these two on the same scale.

Second, rather than composing my own developer tests, I should seek volunteers for independent or double programming tests, which hold more value and solidity compared to my basic developer tests.

Third, instead of including the above in the tests, I would rather keep them as examples in the help page, where users can readily distinguish the two ways to set ratio, even if they don't review the test files.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely agree with you that adding tests is valuable and advantageous.

I'll add the tests in a follow-up PR

First of all, since there is no equivalence between the one with ratio=2 and ratio=NULL, it's challenging to compare these two on the same scale.

I don't understand the issue with the scale. I am requesting a test that confirms that the newly added argument ratio has an effect on the results. Thus something like the below would be sufficient:

wlr_w_ratio <- x |> wlr(weight = fh(rho = 0, gamma = 0.5), ratio = 2)
wlr_wo_ratio <- x |> wlr(weight = fh(rho = 0, gamma = 0.5))
expect_false(isTRUE(all.equal(wlr_w_ratio, wlr_wo_ratio)))

Second, rather than composing my own developer tests, I should seek volunteers for independent or double programming tests, which hold more value and solidity compared to my basic developer tests.

I do not understand this perspective. The potential for better tests in the future does not remove the requirement for basic developer tests. Tests are our best method to prevent the accumulation of technical debt. As a concrete, recent example, the argument ratio of gsDesign2::fixed_design_mb() had no effect because it was not passed to the downstream sub-functions (Merck/gsDesign2#463). A basic developer test could have caught this.

Third, instead of including the above in the tests, I would rather keep them as examples in the help page, where users can readily distinguish the two ways to set ratio, even if they don't review the test files.

I was not requesting that the examples be removed. I completely agree with you that the examples section is the correct place to document this for end users. My request was to use these examples as the basis for automated tests.

#'
#' 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 empirical 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")
if (is.null(ratio)) {
ratio <- attributes(data)$ratio
}
q_e <- ratio / (1 + ratio)
q_c <- 1 - q_e

Expand Down
60 changes: 54 additions & 6 deletions man/wlr.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.