R/DataFunctions.R

#' @title continueRecruitment
#' @md
#' @description This function continues the recruitment of subjects for a set of scenarios
#' based on the Go / NoGo decisions in the simulated trial outcomes of said scenarios.
#' @param n_subjects_add_list A list that contains for each scenario an integer vector for
#' the number of subjects per cohort to be additionally recruited.
#' @param decisions_list A list with decisions per scenario created with
#' \code{\link[bhmbasket]{getGoDecisions}}
#' @param method_name A string for the method name of the analysis the decisions are based on
#' @return An object of class `scenario_list` with the scenario data for each specified scenario.
#' @details
#' This function is intended to be used for analyses with the following work flow:\cr
#' `simulateScenarios()` -> `performAnalyses()` -> `getGoDecisions()`-> \cr
#' `continueRecruitment()` -> `performAnalyses()` -> `getGoDecisions()`-> \cr
#' `continueRecruitment()` -> ...
#'
#' Note that `n_subjects_add_list` takes the additional number of subjects to be recruited,
#' not the overall number of subjects.
#' This way the work flow can be repeated as often as
#' required, which can be useful e.g. for interim analyses.
#' @examples
#' interim_scenarios <- simulateScenarios(
#'   n_subjects_list     = list(c(10, 20, 30)),
#'   response_rates_list = list(rep(0.9, 3)),
#'   n_trials            = 10)
#'
#' interim_analyses <- performAnalyses(
#'   scenario_list       = interim_scenarios,
#'   target_rates        = rep(0.5, 3),
#'   n_mcmc_iterations   = 100)
#'
#' interim_gos <- getGoDecisions(
#'   analyses_list       = interim_analyses,
#'   cohort_names        = c("p_1", "p_2", "p_3"),
#'   evidence_levels     = c(0.5, 0.8, 0.5),
#'   boundary_rules      = quote(c(x[1] > 0.8, x[2] > 0.6, x[3] > 0.7)))
#'
#' scenarios_list <- continueRecruitment(
#'   n_subjects_add_list = list(c(30, 20, 10)),
#'   decisions_list      = interim_gos,
#'   method_name         = "exnex_adj")
#' @seealso
#'  \code{\link[bhmbasket]{simulateScenarios}}
#'  \code{\link[bhmbasket]{performAnalyses}}
#'  \code{\link[bhmbasket]{getGoDecisions}}
#' @rdname continueRecruitment
#' @author Stephan Wojciekowski
#' @export
continueRecruitment <- function (
  
  n_subjects_add_list,
  decisions_list,
  
  method_name
  
) {
  
  error_n_subjects_add_list <- simpleError(
    "Please provide a list of vectors of positive integers for the argument 'n_subjects_add_list'")
  error_decisions_list <- simpleError(
    "Please provide an object of class decision_list for the argument 'decisions_list'")
  error_method_name <- simpleError(paste(
    "Please provide a string naming an analysis method for the argument 'method_name'",
    "Must be one of 'berry', 'exnex', 'exnex_adj', 'pooled', 'stratified'"))
  
  if (missing(n_subjects_add_list))            stop (error_n_subjects_add_list)
  if (missing(decisions_list))                 stop (error_decisions_list)
  if (missing(method_name))                    stop (error_method_name)
  
  method_name <- tryCatch({
    
    match.arg(
      method_name,
      choices    = c('berry', 'exnex', 'exnex_adj', 'pooled', 'stratified'),
      several.ok = FALSE)
    
  }, error = function (e) e)
  
  if (!is.decision_list(decisions_list))       stop (error_decisions_list)
  if (inherits(method_name, "error"))          stop (error_method_name)
  
  if (!is.list(n_subjects_add_list)) {
    n_subjects_add_list <- rep(list(n_subjects_add_list), length(decisions_list))
  }
  
  if (!is.list(n_subjects_add_list) ||
      any(!sapply(n_subjects_add_list, is.non.negative.wholenumber)))
    stop (error_n_subjects_add_list)
  
  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
  ## Note: also some checks hereafter
  
  ## get scenario numbers
  scenario_numbers <- as.numeric(sub("scenario_", "", names(decisions_list)))
  
  if (length(n_subjects_add_list) != length(decisions_list)) {
    stop (simpleError("The lengths of 'n_subjects_add_list' and 'decisions_list' must be equal"))
  }
  
  scenario_list <- vector(mode = "list", length = length(decisions_list))
  names(scenario_list) <- paste0("scenario_", scenario_numbers)
  for (s in seq_along(scenario_list)) {
    
    if (!(method_name %in%
          decisions_list[[s]]$analysis_data$analysis_parameters$method_names)) {
      
      stop (simpleError("Selected method_name not analyzed"))
      
    }
    
    ## get new data, i.e. get new responders and new number of subjects per trial
    
    n_subjects_add <- n_subjects_add_list[[s]]
    response_rates <- decisions_list[[s]]$scenario_data$response_rates
    cohort_names   <- sub("rr_", "", colnames(response_rates))
    
    if (any(response_rates > 0 & response_rates < 1)) {
      
      index_new <- which(response_rates > 0 & response_rates < 1)
      
    } else {
      
      stop (simpleError(paste0(
        "Only historical cohorts in scenario ",
        decisions_list[[s]]$scenario_data$scenario_number)))
      
    }
    
    response_rates_new <- response_rates[, index_new]
    cohort_names_new   <- cohort_names[index_new]
    
    if (!identical(length(n_subjects_add), length(response_rates_new))) {
      stop (simpleError(paste0(
        "The length of n_subjects_add must be equal ",
        "to the length of the response rates that are in (0, 1)")))
    }
    
    n_trials <- decisions_list[[s]]$scenario_data$n_trials
    
    add_scenario <- getScenario(
      n_subjects     = n_subjects_add,
      response_rates = response_rates_new,
      cohort_names   = cohort_names_new,
      n_trials       = n_trials)
    
    ## Combine with existing data
    
    ## get previous decisions
    go_decisions <- decisions_list[[s]]$decisions_list[[method_name]]
    previous_gos <- go_decisions
    
    if ("overall" %in% colnames(go_decisions)) {
      overall_gos  <- go_decisions[, which(colnames(go_decisions) == "overall")]
      go_decisions <- go_decisions[, -which(colnames(go_decisions) == "overall")]
    } else {
      overall_gos <- rep(TRUE, nrow(go_decisions))
    }
    if (!all(index_new %in% as.numeric(sub("decision_", "", colnames(go_decisions))))) {
      stop (simpleError(
        "There must be a decision for each recruiting cohort in the 'decisions_list'"))
    }
    
    ## pick only cohorts that need updating
    go_decisions <- go_decisions[overall_gos, index_new]
    
    ## additional subjects and responders, only those that have overall go
    n_responders_add <- add_scenario$n_responders[overall_gos, ] * go_decisions
    n_subjects_add   <- add_scenario$n_subjects[overall_gos, ] * go_decisions
    
    ## existing cohorts that need updating
    n_responders <- decisions_list[[s]]$scenario_data$n_responders
    n_subjects   <- decisions_list[[s]]$scenario_data$n_subjects
    
    ## combine, only for cohorts that have overall go and need updating
    n_responders[overall_gos, index_new] <- n_responders[overall_gos, index_new] + n_responders_add
    n_subjects[overall_gos, index_new]   <- n_subjects[overall_gos, index_new] + n_subjects_add
    
    ## Saving Scenario
    
    scenario_list[[s]] <- list(
      n_subjects        = n_subjects,
      n_responders      = n_responders,
      response_rates    = response_rates,
      previous_analyses = list(go_decisions   = previous_gos,
                               post_quantiles = decisions_list[[s]]$analysis_data$quantiles_list),
      n_trials          = n_trials)
    
    scenario_list[[s]]$scenario_number <-
      decisions_list[[s]]$scenario_data$scenario_number
    
  }
  
  class(scenario_list) <- "scenario_list"
  
  return (scenario_list)
  
}


#' @title createTrial
#' @description This function creates an object of class `scenario_list`
#' for a single trial outcome, which can subsequently be analyzed with other functions of
#' `bhmbasket`, e.g. \code{\link[bhmbasket]{performAnalyses}}
#' @param n_subjects A vector of integers for the number of subjects in the trial outcome
#' @param n_responders A vector of integers for the number of responders in the trial outcome
#' @return An object of class `scenario_list` with the scenario data for a single trial outcome.
#' @details This function is a wrapper for \code{\link[bhmbasket]{simulateScenarios}} with
#' ```
#' simulateScenarios(
#'  n_subjects_list     = list(n_subjects),
#'  response_rates_list = list(n_responders),
#'  n_trials            = 1)
#' ```
#' @seealso
#'  \code{\link[bhmbasket]{simulateScenarios}}
#'  \code{\link[bhmbasket]{performAnalyses}}
#' @author Stephan Wojciekowski
#' @rdname createTrial
#' @examples
#'  trial_outcome <- createTrial(n_subjects   = c(10, 20, 30, 40),
#'                               n_responders = c( 1,  2,  3,  4))
#' @export
#' @md
createTrial <- function (

  n_subjects,
  n_responders

) {

  error_n_subjects <- simpleError(
    "Please provide a vector of integers for the argument 'error_n_subjects'")
  error_n_responders <- simpleError(
    "Please provide a vector of integers for the argument 'n_responders'")

  if (missing(n_subjects))               stop (error_n_subjects)
  if (missing(n_responders))             stop (error_n_responders)

  if (any(!is.wholenumber(n_subjects)))   stop (error_n_subjects)
  if (any(!is.wholenumber(n_responders))) stop (error_n_responders)

  utils::capture.output({

    trial <- simulateScenarios(
      n_subjects_list     = list(n_subjects),
      response_rates_list = list(n_responders),
      n_trials            = 1)

  })

  return (trial)

}

getInterimPassed <- function (
  
  interim_n_responders,
  interim_n_min
  
) {
  
  interim_passed <- t(apply(interim_n_responders, 1, function (x) {
    x >= interim_n_min
  }))
  
  return (interim_passed)
  
}

getNSubjects <- function (
  
  recruitment_per_month,
  start_date,
  analysis_dates,
  
  date_format = "%m/%d/%Y"
  
) {
  
  recruitment_per_day <- recruitment_per_month * (12 / 365)
  
  start_date     <- as.Date(start_date, format = date_format)
  analysis_dates <- as.Date(analysis_dates, format = date_format)
  
  n_subjects_matrix <- floor((analysis_dates - start_date) %o% recruitment_per_day)
  rownames(n_subjects_matrix) <- as.character(analysis_dates, format = date_format)
  colnames(n_subjects_matrix) <- paste0("cohort_", seq_len(ncol(n_subjects_matrix)))
  
  return (n_subjects_matrix)
  
}

getRecruitment <- function (
  
  n_subjects_required,
  recruitment_per_month,
  start_date,
  
  date_format = "%m/%d/%Y"
  
) {
  
  if (missing(n_subjects_required))
    stop ("Please provide a matrix of non-negative integers for the argument 'n_subjects_required'")
  if (missing(recruitment_per_month))
    stop ("Please provide a vector of non-negative numerics for the argument 'recruitment_per_month'")
  if (missing(start_date))
    stop ("Please provide a string in the format 'date_format' of for the argument 'start_date'")
  
  if (!is.numeric(n_subjects_required) || #!is.matrix(n_subjects_required) ||
      any(!is.wholenumber(n_subjects_required)) || any(n_subjects_required < 0))
    stop ("Please provide a matrix of non-negative integers for the argument 'n_subjects_required'")
  if (!is.numeric(recruitment_per_month) || any(recruitment_per_month < 0))
    stop ("Please provide a vector of non-negative numerics for the argument 'recruitment_per_month'")
  
  ## Get indices of historical cohorts
  hist_index <- recruitment_per_month == 0
  
  recruitment_per_day <- recruitment_per_month[!hist_index] * (12 / 365)
  
  start_date <- as.Date(start_date, format = date_format)
  
  n_subjects_required <- convertVector2Matrix(n_subjects_required)
  
  if (!identical(length(recruitment_per_month), ncol(n_subjects_required)))
    stop (paste0("The number of columns of 'n_subjects_required' ",
                 "and the length of 'recruitment_per_month' must be equal"))
  
  days_required <- apply(convertVector2Matrix(n_subjects_required[, !hist_index]),
                         1, function (n_subj) {
                           max(ceiling(n_subj * recruitment_per_day^-1))
                         })
  
  n_subjects_matrix_0 <- getNSubjects(
    recruitment_per_month = recruitment_per_month[!hist_index],
    start_date            = start_date,
    analysis_dates        = start_date + days_required,
    date_format           = date_format)
  
  n_subjects_matrix <- matrix(NA,
                              ncol = ncol(n_subjects_required),
                              nrow = nrow(n_subjects_required))
  
  n_subjects_matrix[, !hist_index] <- n_subjects_matrix_0
  n_subjects_matrix[, hist_index]  <- n_subjects_required[, hist_index]
  
  rownames(n_subjects_matrix) <- rownames(n_subjects_matrix_0)
  colnames(n_subjects_matrix) <- paste0("cohort_", seq_len(ncol(n_subjects_required)))
  
  return (n_subjects_matrix)
  
}

getResponders <- function (
  
  n_subjects,
  response_rates
  
) {
  
  ## Adjust for working with apply()
  if (is.null(dim(response_rates))) {
    response_rates <- t(as.matrix(response_rates))
  }
  
  n_responders <- t(apply(response_rates, 1,
                          function (x) {stats::rbinom(n    = length(n_subjects),
                                                      size = n_subjects,
                                                      prob = x)}))
  
  return (n_responders)
  
}

getRespondersNonParallel <- function (
  
  response_rates,
  n_subjects,
  
  n_trials
  
) {
  
  n_responders <- t(replicate(n = n_trials, {
    
    stats::rbinom(n    = length(n_subjects), # n_cohorts
                  size = n_subjects,
                  prob = response_rates)
    
    
    
  }))
  
  return (n_responders)
  
}

getScenario <- function (

  n_subjects,
  response_rates,

  cohort_names = seq_along(n_subjects),

  n_trials     = 1e4

) {

  response_rates           <- convertVector2Matrix(response_rates)
  colnames(response_rates) <- paste0("rr_", cohort_names)

  if (any(response_rates < 1 & response_rates > 0)) {

    ## Simulations for new cohorts
    ## A cohort is new if it has a response rate greater than 0 and less than 1.
    ## Response rates equal to 0 or
    ## greater than or equal to 1 will be used as fixed responses.

    new_cohorts <- TRUE
    index_new   <- which(response_rates < 1 & response_rates > 0)

    if (length(n_subjects[index_new]) != length(response_rates[, index_new])) {
      stop ("n_subjects and response_rates must have same length")
    }

    ## Simulate the number of responses without interim analysis
    n_responders <- getRespondersNonParallel(
      response_rates = response_rates[, index_new],
      n_subjects     = n_subjects[index_new],
      n_trials       = n_trials)

  } else {

    ## No new cohorts, as response rates are all greater than or equal to 1

    new_cohorts <- FALSE

  }

  if (any(response_rates >= 1 | response_rates == 0)) {

    ## Historical cohorts

    hist_cohorts <- TRUE
    index_hist   <- which(response_rates >= 1 | response_rates == 0)

    if (length(n_subjects[index_hist]) != length(response_rates[, index_hist])) {
      stop ("n_subjects and response_rates must have same length")
    }

    if (new_cohorts) {

      ## In case of simulated (new) cohorts, the historic responses will be recycled to match
      ## the number of unique trials

      ## Make matrix if necessary
      n_responders_hist <-  matrix(rep(response_rates[index_hist],
                                       each = nrow(n_responders)),
                                   nrow = nrow(n_responders))

    } else {

      ## In case of no simulated cohorts, only one set of fixed responses is needed

      n_responders_hist <- matrix(response_rates[index_hist], nrow = 1)

    }

  } else {

    ## No historical cohorts, as response rates are all smaller than 1

    hist_cohorts <- FALSE

  }

  ## Combine new and historical cohorts as appropriate
  if (new_cohorts & hist_cohorts) {

    n_responders <- cbind(n_responders, n_responders_hist)

  } else if (hist_cohorts) {

    n_responders <- n_responders_hist

  }

  previous_gos <- matrix(TRUE, byrow = TRUE,
                         ncol = length(n_subjects) + 1L,
                         nrow = nrow(n_responders))
  colnames(previous_gos) <- c("overall", paste0("decision_", cohort_names))

  n_subjects <- matrix(n_subjects, byrow = TRUE,
                       ncol = length(n_subjects),
                       nrow = nrow(n_responders))

  colnames(n_subjects)   <- paste0("n_", cohort_names)
  colnames(n_responders) <- paste0("r_", cohort_names)


  ## Create list to return data
  scenario_data <- list(n_subjects        = n_subjects,
                        n_responders      = n_responders,
                        response_rates    = response_rates,
                        previous_analyses = list(go_decisions   = previous_gos,
                                                 post_quantiles = NULL),
                        n_trials          = n_trials)

  return (scenario_data)

}

is.scenario_data <- function (x) {

  if (missing(x)) stop ("Please provide an object for the argument 'x'")

  inherits(x, "decision_list")

  # ## x must be list
  # if (!is.list(x)) {
  #   warning ("x must be list")
  #   return (FALSE)
  # }
  #
  # ## names of x must be as below
  # if (!identical(sort(names(x)), c("n_responders", "n_subjects", "n_trials",
  #                                  "response_rates", "scenario_number", "seed"))) {
  #   warning ("names of x must be c('n_responders', 'n_subjects', 'n_trials',
  #                                  'response_rates', 'scenario_number', 'seed')")
  #   return (FALSE)
  # }
  #
  # ## x$n_subjects must be matrix with non-negative integers
  # if (!is.matrix(x$n_subjects) || !is.numeric(x$n_subjects) ||
  #     !all(is.wholenumber(x$n_subjects)) || !all(x$n_subjects >= 0)) {
  #   warning ("x$n_subjects must be matrix with non-negative integers")
  #   return (FALSE)
  # }
  #
  # ## x$n_responders must be matrix with non-negative integers
  # if (!is.matrix(x$n_responders) || !is.numeric(x$n_responders) ||
  #     !all(is.wholenumber(x$n_responders)) || !all(x$n_responders >= 0)) {
  #   warning ("x$n_responders must be matrix with non-negative integers")
  #   return (FALSE)
  # }
  #
  # ## x$n_subjects and x$n_responders must have same dimension
  # if (!identical(dim(x$n_responders), dim(x$n_subjects))) {
  #   warning ("x$n_subjects and x$n_responders must have same dimension")
  #   return (FALSE)
  # }
  #
  # ## x$response_rates must be matrix with with integers in [0, 1]
  # if (!is.matrix(x$response_rates) || !is.numeric(x$response_rates) ||
  #     !all(x$response_rates >= 0 && x$response_rates <= 1)) {
  #   warning ("x$response_rates must be matrix with with integers in [0, 1]")
  #   return (FALSE)
  # }
  #
  # ## x$response_rates must have same number of columns as x$n_subjects
  # if (!identical(ncol(x$response_rates), ncol(x$n_subjects))) {
  #   warning ("x$response_rates must have same number of columns as x$n_subjects")
  #   return (FALSE)
  # }
  #
  # ## x$response_rates must have number of rows between 1 and nrow(x$n_subjects)
  # if (!nrow(x$response_rates) >= 1 || !nrow(x$response_rates) <= nrow(x$n_subjects)) {
  #   warning ("x$response_rates must have number of rows between 1 and nrow(x$n_subjects)")
  #   return (FALSE)
  # }
  #
  # ## x$n_trials must be positive integer
  # if (!is.numeric(x$n_trials) || !is.wholenumber(x$n_trials) || !x$n_trials > 0) {
  #   warning ("x$n_trials must be positive integer")
  #   return (FALSE)
  # }
  #
  # ## x$n_trials must be equal to nrow(x$n_subjects)
  # if (!isTRUE(all.equal(x$n_trials, nrow(x$n_subjects)))) {
  #   warning ("x$n_trials must be equal to nrow(x$n_subjects)")
  #   return (FALSE)
  # }
  #
  # ## x$seed must be non-negative integer
  # if (!is.numeric(x$seed) || !is.wholenumber(x$seed) || !x$seed >= 0) {
  #   warning ("x$seed must be non-negative integer")
  #   return (FALSE)
  # }
  #
  # ## x$scenario_number must be non-negative integer
  # if (!is.numeric(x$scenario_number) || !is.wholenumber(x$scenario_number) ||
  #     !x$scenario_number >= 0) {
  #   warning ("x$scenario_number must be non-negative integer")
  #   return (FALSE)
  # }
  #
  # return (TRUE)

}

is.scenario_list <- function (x) {

  if (missing(x)) stop ("Please provide an object for the argument 'x'")

  inherits(x, "scenario_list")

}

#' @title loadScenarios
#' @md
#' @description This function loads scenarios saved with \code{\link[bhmbasket]{saveScenarios}}
#' @param scenario_numbers A vector of integers naming the scenario to be loaded
#' @param load_path A string for the directory where the scenarios are being stored,
#' Default: \code{\link[base]{tempfile}}
#' @return Returns an object of class `scenario_list`
#' @rdname loadScenarios
#' @author Stephan Wojciekowski
#' @seealso
#'  \code{\link[bhmbasket]{simulateScenarios}}
#'  \code{\link[bhmbasket]{saveScenarios}}
#'  \code{\link[base]{tempfile}}
#' @examples
#'   scenarios_list <- simulateScenarios(
#'     n_subjects_list     = list(c(10, 20, 30)),
#'     response_rates_list = list(rep(0.9, 3)),
#'     n_trials            = 10)
#'
#'   save_info      <- saveScenarios(scenarios_list)
#'   scenarios_list <- loadScenarios(scenario_numbers = save_info$scenario_numbers,
#'                                   load_path        = save_info$path)
#' @export
loadScenarios <- function (

  scenario_numbers,
  load_path = tempdir()

) {

  if (missing(scenario_numbers))
    stop ("Please provide a vector of positive integers for the argument 'scenario_numbers'")

  if (!is.numeric(scenario_numbers) || !is.wholenumber(scenario_numbers) || scenario_numbers <= 0)
    stop ("Please provide a positive integer for the argument 'scenario_numbers'")
  if (!is.character(load_path) || length(load_path) > 1)
    stop ("Please provide a string for the argument 'load_path'")

  scenario_list <- vector(mode = "list", length = length(scenario_numbers))

  for (s in seq_along(scenario_numbers)) {

    paste0("scenario_data_", scenario_numbers[s], ".rds")

    scenario_list[[s]] <- readRDS(
      file.path(load_path, paste0("scenario_data_", scenario_numbers[s], ".rds")))

    names(scenario_list) <- paste0("scenario_", scenario_numbers)

  }

  class(scenario_list) <- "scenario_list"

  return (scenario_list)

}

#' @title saveScenarios
#' @md
#' @description Saves the scenario data in a newly created or existing directory
#' @param scenario_list An object of class `scenario_list`, e.g. created with `simulateScenarios()`
#' @param save_path A string providing the path for the directory in which the directory of the
#' scenario should be created, Default: \code{\link[base]{tempfile}}
#' @return A named list of length 2 with the scenario numbers and the `save_path`
#' @author Stephan Wojciekowski
#' @seealso
#'  \code{\link[bhmbasket]{simulateScenarios}}
#'  \code{\link[bhmbasket]{loadScenarios}}
#'  \code{\link[base]{tempfile}}
#' @examples
#'   scenarios_list <- simulateScenarios(
#'     n_subjects_list     = list(c(10, 20, 30)),
#'     response_rates_list = list(rep(0.9, 3)),
#'     n_trials            = 10)
#'
#'   save_info      <- saveScenarios(scenarios_list)
#'   scenarios_list <- loadScenarios(scenario_numbers = save_info$scenario_numbers,
#'                                   load_path        = save_info$path)
#' @rdname saveScenarios
#' @export
saveScenarios <- function (
  
  scenario_list,
  save_path = tempdir()
  
) {
  
  if (missing(scenario_list))
    stop ("Please provide an object of class scenario_list for the argument 'scenario_list'")
  
  if (!is.scenario_list(scenario_list))
    stop ("Please provide an object of class scenario_list for the argument 'scenario_list'")
  if (!is.character(save_path) || length(save_path) > 1)
    stop ("Please provide a string for the argument 'save_path'")
  
  if (!dir.exists(save_path)) {
    dir.create(save_path)
  }
  
  scenario_numbers <- sapply(scenario_list, function (x) x$scenario_number)
  
  for (s in seq_along(scenario_list)) {
    
    saveRDS(scenario_list[[s]],
            file = paste0(save_path, "/scenario_data_", scenario_numbers[s], ".rds"))
    
  }
  
  return (list(scenario_numbers = scenario_numbers, path = save_path))
  
}

#' @title simulateScenarios
#' @description This function creates scenarios for the analysis with
#' \code{\link[bhmbasket]{performAnalyses}}.
#' @param n_subjects_list A list that contains for each scenario a vector for
#' the number of subjects per cohort.
#' A single vector can be provided if all scenarios should have the same number of subjects.
#' @param response_rates_list A list that contains for each scenario a vector for
#' the response rates per cohort.
#' @param scenario_numbers A vector of positive integers naming the scenarios,
#' Default: `seq_along(response_rates_list)`
#' @param n_trials An integer indicating the number of trial simulations per response rates,
#' Default: `10000`. If `n_trials` is present in `.GlobalEnv` and `missing(n_trials)`,
#' the globally available value will be used.
#' @return An object of class `scenario_list` with the scenario data for each specified scenario.
#' @details The function simulates trials with binary outcome for each scenario.
#' Integer values for the response rates will be treated as observed outcomes.
#' @author Stephan Wojciekowski
#' @seealso
#'  \code{\link[bhmbasket]{saveScenarios}}
#'  \code{\link[bhmbasket]{createTrial}}
#'  \code{\link[bhmbasket]{performAnalyses}}
#' @rdname simulateScenarios
#' @examples
#'   n_subjects     <- c(10, 20, 30)
#'
#'   rr_negative    <- rep(0.1, 3)
#'   rr_nugget      <- c(0.9, 0.1, 0.1)
#'   rr_positive    <- rep(0.9, 3)
#'
#'   scenarios_list <- simulateScenarios(
#'     n_subjects_list     = list(n_subjects,
#'                                n_subjects,
#'                                n_subjects),
#'     response_rates_list = list(rr_negative,
#'                                rr_nugget,
#'                                rr_positive))
#' @export
#' @md
simulateScenarios <- function (
  
  n_subjects_list,
  response_rates_list,
  scenario_numbers = seq_along(response_rates_list),
  
  n_trials         = 1e4
  
) {
  
  error_n_subjects_list     <- simpleError(
    "Please provide a list of vectors of positive integers for the argument 'n_subjects_list'")
  error_response_rates_list <- simpleError(
    paste("Please provide a list of vectors of non-negative numerics for the argument",
          "'response_rates_list'\n", "Values outside of (0, 1) must be integers"))
  error_n_trials            <- simpleError(
    "Please provide a positive integer for the argument 'n_trials'")
  error_scenario_numbers    <- simpleError(
    "Please provide a vector of positive integers for the argument 'scenario_numbers'")
  
  if (missing(n_subjects_list))                                stop (error_n_subjects_list)
  if (missing(response_rates_list))                            stop (error_response_rates_list)
  
  if (!is.list(response_rates_list) ||
      any(!sapply(response_rates_list, is.numeric)))           stop (error_response_rates_list)
  
  ## put n_subjects as a list if provided as vector
  if (!is.list(n_subjects_list)) {
    n_subjects_list <- rep(list(n_subjects_list), length(response_rates_list))
  }
  
  if (!is.list(n_subjects_list) ||
      any(!sapply(n_subjects_list, is.positive.wholenumber)))  stop (error_n_subjects_list)
  
  if (!is.positive.wholenumber(scenario_numbers))              stop (error_scenario_numbers)
  
  if (!identical(length(scenario_numbers), length(n_subjects_list))) stop (simpleError(
    "'scenario_numbers' and 'n_subjects_list' must have same lenth"
  ))
  
  if (!identical(length(n_subjects_list), length(response_rates_list))) stop (simpleError(
    "'n_subjects_list' and 'response_rates_list' must have same length"))
  
  if (any(!sapply(response_rates_list, function (x) {
    identical(length(response_rates_list[[1]]), length(x))}))) stop (simpleError(
      "All scenarios within a set of scenarios must have the same number of cohorts"))
  
  if (identical(length(response_rates_list[[1]]), 1L)) stop (simpleError(
    "Each scenario must have at least 2 cohorts"))
  
  if (any(!sapply(n_subjects_list, function (x) {
    identical(length(response_rates_list[[1]]), length(x))}))) stop (simpleError(
      "All scenarios within a set of scenarios must have the same number of cohorts"))
  
  if (any(sapply(seq_along(n_subjects_list), function (x) {
    any(n_subjects_list[[x]] < response_rates_list[[x]])})))   stop (simpleError(
      "Values in 'response_rates_list' must not be greater than values in 'n_subjects_list'"))
  
  if (any(!sapply(response_rates_list, function (x) {
    is.numeric(x) & x > 0 & x < 1 |
      is.wholenumber(x) & (x == 0 | x >= 1)})))                stop(error_response_rates_list)
  
  ## check whether n_trials is present in global environment
  if ("n_trials" %in% ls(envir = .GlobalEnv) & missing(n_trials)) {
    n_trials <- get("n_trials", envir = .GlobalEnv)
  }
  
  if (!is.single.positive.wholenumber(n_trials))               stop (error_n_trials)
  
  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
  
  scenario_list    <- vector(mode = "list", length = length(scenario_numbers))
  for (s in seq_along(scenario_numbers)) {
    
    scenario_list[[s]] <- getScenario(
      n_subjects          = n_subjects_list[[s]],
      response_rates      = response_rates_list[[s]],
      n_trials            = n_trials)
    
    scenario_list[[s]]$scenario_number <- scenario_numbers[s]
    
  }
  
  names(scenario_list) <- paste0("scenario_", scenario_numbers)
  class(scenario_list) <- "scenario_list"
  
  return (scenario_list)
  
}

Try the bhmbasket package in your browser

Any scripts or data that you put into this service are public.

bhmbasket documentation built on March 18, 2022, 7:46 p.m.