From 03539de22c0347e280aa1ffd3bcf994e8d1fbe75 Mon Sep 17 00:00:00 2001 From: Benedikt Sommer Date: Mon, 10 Nov 2025 10:34:27 +0100 Subject: [PATCH 1/3] fixes --- DESCRIPTION | 17 +++++-- Makefile | 4 +- R/Trial.R | 10 ++-- R/carts-package.R | 2 +- R/estimators.R | 4 +- R/optimization.R | 16 +++--- R/outcome_models.R | 96 +++++++++++++++++------------------- R/trial_run.R | 2 - man/Trial.Rd | 16 +++--- man/bisection.Rd | 4 -- man/carts-package.Rd | 9 +++- man/est_adj.Rd | 2 +- man/est_glm.Rd | 2 +- man/optim_sa.Rd | 6 --- man/outcome_binary.Rd | 2 +- man/outcome_continuous.Rd | 2 +- man/outcome_count.Rd | 2 +- man/outcome_lp.Rd | 4 -- man/outcome_phreg.Rd | 48 +----------------- man/outcome_recurrent.Rd | 4 -- man/trial.estimates-class.Rd | 2 - 21 files changed, 97 insertions(+), 157 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0e76d23..6afb7e1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,13 +10,22 @@ Authors@R: c(person(given = "Benedikt", family = "Holst", role = c("aut"), email = "klaus@holst.it"), - person(given = "Foroogh", + person(given = "Foroogh", family = "Shamsi", role = c("aut"), - email = "foroogh.shamsi1@gmail.com")) + email = "foroogh.shamsi1@gmail.com"), + person("Novo Nordisk A/S", role = "cph") + ) Author: Benedikt Sommer [aut, cre], Klaus K. Holst [aut], Foroogh Shamsi [aut] Maintainer: Benedikt Sommer -Description: Monte Carlo simulation framework for different randomized clinical trial designs with a special emphasis on estimators based on covariate adjustment. +Description: Monte Carlo simulation framework for different randomized clinical + trial designs with a special emphasis on estimators based on covariate + adjustment. The estimation of the minimum sample-size required to reach a + specified statistical power for a given estimator uses bisection to find an + initial rough estimate, followed by stochastic approximation (Robbins-Monro + (1951) ) to improve the estimate, and finally, + a grid search for refinement in the neighborhood of the current best + solution. License: MIT + file LICENSE Depends: R (>= 4.1), @@ -51,5 +60,5 @@ URL: https://novonordisk-opensource.github.io/carts/, https://github.com/NovoNor VignetteBuilder: knitr ByteCompile: yes Encoding: UTF-8 -Roxygen: list(markdown = TRUE) +Roxygen: list(markdown = TRUE, r6 = TRUE) RoxygenNote: 7.3.3 diff --git a/Makefile b/Makefile index ec062f5..8369e4b 100644 --- a/Makefile +++ b/Makefile @@ -67,10 +67,10 @@ check: lint @_R_CHECK_FORCE_SUGGESTS_=0 echo 'future::plan("multicore"); res <- rcmdcheck::rcmdcheck(".", build_args=c("--no-build-vignettes"), args=c("--ignore-vignettes"))' | $(R) check-examples: - @_R_CHECK_FORCE_SUGGESTS_=0 echo 'future::plan("multicore"); devtools::run_examples(".", run_donttest = TRUE, run_dontrun = FALSE)' | $(R) + @_R_CHECK_FORCE_SUGGESTS_=0 echo 'future::plan("multicore"); devtools::run_examples(".", run_donttest = TRUE, run_dontrun = TRUE)' | $(R) check-cran: build - @$(R) CMD check $(BUILD_DIR)/$(PKG)_$(GETVER).tar.gz --timings --as-cran --no-multiarch --run-donttest + @$(R) CMD check $(BUILD_DIR)/$(PKG)_$(GETVER).tar.gz --timings --as-cran --no-multiarch in: install install: diff --git a/R/Trial.R b/R/Trial.R index c21c1b5..6d4cb07 100644 --- a/R/Trial.R +++ b/R/Trial.R @@ -11,7 +11,7 @@ #' for decision-making #' @author Klaus Kähler Holst, Benedikt Sommer #' @examples -#' \dontrun{ # don't run because of high computational time +#' \donttest{ #' trial <- Trial$new( #' covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), #' outcome = setargs(outcome_count, par = c(1, 0.5, 1), overdispersion = 0.7) @@ -320,7 +320,7 @@ Trial <- R6::R6Class("Trial", #nolint #' the simulation. The return object is also assigned to the `estimates` #' field of this Trial class object (see examples). #' @examples - #' \dontrun{ # don't run because of high computational time + #' \donttest{ #' # future::plan("multicore") #' trial <- Trial$new( #' covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)), @@ -366,7 +366,7 @@ Trial <- R6::R6Class("Trial", #nolint #' estimator. The behavior of passing arguments to lower level functions is #' identical to [Trial$run()][Trial]. #' @examples - #' \dontrun{ # don't run because of high computational time + #' \donttest{ #' # toy examples with small number of Monte-Carlo replicates #' # future::plan("multicore") #' trial <- Trial$new( @@ -407,7 +407,7 @@ Trial <- R6::R6Class("Trial", #nolint #' statistical power with a specified estimator. An initial rough estimate #' is obtained via bisection, followed by a #' stochastic approximation (Robbins-Monro) algorithm, and finally, a grid - #' search (refinement step) in the neighbourhood of the current best + #' search (refinement step) in the neighborhood of the current best #' solution. #' #' Note that the estimation procedure for the sample size will not populate @@ -438,7 +438,7 @@ Trial <- R6::R6Class("Trial", #nolint #' @return samplesize_estimate S3 object #' @author Klaus Kähler Holst #' @examples - #' \dontrun{ # don't run because of high computational time + #' \donttest{ #' trial <- Trial$new( #' covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)), #' outcome = \(data, ate, sd) with(data, rnorm(nrow(data), a * ate, sd)), diff --git a/R/carts-package.R b/R/carts-package.R index 1d9270c..0ab4def 100644 --- a/R/carts-package.R +++ b/R/carts-package.R @@ -24,7 +24,7 @@ #' @useDynLib carts, .registration=TRUE #' @author Benedikt Sommer, Klaus Holst, Foroogh Shamsi #' @examples -#' \dontrun{ # don't run because of high computational time +#' \donttest{ #' trial <- Trial$new( #' covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), #' outcome = setargs(outcome_count, par = c(1, 0.5, 1), overdispersion = 0.7) diff --git a/R/estimators.R b/R/estimators.R index 614038f..9566a4a 100644 --- a/R/estimators.R +++ b/R/estimators.R @@ -20,7 +20,7 @@ #' @author Klaus Kähler Holst #' @export #' @examples -#' \dontrun{ +#' \donttest{ #' trial <- Trial$new( #' covariates = function(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), #' outcome = setargs(outcome_count, @@ -166,7 +166,7 @@ est_glmbin <- function(...) { #' @author Klaus Kähler Holst #' @export #' @examples -#' \dontrun{ # don't run because of high computational time +#' \donttest{ #' trial <- Trial$new( #' covariates = function(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), #' outcome = setargs(outcome_count, diff --git a/R/optimization.R b/R/optimization.R index e4b54d6..3ad0e93 100644 --- a/R/optimization.R +++ b/R/optimization.R @@ -10,9 +10,9 @@ #' optimization #' @param ... additional arguments passed to `f` #' @return numeric specifying the root -#' @examples -#' testf <- function(x) (x - sqrt(2)) -#' carts:::bisection(testf, c(0, 1000), niter = 30) +# #' @examples +# #' testf <- function(x) (x - sqrt(2)) +# #' bisection(testf, c(0, 1000), niter = 30) bisection <- function(f, interval, niter = 6, tol = 1e-12, verbose = TRUE, ...) { interval <- sort(interval) @@ -98,11 +98,11 @@ bisection <- function(f, interval, niter = 6, tol = 1e-12, #' #' Dupač, V., & Herkenrath, U. (1984). On integer stochastic approximation. #' Aplikace matematiky, 29(5), 372-383. -#' @examples -#' # Finding approximate median u, P(X<=u) = .5, of X~Exp(1): -#' f <- function(x) mean(rexp(10) <= x) - 0.5 -#' res <- carts:::optim_sa(f, 0, control=list(niter=2000, alpha=.5)) -#' res$estimate +# #' @examples +# #' # Finding approximate median u, P(X<=u) = .5, of X~Exp(1): +# #' f <- function(x) mean(rexp(10) <= x) - 0.5 +# #' res <- optim_sa(f, 0, control=list(niter=2000, alpha=.5)) +# #' res$estimate optim_sa <- function(f, init = 0, #nolint function.args = list(), ..., diff --git a/R/outcome_models.R b/R/outcome_models.R index cccf2ac..07a546c 100644 --- a/R/outcome_models.R +++ b/R/outcome_models.R @@ -37,8 +37,6 @@ NULL #' mean is given as a function) #' @param remove variables that will be removed from input data (if formula is #' not specified) -#' @seealso [outcome_count] [outcome_binary] [outcome_continuous] -#' [outcome_phreg] outcome_lp <- function(data, mean = NULL, par = NULL, @@ -120,7 +118,7 @@ outcome_lp <- function(data, #' vector or a function of the covariates 'x' with an extra column 'rate' #' holding the rate parameter 'rate' #' @param ... Additional arguments passed to `mean` and `exposure` function -#' @seealso [outcome_binary] [outcome_continuous] [outcome_lp] +#' @seealso [outcome_binary] [outcome_continuous] #' @examples #' covariates <- function(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)) #' trial <- Trial$new(covariates = covariates, outcome = outcome_count) @@ -192,7 +190,7 @@ outcome_count <- function(data, #' g(\text{par}^\top X)} where \eqn{X} is the design matrix specified by the #' formula, and \eqn{g} is the link function specified by the family argument #' @param family exponential family (default `binomial(logit)`) -#' @seealso [outcome_count] [outcome_lp] [outcome_continuous] +#' @seealso [outcome_count] [outcome_continuous] #' @export #' @examples #' trial <- Trial$new( @@ -252,7 +250,7 @@ outcome_binary <- function(data, #' variable. This term is in addition to the measurement error introduced by #' the `sd` argument. #' @param family exponential family (default `gaussian(identity)`) -#' @seealso [outcome_count] [outcome_binary] [outcome_lp] +#' @seealso [outcome_count] [outcome_binary] #' @export #' @examples #' trial <- Trial$new( @@ -313,51 +311,49 @@ outcome_continuous <- function(data, #' @param cens.lp censoring linear predictor argument (formula or function) #' @param cens.par list of censoring model parameters #' @param ... Additional arguments to [outcome_lp] -#' @return function (random generator) #' @author Klaus Kähler Holst -#' @seealso [outcome_count] [outcome_lp] [outcome_binary] [outcome_continuous] -#' @examples -#' \dontrun{ -#' outcome_phreg <- carts:::outcome_phreg -#' library("survival") -#' data(pbc, package = "survival") -#' pbc0 <- na.omit(pbc) |> -#' transform(trt = factor(trt, labels = c("Active", "Placebo")) |> -#' relevel(ref = "Placebo")) -#' -#' fit1 <- mets::phreg(Surv(time, status > 0) ~ age + sex * trt, data = pbc0) -#' -#' covar <- covar_bootstrap(pbc0, subset = c("age", "sex")) %join% -#' function(n, ...) data.frame(trt = sample(pbc0$trt, n, replace = TRUE)) -#' -#' outcome <- setargs( -#' outcome_phreg, -#' model = fit1, -#' par = list("trtActive" = 0) -#' ) -#' -#' xx <- covar(5e3) -#' pbc1 <- outcome(xx) |> cbind(xx) -#' mets::phreg(formula(fit1), data = pbc1) -#' -#' -#' ## Introducing additional interactions -#' fit2 <- mets::phreg(Surv(time, status > 0) ~ age + sex + trt, data = pbc0) -#' -#' outcome <- setallargs( -#' outcome_phreg, -#' model = fit2, -#' par = list("trtActive" = -.5, "age:trtActive" = 0), -#' treatment = "trt", -#' default.parameter = -0.2 -#' ) -#' -#' xx <- covar(1e4) -#' attr(outcome(xx), "par") -#' pbc1 <- outcome(xx) |> cbind(xx) -#' mets::phreg(Surv(time, status) ~ (age + sex) * trt, pbc1) -#' rm(pbc1, xx) -#' } +# #' @examples +# #' \donttest{ +# #' outcome_phreg <- carts:::outcome_phreg +# #' library("survival") +# #' data(pbc, package = "survival") +# #' pbc0 <- na.omit(pbc) |> +# #' transform(trt = factor(trt, labels = c("Active", "Placebo")) |> +# #' relevel(ref = "Placebo")) +# #' +# #' fit1 <- mets::phreg(Surv(time, status > 0) ~ age + sex * trt, data = pbc0) +# #' +# #' covar <- covar_bootstrap(pbc0, subset = c("age", "sex")) %join% +# #' function(n, ...) data.frame(trt = sample(pbc0$trt, n, replace = TRUE)) +# #' +# #' outcome <- setargs( +# #' outcome_phreg, +# #' model = fit1, +# #' par = list("trtActive" = 0) +# #' ) +# #' +# #' xx <- covar(5e3) +# #' pbc1 <- outcome(xx) |> cbind(xx) +# #' mets::phreg(formula(fit1), data = pbc1) +# #' +# #' +# #' ## Introducing additional interactions +# #' fit2 <- mets::phreg(Surv(time, status > 0) ~ age + sex + trt, data = pbc0) +# #' +# #' outcome <- setallargs( +# #' outcome_phreg, +# #' model = fit2, +# #' par = list("trtActive" = -.5, "age:trtActive" = 0), +# #' treatment = "trt", +# #' default.parameter = -0.2 +# #' ) +# #' +# #' xx <- covar(1e4) +# #' attr(outcome(xx), "par") +# #' pbc1 <- outcome(xx) |> cbind(xx) +# #' mets::phreg(Surv(time, status) ~ (age + sex) * trt, pbc1) +# #' rm(pbc1, xx) +# #' } outcome_phreg <- function(data, lp = NULL, par = NULL, @@ -429,8 +425,6 @@ outcome_phreg <- function(data, #' @param cens.par optional list of censoring model parameters #' @param ... Additional arguments to [outcome_lp] #' @return function (random generator) -#' @seealso [outcome_count] [outcome_lp] [outcome_binary] [outcome_continuous] -#' [outcome_phreg] outcome_recurrent <- function(data, lp = NULL, par = NULL, diff --git a/R/trial_run.R b/R/trial_run.R index 807995f..e3dde76 100644 --- a/R/trial_run.R +++ b/R/trial_run.R @@ -163,8 +163,6 @@ print.trial.estimates <- function(x, ...) { #' of class \code{trial.estimates}: #' \describe{ #' \item{\code{print}}{Basic print method.} -#' \item{\code{summary}}{Apply decision-making to estimates of each run and -#' estimator.} #' } #' @aliases trial.estimates-class #' @examples diff --git a/man/Trial.Rd b/man/Trial.Rd index 2cca053..5ae9b2c 100644 --- a/man/Trial.Rd +++ b/man/Trial.Rd @@ -8,7 +8,7 @@ Simulation of RCT with flexible covariates distributions simulation. } \examples{ -\dontrun{ # don't run because of high computational time +\donttest{ trial <- Trial$new( covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), outcome = setargs(outcome_count, par = c(1, 0.5, 1), overdispersion = 0.7) @@ -169,7 +169,7 @@ trial$simulate(n) ## Method `Trial$run` ## ------------------------------------------------ -\dontrun{ # don't run because of high computational time +\donttest{ # future::plan("multicore") trial <- Trial$new( covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)), @@ -207,7 +207,7 @@ trial$run(n = 100, R = 50, estimators = est_glm(robust = FALSE)) ## Method `Trial$estimate_power` ## ------------------------------------------------ -\dontrun{ # don't run because of high computational time +\donttest{ # toy examples with small number of Monte-Carlo replicates # future::plan("multicore") trial <- Trial$new( @@ -237,7 +237,7 @@ trial$estimate_power(n = 100, R = 20, ## Method `Trial$estimate_samplesize` ## ------------------------------------------------ -\dontrun{ # don't run because of high computational time +\donttest{ trial <- Trial$new( covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)), outcome = \(data, ate, sd) with(data, rnorm(nrow(data), a * ate, sd)), @@ -721,7 +721,7 @@ field of this Trial class object (see examples). } \subsection{Examples}{ \if{html}{\out{
}} -\preformatted{\dontrun{ # don't run because of high computational time +\preformatted{\donttest{ # future::plan("multicore") trial <- Trial$new( covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)), @@ -797,7 +797,7 @@ numeric } \subsection{Examples}{ \if{html}{\out{
}} -\preformatted{\dontrun{ # don't run because of high computational time +\preformatted{\donttest{ # toy examples with small number of Monte-Carlo replicates # future::plan("multicore") trial <- Trial$new( @@ -836,7 +836,7 @@ Estimate the minimum sample-size required to reach a desired statistical power with a specified estimator. An initial rough estimate is obtained via bisection, followed by a stochastic approximation (Robbins-Monro) algorithm, and finally, a grid -search (refinement step) in the neighbourhood of the current best +search (refinement step) in the neighborhood of the current best solution. Note that the estimation procedure for the sample size will not populate @@ -907,7 +907,7 @@ samplesize_estimate S3 object } \subsection{Examples}{ \if{html}{\out{
}} -\preformatted{\dontrun{ # don't run because of high computational time +\preformatted{\donttest{ trial <- Trial$new( covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)), outcome = \(data, ate, sd) with(data, rnorm(nrow(data), a * ate, sd)), diff --git a/man/bisection.Rd b/man/bisection.Rd index f65626b..36a2302 100644 --- a/man/bisection.Rd +++ b/man/bisection.Rd @@ -28,7 +28,3 @@ numeric specifying the root \description{ Root finding by bisection } -\examples{ -testf <- function(x) (x - sqrt(2)) -carts:::bisection(testf, c(0, 1000), niter = 30) -} diff --git a/man/carts-package.Rd b/man/carts-package.Rd index 0e1f4d4..a998c65 100644 --- a/man/carts-package.Rd +++ b/man/carts-package.Rd @@ -8,14 +8,14 @@ \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} -Monte Carlo simulation framework for different randomized clinical trial designs with a special emphasis on estimators based on covariate adjustment. +Monte Carlo simulation framework for different randomized clinical trial designs with a special emphasis on estimators based on covariate adjustment. The estimation of the minimum sample-size required to reach a specified statistical power for a given estimator uses bisection to find an initial rough estimate, followed by stochastic approximation (Robbins-Monro (1951) \doi{10.1214/aoms/1177729586}) to improve the estimate, and finally, a grid search for refinement in the neighborhood of the current best solution. Monte Carlo simulation framework for different randomized clinical trial designs with a special emphasis on estimators based on covariate adjustment. } \examples{ -\dontrun{ # don't run because of high computational time +\donttest{ trial <- Trial$new( covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), outcome = setargs(outcome_count, par = c(1, 0.5, 1), overdispersion = 0.7) @@ -48,6 +48,11 @@ Authors: \item Foroogh Shamsi \email{foroogh.shamsi1@gmail.com} } +Other contributors: +\itemize{ + \item Novo Nordisk A/S [copyright holder] +} + Benedikt Sommer, Klaus Holst, Foroogh Shamsi } diff --git a/man/est_adj.Rd b/man/est_adj.Rd index 0200ba8..8d5add9 100644 --- a/man/est_adj.Rd +++ b/man/est_adj.Rd @@ -76,7 +76,7 @@ numeric variable or corresponds to the first level when the treatment variable is encoded as a factor. } \examples{ -\dontrun{ # don't run because of high computational time +\donttest{ trial <- Trial$new( covariates = function(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), outcome = setargs(outcome_count, diff --git a/man/est_glm.Rd b/man/est_glm.Rd index 8979804..b4d45de 100644 --- a/man/est_glm.Rd +++ b/man/est_glm.Rd @@ -50,7 +50,7 @@ estimator is a function with a single argument (data) and returns the treatment effect estimate, which is estimated with \link[lava:estimate.default]{lava::estimate} } \examples{ -\dontrun{ +\donttest{ trial <- Trial$new( covariates = function(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)), outcome = setargs(outcome_count, diff --git a/man/optim_sa.Rd b/man/optim_sa.Rd index dac556d..f52a6b6 100644 --- a/man/optim_sa.Rd +++ b/man/optim_sa.Rd @@ -79,9 +79,3 @@ approximation method is then applied directly on \eqn{g}. Dupač, V., & Herkenrath, U. (1984). On integer stochastic approximation. Aplikace matematiky, 29(5), 372-383. } -\examples{ -# Finding approximate median u, P(X<=u) = .5, of X~Exp(1): -f <- function(x) mean(rexp(10) <= x) - 0.5 -res <- carts:::optim_sa(f, 0, control=list(niter=2000, alpha=.5)) -res$estimate -} diff --git a/man/outcome_binary.Rd b/man/outcome_binary.Rd index df4055b..b96cc8b 100644 --- a/man/outcome_binary.Rd +++ b/man/outcome_binary.Rd @@ -70,5 +70,5 @@ trial$simulate(1e4, ) |> est() } \seealso{ -\link{outcome_count} \link{outcome_lp} \link{outcome_continuous} +\link{outcome_count} \link{outcome_continuous} } diff --git a/man/outcome_continuous.Rd b/man/outcome_continuous.Rd index 0e8850e..177ea84 100644 --- a/man/outcome_continuous.Rd +++ b/man/outcome_continuous.Rd @@ -81,5 +81,5 @@ trial$simulate(1e4, ) |> est() } \seealso{ -\link{outcome_count} \link{outcome_binary} \link{outcome_lp} +\link{outcome_count} \link{outcome_binary} } diff --git a/man/outcome_count.Rd b/man/outcome_count.Rd index 05f2dc3..4b5f08b 100644 --- a/man/outcome_count.Rd +++ b/man/outcome_count.Rd @@ -81,5 +81,5 @@ trial$simulate(1e4, exposure = function(dd) 1 - 0.5 * dd$a) |> head() } \seealso{ -\link{outcome_binary} \link{outcome_continuous} \link{outcome_lp} +\link{outcome_binary} \link{outcome_continuous} } diff --git a/man/outcome_lp.Rd b/man/outcome_lp.Rd index 24794d6..0e659df 100644 --- a/man/outcome_lp.Rd +++ b/man/outcome_lp.Rd @@ -58,7 +58,3 @@ data.table Calculate linear predictor \deqn{\text{par}^\top X} where \eqn{X} is the design matrix specified by the formula } -\seealso{ -\link{outcome_count} \link{outcome_binary} \link{outcome_continuous} -\link{outcome_phreg} -} diff --git a/man/outcome_phreg.Rd b/man/outcome_phreg.Rd index ac94cb8..69edd4b 100644 --- a/man/outcome_phreg.Rd +++ b/man/outcome_phreg.Rd @@ -41,57 +41,11 @@ not specified).} \item{...}{Additional arguments to \link{outcome_lp}} } \value{ -function (random generator) +data.table } \description{ Outcome model for time-to-event end-points (proportional hazards) } -\examples{ -\dontrun{ -outcome_phreg <- carts:::outcome_phreg -library("survival") -data(pbc, package = "survival") -pbc0 <- na.omit(pbc) |> - transform(trt = factor(trt, labels = c("Active", "Placebo")) |> - relevel(ref = "Placebo")) - -fit1 <- mets::phreg(Surv(time, status > 0) ~ age + sex * trt, data = pbc0) - -covar <- covar_bootstrap(pbc0, subset = c("age", "sex")) \%join\% - function(n, ...) data.frame(trt = sample(pbc0$trt, n, replace = TRUE)) - -outcome <- setargs( - outcome_phreg, - model = fit1, - par = list("trtActive" = 0) -) - -xx <- covar(5e3) -pbc1 <- outcome(xx) |> cbind(xx) -mets::phreg(formula(fit1), data = pbc1) - - -## Introducing additional interactions -fit2 <- mets::phreg(Surv(time, status > 0) ~ age + sex + trt, data = pbc0) - -outcome <- setallargs( - outcome_phreg, - model = fit2, - par = list("trtActive" = -.5, "age:trtActive" = 0), - treatment = "trt", - default.parameter = -0.2 -) - -xx <- covar(1e4) -attr(outcome(xx), "par") -pbc1 <- outcome(xx) |> cbind(xx) -mets::phreg(Surv(time, status) ~ (age + sex) * trt, pbc1) -rm(pbc1, xx) -} -} -\seealso{ -\link{outcome_count} \link{outcome_lp} \link{outcome_binary} \link{outcome_continuous} -} \author{ Klaus Kähler Holst } diff --git a/man/outcome_recurrent.Rd b/man/outcome_recurrent.Rd index fc15087..c3fdcc2 100644 --- a/man/outcome_recurrent.Rd +++ b/man/outcome_recurrent.Rd @@ -58,7 +58,3 @@ function (random generator) This function is still in an experimental state where the interface and functionality might change in the future } -\seealso{ -\link{outcome_count} \link{outcome_lp} \link{outcome_binary} \link{outcome_continuous} -\link{outcome_phreg} -} diff --git a/man/trial.estimates-class.Rd b/man/trial.estimates-class.Rd index c7e6631..897e423 100644 --- a/man/trial.estimates-class.Rd +++ b/man/trial.estimates-class.Rd @@ -28,8 +28,6 @@ The following S3 generic functions are available for an object of class \code{trial.estimates}: \describe{ \item{\code{print}}{Basic print method.} -\item{\code{summary}}{Apply decision-making to estimates of each run and -estimator.} } } From fea06a884bdb69bd545bc761e49791ea0847612f Mon Sep 17 00:00:00 2001 From: Benedikt Sommer Date: Tue, 11 Nov 2025 09:48:18 +0100 Subject: [PATCH 2/3] add references --- DESCRIPTION | 17 ++++++++++------- R/carts-package.R | 6 +----- R/estimators.R | 18 +++++++++++++----- man/carts-package.Rd | 8 +------- man/est_adj.Rd | 8 +++++++- man/est_glm.Rd | 14 ++++++++++---- 6 files changed, 42 insertions(+), 29 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6afb7e1..6e98c6a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,16 +16,19 @@ Authors@R: c(person(given = "Benedikt", email = "foroogh.shamsi1@gmail.com"), person("Novo Nordisk A/S", role = "cph") ) -Author: Benedikt Sommer [aut, cre], Klaus K. Holst [aut], Foroogh Shamsi [aut] Maintainer: Benedikt Sommer Description: Monte Carlo simulation framework for different randomized clinical trial designs with a special emphasis on estimators based on covariate - adjustment. The estimation of the minimum sample-size required to reach a - specified statistical power for a given estimator uses bisection to find an - initial rough estimate, followed by stochastic approximation (Robbins-Monro - (1951) ) to improve the estimate, and finally, - a grid search for refinement in the neighborhood of the current best - solution. + adjustment. The package implements regression-based covariate adjustment + (Rosenblum & van der Laan (2010) ) and a + one-step estimator (Van Lancker et al (2024) + ) for trials with continuous, binary and + count outcomes. The estimation of the minimum sample-size required to reach + a specified statistical power for a given estimator uses bisection to find + an initial rough estimate, followed by stochastic approximation + (Robbins-Monro (1951) ) to improve the + estimate, and finally, a grid search to refine the estimate in the + neighborhood of the current best solution. License: MIT + file LICENSE Depends: R (>= 4.1), diff --git a/R/carts-package.R b/R/carts-package.R index 0ab4def..258c044 100644 --- a/R/carts-package.R +++ b/R/carts-package.R @@ -3,9 +3,6 @@ #' @title Simulation-Based Assessment of Covariate Adjustment in Randomized #' Trials -#' @description Monte Carlo simulation framework for different randomized -#' clinical trial designs with a special emphasis on estimators based on -#' covariate adjustment. #' @name carts-package #' @importFrom stats glm poisson binomial gaussian rpois rnbinom pnbinom rbinom #' rgamma nlminb na.omit coef update qnorm model.offset model.frame as.formula @@ -20,9 +17,8 @@ #' @importFrom methods formalArgs #' @importFrom R6 R6Class #' @aliases carts-package carts -#' @keywords package #' @useDynLib carts, .registration=TRUE -#' @author Benedikt Sommer, Klaus Holst, Foroogh Shamsi +#' @keywords package #' @examples #' \donttest{ #' trial <- Trial$new( diff --git a/R/estimators.R b/R/estimators.R index 9566a4a..5fa24bd 100644 --- a/R/estimators.R +++ b/R/estimators.R @@ -1,8 +1,9 @@ #' @title Construct estimator for the treatment effect in RCT -#' @description Returns an estimator for the estimation of a treatment effect -#' with parametric models ([stats::glm] and [MASS::glm.nb]). The returned -#' estimator is a function with a single argument (data) and returns the -#' treatment effect estimate, which is estimated with [lava::estimate] +#' @description Regression-based covariate adjustment as described by Rosenblum +#' & van der Laan (2010). Standard errors are estimated with the Hubert-White +#' sandwich estimator, instead using the efficient influence function as +#' described in the paper. Available parametric models are ([stats::glm] and +#' [MASS::glm.nb]). #' @param response (character) Response variable #' @param treatment (character) Treatment variable. Additional care must be #' taken when the treatment variable is encoded as a factor (see examples). @@ -16,6 +17,9 @@ #' @param ... Additional arguments to [lava::estimate] #' @return function #' @seealso [Trial] +#' @references Rosenblum & van der Laan (2010) Simple, Efficient Estimators of +#' Treatment Effects in Randomized Trials Using Generalized Linear Models to +#' Leverage Baseline Variables, The International Journal of Biostatistics #' @aliases est_glm est_gee est_geebin est_glmbin #' @author Klaus Kähler Holst #' @export @@ -122,7 +126,11 @@ est_glmbin <- function(...) { #' @description Efficient estimator of the treatment effect based on the #' efficient influence function. This involves a model for the conditional -#' mean of the outcome variable given covariates (Q-model). +#' mean of the outcome variable given covariates (Q-model). The implementation +#' is a one-step estimator as described by Van Lancker et al (2024). +#' @references Van Lancker et al (2024) Automated, efficient and model-free +#' inference for randomized clinical trials via data-driven covariate +#' adjustment, arXiv:2404.11150 #' @title Construct estimator for the treatment effect in RCT based on covariate #' adjustment #' @param response (character, formula, [targeted::learner]) The default diff --git a/man/carts-package.Rd b/man/carts-package.Rd index a998c65..cae42f1 100644 --- a/man/carts-package.Rd +++ b/man/carts-package.Rd @@ -8,11 +8,7 @@ \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} -Monte Carlo simulation framework for different randomized clinical trial designs with a special emphasis on estimators based on covariate adjustment. The estimation of the minimum sample-size required to reach a specified statistical power for a given estimator uses bisection to find an initial rough estimate, followed by stochastic approximation (Robbins-Monro (1951) \doi{10.1214/aoms/1177729586}) to improve the estimate, and finally, a grid search for refinement in the neighborhood of the current best solution. - -Monte Carlo simulation framework for different randomized -clinical trial designs with a special emphasis on estimators based on -covariate adjustment. +Monte Carlo simulation framework for different randomized clinical trial designs with a special emphasis on estimators based on covariate adjustment. The package implements regression-based covariate adjustment (Rosenblum & van der Laan (2010) \doi{10.2202/1557-4679.1138}) and a one-step estimator (Van Lancker et al (2024) \doi{10.48550/arXiv.2404.11150}) for trials with continuous, binary and count outcomes. The estimation of the minimum sample-size required to reach a specified statistical power for a given estimator uses bisection to find an initial rough estimate, followed by stochastic approximation (Robbins-Monro (1951) \doi{10.1214/aoms/1177729586}) to improve the estimate, and finally, a grid search to refine the estimate in the neighborhood of the current best solution. } \examples{ \donttest{ @@ -53,8 +49,6 @@ Other contributors: \item Novo Nordisk A/S [copyright holder] } - -Benedikt Sommer, Klaus Holst, Foroogh Shamsi } \keyword{internal} \keyword{package} diff --git a/man/est_adj.Rd b/man/est_adj.Rd index 8d5add9..04bf5a3 100644 --- a/man/est_adj.Rd +++ b/man/est_adj.Rd @@ -64,7 +64,8 @@ function \description{ Efficient estimator of the treatment effect based on the efficient influence function. This involves a model for the conditional -mean of the outcome variable given covariates (Q-model). +mean of the outcome variable given covariates (Q-model). The implementation +is a one-step estimator as described by Van Lancker et al (2024). } \details{ The user-defined function for \code{treatment.effect} needs to accept a @@ -123,6 +124,11 @@ dd_factor$a <- factor(dd_factor$a, levels = c(1, 0)) estimator(dd_factor) # E[Y(1)] - E[Y(0)] } } +\references{ +Van Lancker et al (2024) Automated, efficient and model-free +inference for randomized clinical trials via data-driven covariate +adjustment, arXiv:2404.11150 +} \seealso{ \link{Trial} \link{est_glm} } diff --git a/man/est_glm.Rd b/man/est_glm.Rd index b4d45de..4ab675f 100644 --- a/man/est_glm.Rd +++ b/man/est_glm.Rd @@ -44,10 +44,11 @@ taken when the treatment variable is encoded as a factor (see examples).} function } \description{ -Returns an estimator for the estimation of a treatment effect -with parametric models (\link[stats:glm]{stats::glm} and \link[MASS:glm.nb]{MASS::glm.nb}). The returned -estimator is a function with a single argument (data) and returns the -treatment effect estimate, which is estimated with \link[lava:estimate.default]{lava::estimate} +Regression-based covariate adjustment as described by Rosenblum +& van der Laan (2010). Standard errors are estimated with the Hubert-White +sandwich estimator, instead using the efficient influence function as +described in the paper. Available parametric models are (\link[stats:glm]{stats::glm} and +\link[MASS:glm.nb]{MASS::glm.nb}). } \examples{ \donttest{ @@ -90,6 +91,11 @@ dd_factor$a <- as.factor(dd_factor$a) est_glm(family = poisson, target.parameter = "a1")(dd_factor) } } +\references{ +Rosenblum & van der Laan (2010) Simple, Efficient Estimators of +Treatment Effects in Randomized Trials Using Generalized Linear Models to +Leverage Baseline Variables, The International Journal of Biostatistics +} \seealso{ \link{Trial} } From 74481e4c36c377c0a6bba9af11b84548f6287793 Mon Sep 17 00:00:00 2001 From: Benedikt Sommer Date: Tue, 11 Nov 2025 10:20:00 +0100 Subject: [PATCH 3/3] change licensing --- DESCRIPTION | 2 +- LICENSE | 2 - LICENSE.md | 216 +++++++++++++++++++++++++++++++++++++++++++++++----- Makefile | 7 ++ 4 files changed, 203 insertions(+), 24 deletions(-) delete mode 100644 LICENSE diff --git a/DESCRIPTION b/DESCRIPTION index 6e98c6a..9c84341 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -29,7 +29,7 @@ Description: Monte Carlo simulation framework for different randomized clinical (Robbins-Monro (1951) ) to improve the estimate, and finally, a grid search to refine the estimate in the neighborhood of the current best solution. -License: MIT + file LICENSE +License: Apache License (>= 2) Depends: R (>= 4.1), lava (>= 1.8.0) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ac909be..0000000 --- a/LICENSE +++ /dev/null @@ -1,2 +0,0 @@ -YEAR: 2025 -COPYRIGHT HOLDER: Novo Nordisk A/S \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 138cd81..0b3612e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,195 @@ -# MIT License - -Copyright (c) 2025 carts authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +Apache License +============== + +_Version 2.0, January 2004_ +_<>_ + +### Terms and Conditions for use, reproduction, and distribution + +#### 1. Definitions + +“License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means **(i)** the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the +outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +“Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of +this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You +changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +_END OF TERMS AND CONDITIONS_ + +### APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets `[]` replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same “printed page” as the copyright notice for easier identification within +third-party archives. + + Copyright 2025 Novo Nordisk A/S, Danish company registration no. 24256790 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + \ No newline at end of file diff --git a/Makefile b/Makefile index 8369e4b..8af6ef3 100644 --- a/Makefile +++ b/Makefile @@ -81,3 +81,10 @@ upgrade: install-deps: @echo 'devtools::install_deps(".", dependencies = TRUE)' | $(R) + +cran-build: + @$(make_build_dir) + @echo 'devtools::build(".", path="$(BUILD_DIR)")' | $(R) --slave + +cran-prep: + @cd $(BUILD_DIR); _R_CHECK_FORCE_SUGGESTS_=0 $(R) CMD check $(PKG)_$(GETVER)".tar.gz" --as-cran