Simulation of RCT with flexible covariates distributions simulation.
Public fields
infoOptional information/name of the model
covariatescovariate generator (function of sample-size n and optional parameters)
outcome_modelGenerator for outcome given covariates (function of covariates x in long format)
exclusionfunction defining exclusion criterion
estimates(trial.estimates) Parameter estimates of Monte-Carlo simulations returned by Trial$run(). The field is flushed, i.e. set to its default value NULL, when model arguments (Trial$args_model()) or estimators (Trial$estimators()) are modified.
Methods
Method new()
Initialize new Trial object
Arguments
outcomeoutcome model given covariates (the first positional argument must be the covariate data)
covariatescovariate simulation function (must have 'n' as first named argument and returns either a list of data.table (data.frame) for each observation period or a single data.table (data.frame) in case of a single observation period)
exclusionfunction describing selection criterion (the first positional argument must be the combined covariate and outcome data and the function must return the subjects who are included in the trial)
estimatorsoptional list of estimators or single estimator function
summary.argslist of arguments that override default values in Trial$summary() when power and sample sizes are estimated with Trial$estimate_power() and Trial$estimate_samplesize()
infooptional string describing the model
Method args_model()
Get, specify or update parameters of covariate, outcome and exclusion model. Parameters are set in a named list, and updated when parameter names match with existing values in the list.
Arguments
.args(list or character) named list of arguments to update or set. A single or subset of arguments can be retrieved by passing the respective argument names as a character or character vector.
.reset(logical or character) Reset all or a subset of previously set parameters. Can be combined with setting new parameters.
...Alternative to using
.argsto update or set arguments
Examples
trial <- Trial$new(
covariates = function(n, p = 0.5) data.frame(a = rbinom(n, 1, p)),
outcome = function(data, ate, mu) rnorm(nrow(data), mu + data$a * ate)
)
# set and update parameters
trial$args_model(.args = list(ate = 2, p = 0.5, mu = 3))
trial$args_model(ate = 5, p = 0.6) # update parameters
trial$args_model(list(ate = 2), p = 0.5) # combine first arg with ...
# retrieve parameters
trial$args_model() # return all set parameters
trial$args_model("ate") # select a single parameter
trial$args_model(c("ate", "mu")) # multiple parameters
# remove parameters
trial$args_model(.reset = "ate") # remove a single parameter
trial$args_model(.reset = TRUE) # remove all parameters
# remove and set/update parameters
trial$args_model(ate = 2, p = 0.5, .reset = TRUE)
trial$args_model(ate = 5, .reset = "p") # removing p and updating ateMethod args_summary()
Get, specify or update the summary.args attribute.
Arguments
.args(list or character) named list of arguments to update or set. A single or subset of arguments can be retrieved by passing the respective argument names as a character or character vector.
.reset(logical or character) Reset all or a subset of previously set parameters. Can be combined with setting new parameters.
...Alternative to using
.argsto update or set arguments
Examples
trial <- Trial$new(
covariates = function(n, p = 0.5) data.frame(a = rbinom(n, 1, p)),
outcome = function(data, ate, mu) rnorm(nrow(data), mu + data$a * ate)
)
# set and update parameters
trial$args_summary(list(level = 0.05, alternative = "<"))
trial$args_summary(level = 0.25) # update parameters
# retrieve parameters
trial$args_summary() # return all set parameters
trial$args_summary("level") # select a single parameter
trial$args_summary(c("level", "alternative")) # multiple parameters
# remove parameters
trial$args_summary(.reset = "level") # remove a single parameter
trial$args_summary(.reset = TRUE) # remove all parameters
# remove and set/update parameters
trial$args_summary(alternative = "!=", level = 0.05, .reset = TRUE)
# removing alternative and setting level
trial$args_summary(level = 0.05, .reset = "alternative")Method estimators()
Get, specify or update estimators.
Arguments
.estimators(list, function or character) Argument controlling the getter or setter behavior. Estimators are set or updated by providing a single estimator (function) or list of estimators, and retrieved by providing a character (see examples).
.reset(logical or character) Reset all or a subset of previously set estimators. Can be combined with setting new estimators.
...Alternative to
.estimatorsfor updating or setting estimators.
Details
A name is internally assigned to estimators when calling the
method with .estimators set to a single estimator or a list with
unnamed elements. The names are a combination of an est prefix and an
integer that indicates the nth added unnamed estimator.
Examples
estimators <- list(marginal = est_glm(), adj = est_glm(covariates = "x"))
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)),
outcome = \(data, ate = -0.5) rnorm(nrow(data), data$a * ate),
estimators = estimators
)
# get estimators
trial$estimators() |> names() # list all estimators
trial$estimators("marginal") |> names() # select a single estimator
trial$estimators(c("marginal", "adj")) |> names() # select mult. est.
# remove estimators
trial$estimators(.reset = "marginal") # remove a single estimator
trial$estimators(.reset = TRUE) # remove all estimators
# set or update estimators
trial$estimators(estimators)
trial$estimators(adj2 = est_adj(covariates = "x")) # add new estimator
# update adj and remove adj2
trial$estimators(adj = est_glm(covariates = "x"), .reset = "adj2")
# unnamed estimators (adding default name)
estimators <- list(est_glm(), est_glm(covariates = "x"))
trial$estimators(estimators, .reset = TRUE)
trial$estimators(.reset = "est1")
trial$estimators(est_glm()) # replaces removed est1Method simulate()
Simulate data by applying parameters to the trial model. The
method samples first from the covariate model. Outcome data sampling
follows by passing the simulated covariate data to the outcome model. An
optional exclusion model is applied to the combined covariates and
outcomes data. The sampling process is repeated in case any subjects are
removed by the exclusion model until a total of n subjects are sampled
or the maximum iteration number .niter is reached.
The method adds two auxiliary columns to the simulated data to identify distinct patients (id) and periods (num) in case of time-dependent covariate and outcome models. The columns are added to the sampled covariate data before sampling the outcomes. A data.table with both columns is provided to the outcome model in case no covariate model is defined. Thus, the outcome model is always applied to at least a data.table with an id and num column. The default column name y is used for the outcome variable in the returned data.table when the defined outcome model returns a vector. The name is easily changed by returning a data.table with a named column (see examples).
The optional argument ... of this method can be used to provide
parameters to the trial model as an addition to parameters that have
already been defined via Trial$args_model(). Data is simulated
from the union of parameters, where parameters provided via the optional
argument of this method take precedence over parameters defined via
Trial$args_model(). However, parameters that have been set via
Trial$args_model() are not updated when optional arguments are
provided.
Arguments
n(integer) Number of observations (sample size)
.niter(integer) Maximum number of simulation runs to avoid infinite loops for ill defined exclusion functions.
...Arguments to covariate, outcome and exclusion model functions
Examples
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate))
)
# applying and modifying parameters
n <- 10
trial$simulate(n) # use parameters set during initialization
trial$args_model(ate = -100) # update parameters
trial$simulate(n)
trial$simulate(n, ate = 100) # change ate via optional argument
# rename outcome variable
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) {
data.frame(yy = with(data, rnorm(nrow(data), a * ate)))
}
)
trial$simulate(n)
# return multiple outcome variables
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), y.base = rnorm(n)),
outcome = \(data, ate = 0) {
y <- with(data, rnorm(nrow(data), a * ate))
return(data.frame(y = y, y.chg = data$y.base - y))
}
)
trial$simulate(n)
# use exclusion argument to post-process sampled outcome variable to
# achieve the same as in the above example but without modifying the
# originally defined outcome model
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), y.base = rnorm(n)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate)),
exclusion = \(data) {
cbind(data, data.frame(y.chg = data$y.base - data$y))
}
)
trial$simulate(n)
# no covariate model
trial <- Trial$new(
outcome = \(data, ate = 0) {
n <- nrow(data)
a <- rbinom(n, 1, 0.5)
return(data.frame(a = a, y = rnorm(n, a * ate)))
}
)
trial$simulate(n)Method run()
Run trial and estimate parameters multiple times
The method calls Trial$simulate() R times and applies the
specified estimators to each simulated dataset of sample size n.
Parameters to the covariates, outcome and exclusion models can be
provided as optional arguments to this method call in addition to
parameters that have already been defined via
Trial$args_model(). The behavior is identical to
Trial$simulate(), except that .niter can be provided as an
optional argument to this method for controlling the maximum number of
simulation runs in Trial$simulate().
Estimators fail silently in that errors occurring when applying an estimator to each simulated dataset will not terminate the method call. The returned trial.estimates object will instead indicate that estimators failed.
Arguments
n(integer) Number of observations (sample size)
R(integer) Number of replications
estimators(list or function) List of estimators or a single unnamed estimator
...Arguments to covariate, outcome and exclusion model functions
Returns
(invisible) An object of class trial.estimates, which
contains the estimates of all estimators and all information to repeat
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
# future::plan("multicore")
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate)),
estimators = list(glm = est_glm())
)
trial$args_summary(alternative = "<")
# the returned trial.estimates object contains the estimates for each of
# the R simulated data sets + all necessary information to re-run the
# simulation
res <- trial$run(n = 100, R = 50) # store return object in a new variable
print(trial$estimates) # trial$estimates == res
# the basic usage is to apply the summary method to the generated
# trial.estimates object.
trial$summary()
# combining Trial$run and summary is faster than using
# Trial$estimate_power when modifying only the parameters of the
# decision-making function
sapply(
c(0, 0.25, 0.5),
\(ni) trial$summary(ni.margin = ni)[, "power"]
)
# changing the ate parameter value
trial$run(n = 100, R = 50, ate = -0.2)
# supplying another estimator
trial$run(n = 100, R = 50, estimators = est_glm(robust = FALSE))
}
Method estimate_power()
Estimates statistical power for a specified trial
Convenience method that first runs Trial$run() and subsequently applies Trial$summary() to derive the power for each estimator. The behavior of passing arguments to lower level functions is identical to Trial$run().
Usage
Trial$estimate_power(n, R = 100, estimators = NULL, summary.args = list(), ...)Arguments
n(integer) Number of observations (sample size)
R(integer) Number of replications
estimators(list or function) List of estimators or a single unnamed estimator
summary.args(list) Arguments passed to summary method for decision-making
...Arguments to covariate, outcome and exclusion model functions
Examples
\dontrun{ # don't run because of high computational time
# toy examples with small number of Monte-Carlo replicates
# future::plan("multicore")
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate)),
estimators = list(glm = est_glm())
)
trial$args_summary(alternative = "<")
# using previously defined estimators and summary.args
trial$estimate_power(n = 100, R = 20)
# supplying parameters to outcome function
trial$estimate_power(n = 100, R = 20, ate = -100)
# modifying arguments of summary function
trial$estimate_power(n = 100, R = 20, ate = -100,
summary.args = list(alternative = ">")
)
# supplying estimators to overrule previously set estimators
trial$estimate_power(n = 100, R = 20,
estimators = list(est_glm(), est_adj()))
}
Method estimate_samplesize()
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 solution.
Note that the estimation procedure for the sample size will not populate the estimates attribute of a trial object.
Usage
Trial$estimate_samplesize(
...,
power = 0.9,
estimator = NULL,
interval = c(50L, 10000L),
bisection.control = list(R = 100, niter = 6),
sa.control = list(R = 1, niter = 250, stepmult = 100, alpha = 0.5),
refinement = seq(-10, 10, by = 5),
R = 1000,
interpolate = TRUE,
verbose = TRUE,
minimum = 10L,
summary.args = list()
)Arguments
...Arguments to covariate, outcome and exclusion model functions
power(numeric) Desired statistical power
estimator(list or function) Estimator (function) to be applied. If NULL, then estimate sample size for all estimators defined via Trial$estimators(). A prefix est is used to label unnamed estimators.
interval(integer vector) Interval in which to initially look for a solution with the bisection algorithm. Passing an integer will skip the bisection algorithm and use the provided integer as the initial solution for the stochastic approximation algorithm
bisection.control(list) Options controlling the bisection algorithm (bisection). Default values can also be changed for a subset of options only (see examples).
sa.control(list) Options controlling the stochastic approximation (Robbins-Monro) algorithm (optim_sa). Default values can also be changed for a subset of options only (see examples).
refinement(integer vector) Vector to create an interval whose center is the sample size estimate of the Robbins-Monro algorithm.
R(integer) Number of replications to use in Monte Carlo simulation of refinement calculations.
interpolate(logical) If TRUE, a linear interpolation of the refinement points will be used to estimate the power.
verbose(logical) If TRUE, additional output will be displayed.
minimum(integer) Minimum sample size.
summary.args(list) Arguments passed to summary method for decision-making
Examples
\dontrun{ # don't run because of high computational time
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)),
estimators = list(marginal = est_glm())
)
trial$args_model(ate = -1, sd = 5)
trial$args_summary(alternative = "<")
# supply model parameter and estimator to call to overwrite previously
# set values
trial$estimate_samplesize(ate = -2, estimator = est_glm())
# reduce number of iterations for bisection step but keep R = 100
# (default value)
# trial$estimate_samplesize(bisection.control = list(niter = 2))
# reduce significance level from 0.05 to 0.025, but keep alternative as
# before
# trial$estimate_samplesize(summary.args = list(level = 0.025))
}
Method summary()
Summarize Monte Carlo studies of different estimators for the treatment effect in a randomized clinical trial. The method reports the power of both superiority tests (one-sided or two-sided) and non-inferiority tests, together with summary statistics of the different estimators.
Usage
Trial$summary(
level = 0.05,
null = 0,
ni.margin = NULL,
alternative = "!=",
reject.function = NULL,
true.value = NULL,
nominal.coverage = 0.9,
estimates = NULL,
...
)Arguments
level(numeric) significance level
null(numeric) null hypothesis to test
ni.margin(numeric) non-inferiority margin
alternativealternative hypothesis (not equal !=, less <, greater >)
reject.functionOptional function calculating whether to reject the null hypothesis
true.valueOptional true parameter value
nominal.coverageWidth of confidence limits
estimatesOptional trial.estimates object. When provided, these estimates will be used instead of the object's stored estimates. This allows calculating summaries for different trial results without modifying the object's state.
...additional arguments to lower level functions
Examples
outcome <- function(data, p = c(0.5, 0.25)) {
a <- rbinom(nrow(data), 1, 0.5)
data.frame(a = a, y = rbinom(nrow(data), 1, p[1] * (1 - a) + p[2] * a)
)
}
trial <- Trial$new(outcome, estimators = est_glm())
trial$run(n = 100, R = 100)
# two-sided test with 0.05 significance level (alpha = 0.05) (default
# values)
trial$summary(level = 0.05, alternative = "!=")
# on-sided test
trial$summary(level = 0.025, alternative = "<")
# non-inferiority test
trial$summary(level = 0.025, ni.margin = -0.5)
# provide simulation results to summary method via estimates argument
res <- trial$run(n = 100, R = 100, p = c(0.5, 0.5))
trial$summary(estimates = res)
# calculate empirical bias, rmse and coverage for true target parameter
trial$summary(estimates = res, true.value = 0)Method print()
Print method for Trial objects
Arguments
...Additional arguments to lower level functions (not used).
verbose(logical) By default, only print the function arguments of the covariates, outcome and exclusion models. If TRUE, then also print the function body.
Examples
trial <- Trial$new(
covariates = function(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = function(data, sd = 1) rnorm(nrow(data), data$a * -1, sd),
estimators = list(marginal = est_glm()),
info = "Some trial info"
)
trial$args_model(sd = 2)
trial$args_summary(level = 0.025)
print(trial) # only function headers
print(trial, verbose = TRUE) # also print function bodiesExamples
if (FALSE) # don't run because of high computational time
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)
)
trial$estimators(
unadjusted = est_glm(family = "poisson"),
adjusted = est_glm(family = "poisson", covariates = "x")
)
#> Error: object 'trial' not found
trial$run(n = 200, R = 100)
#> Error: object 'trial' not found
trial$summary()
#> Error: object 'trial' not found
# \dontrun{}
## ------------------------------------------------
## Method `Trial$args_model`
## ------------------------------------------------
trial <- Trial$new(
covariates = function(n, p = 0.5) data.frame(a = rbinom(n, 1, p)),
outcome = function(data, ate, mu) rnorm(nrow(data), mu + data$a * ate)
)
# set and update parameters
trial$args_model(.args = list(ate = 2, p = 0.5, mu = 3))
trial$args_model(ate = 5, p = 0.6) # update parameters
trial$args_model(list(ate = 2), p = 0.5) # combine first arg with ...
# retrieve parameters
trial$args_model() # return all set parameters
#> $ate
#> [1] 2
#>
#> $p
#> [1] 0.5
#>
#> $mu
#> [1] 3
#>
trial$args_model("ate") # select a single parameter
#> [1] 2
trial$args_model(c("ate", "mu")) # multiple parameters
#> $ate
#> [1] 2
#>
#> $mu
#> [1] 3
#>
# remove parameters
trial$args_model(.reset = "ate") # remove a single parameter
trial$args_model(.reset = TRUE) # remove all parameters
# remove and set/update parameters
trial$args_model(ate = 2, p = 0.5, .reset = TRUE)
trial$args_model(ate = 5, .reset = "p") # removing p and updating ate
## ------------------------------------------------
## Method `Trial$args_summary`
## ------------------------------------------------
trial <- Trial$new(
covariates = function(n, p = 0.5) data.frame(a = rbinom(n, 1, p)),
outcome = function(data, ate, mu) rnorm(nrow(data), mu + data$a * ate)
)
# set and update parameters
trial$args_summary(list(level = 0.05, alternative = "<"))
trial$args_summary(level = 0.25) # update parameters
# retrieve parameters
trial$args_summary() # return all set parameters
#> $level
#> [1] 0.25
#>
#> $null
#> [1] 0
#>
#> $ni.margin
#> NULL
#>
#> $alternative
#> [1] "<"
#>
#> $reject.function
#> NULL
#>
#> $true.value
#> NULL
#>
#> $nominal.coverage
#> [1] 0.9
#>
trial$args_summary("level") # select a single parameter
#> [1] 0.25
trial$args_summary(c("level", "alternative")) # multiple parameters
#> $level
#> [1] 0.25
#>
#> $alternative
#> [1] "<"
#>
# remove parameters
trial$args_summary(.reset = "level") # remove a single parameter
trial$args_summary(.reset = TRUE) # remove all parameters
# remove and set/update parameters
trial$args_summary(alternative = "!=", level = 0.05, .reset = TRUE)
# removing alternative and setting level
trial$args_summary(level = 0.05, .reset = "alternative")
## ------------------------------------------------
## Method `Trial$estimators`
## ------------------------------------------------
estimators <- list(marginal = est_glm(), adj = est_glm(covariates = "x"))
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), x = rnorm(n)),
outcome = \(data, ate = -0.5) rnorm(nrow(data), data$a * ate),
estimators = estimators
)
# get estimators
trial$estimators() |> names() # list all estimators
#> [1] "marginal" "adj"
trial$estimators("marginal") |> names() # select a single estimator
#> NULL
trial$estimators(c("marginal", "adj")) |> names() # select mult. est.
#> [1] "marginal" "adj"
# remove estimators
trial$estimators(.reset = "marginal") # remove a single estimator
trial$estimators(.reset = TRUE) # remove all estimators
# set or update estimators
trial$estimators(estimators)
trial$estimators(adj2 = est_adj(covariates = "x")) # add new estimator
# update adj and remove adj2
trial$estimators(adj = est_glm(covariates = "x"), .reset = "adj2")
# unnamed estimators (adding default name)
estimators <- list(est_glm(), est_glm(covariates = "x"))
trial$estimators(estimators, .reset = TRUE)
trial$estimators(.reset = "est1")
trial$estimators(est_glm()) # replaces removed est1
## ------------------------------------------------
## Method `Trial$simulate`
## ------------------------------------------------
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate))
)
# applying and modifying parameters
n <- 10
trial$simulate(n) # use parameters set during initialization
#> id a num y
#> <num> <int> <num> <num>
#> 1: 1 1 0 -1.8218177
#> 2: 2 0 0 -0.2473253
#> 3: 3 0 0 -0.2441996
#> 4: 4 0 0 -0.2827054
#> 5: 5 0 0 -0.5536994
#> 6: 6 0 0 0.6289820
#> 7: 7 1 0 2.0650249
#> 8: 8 1 0 -1.6309894
#> 9: 9 1 0 0.5124269
#> 10: 10 0 0 -1.8630115
trial$args_model(ate = -100) # update parameters
trial$simulate(n)
#> id a num y
#> <num> <int> <num> <num>
#> 1: 1 0 0 0.36295126
#> 2: 2 1 0 -101.30454355
#> 3: 3 0 0 0.73777632
#> 4: 4 0 0 1.88850493
#> 5: 5 1 0 -100.09744510
#> 6: 6 1 0 -100.93584735
#> 7: 7 0 0 -0.01595031
#> 8: 8 0 0 -0.82678895
#> 9: 9 1 0 -101.51239965
#> 10: 10 0 0 0.93536319
trial$simulate(n, ate = 100) # change ate via optional argument
#> id a num y
#> <num> <int> <num> <num>
#> 1: 1 1 0 98.0899125
#> 2: 2 0 0 -0.2792372
#> 3: 3 1 0 99.6865540
#> 4: 4 0 0 1.0673079
#> 5: 5 1 0 100.0700349
#> 6: 6 1 0 99.3608767
#> 7: 7 1 0 99.9500351
#> 8: 8 0 0 -0.2514834
#> 9: 9 0 0 0.4447971
#> 10: 10 0 0 2.7554176
# rename outcome variable
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) {
data.frame(yy = with(data, rnorm(nrow(data), a * ate)))
}
)
trial$simulate(n)
#> id a num yy
#> <num> <int> <num> <num>
#> 1: 1 1 0 -0.24323674
#> 2: 2 1 0 -0.20608719
#> 3: 3 1 0 0.01917759
#> 4: 4 0 0 0.02956075
#> 5: 5 1 0 0.54982754
#> 6: 6 1 0 -2.27411486
#> 7: 7 0 0 2.68255718
#> 8: 8 0 0 -0.36122126
#> 9: 9 1 0 0.21335575
#> 10: 10 1 0 1.07434588
# return multiple outcome variables
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), y.base = rnorm(n)),
outcome = \(data, ate = 0) {
y <- with(data, rnorm(nrow(data), a * ate))
return(data.frame(y = y, y.chg = data$y.base - y))
}
)
trial$simulate(n)
#> id a y.base num y y.chg
#> <num> <int> <num> <num> <num> <num>
#> 1: 1 0 1.0650573 0 0.60674805 0.4583093
#> 2: 2 1 0.1316706 0 -0.10993567 0.2416063
#> 3: 3 1 0.4886288 0 0.17218172 0.3164471
#> 4: 4 0 -1.6994506 0 -0.09032729 -1.6091233
#> 5: 5 0 -1.4707363 0 1.92434334 -3.3950796
#> 6: 6 1 0.2841503 0 1.29839276 -1.0142424
#> 7: 7 0 1.3373204 0 0.74879127 0.5885291
#> 8: 8 0 0.2366963 0 0.55622433 -0.3195280
#> 9: 9 0 1.3182934 0 -0.54825726 1.8665506
#> 10: 10 1 0.5239098 0 1.11053489 -0.5866251
# use exclusion argument to post-process sampled outcome variable to
# achieve the same as in the above example but without modifying the
# originally defined outcome model
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5), y.base = rnorm(n)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate)),
exclusion = \(data) {
cbind(data, data.frame(y.chg = data$y.base - data$y))
}
)
trial$simulate(n)
#> id a y.base num y y.chg
#> <num> <int> <num> <num> <num> <num>
#> 1: 1 0 1.06310200 0 -0.7854327 1.8485347
#> 2: 2 1 1.04871262 0 -1.0567369 2.1054495
#> 3: 3 0 -0.03810289 0 -0.7955414 0.7574385
#> 4: 4 1 0.48614892 0 -1.7562754 2.2424243
#> 5: 5 1 1.67288261 0 -0.6905379 2.3634205
#> 6: 6 0 -0.35436116 0 -0.5585420 0.2041808
#> 7: 7 0 0.94634789 0 -0.5366633 1.4830112
#> 8: 8 1 1.31682636 0 0.2271271 1.0896992
#> 9: 9 1 -0.29664002 0 0.9784549 -1.2750949
#> 10: 10 0 -0.38721358 0 -0.2088827 -0.1783309
# no covariate model
trial <- Trial$new(
outcome = \(data, ate = 0) {
n <- nrow(data)
a <- rbinom(n, 1, 0.5)
return(data.frame(a = a, y = rnorm(n, a * ate)))
}
)
trial$simulate(n)
#> id a y
#> <num> <int> <num>
#> 1: 1 0 0.42485844
#> 2: 2 1 -1.68428153
#> 3: 3 1 0.24940178
#> 4: 4 1 1.07283825
#> 5: 5 0 2.03936926
#> 6: 6 1 0.44945378
#> 7: 7 1 1.39181405
#> 8: 8 1 0.42656655
#> 9: 9 1 0.10758399
#> 10: 10 0 0.02229473
## ------------------------------------------------
## Method `Trial$run`
## ------------------------------------------------
if (FALSE) # don't run because of high computational time
# future::plan("multicore")
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate)),
estimators = list(glm = est_glm())
)
trial$args_summary(alternative = "<")
# the returned trial.estimates object contains the estimates for each of
# the R simulated data sets + all necessary information to re-run the
# simulation
res <- trial$run(n = 100, R = 50) # store return object in a new variable
#> Error in names(estimators) <- paste0("est", seq_along(estimators)): 'names' attribute [1] must be the same length as the vector [0]
print(trial$estimates) # trial$estimates == res
#> NULL
# the basic usage is to apply the summary method to the generated
# trial.estimates object.
trial$summary()
#> Error in trial_summary(self = self, level = level, null = null, ni.margin = ni.margin, alternative = alternative, reject.function = reject.function, true.value = true.value, nominal.coverage = nominal.coverage, estimates = estimates, ...): No estimates available. Run trial first.
# combining Trial$run and summary is faster than using
# Trial$estimate_power when modifying only the parameters of the
# decision-making function
sapply(
c(0, 0.25, 0.5),
\(ni) trial$summary(ni.margin = ni)[, "power"]
)
#> Error in trial_summary(self = self, level = level, null = null, ni.margin = ni.margin, alternative = alternative, reject.function = reject.function, true.value = true.value, nominal.coverage = nominal.coverage, estimates = estimates, ...): No estimates available. Run trial first.
# changing the ate parameter value
trial$run(n = 100, R = 50, ate = -0.2)
#> Error in names(estimators) <- paste0("est", seq_along(estimators)): 'names' attribute [1] must be the same length as the vector [0]
# supplying another estimator
trial$run(n = 100, R = 50, estimators = est_glm(robust = FALSE))
# \dontrun{}
## ------------------------------------------------
## Method `Trial$estimate_power`
## ------------------------------------------------
if (FALSE) # don't run because of high computational time
# toy examples with small number of Monte-Carlo replicates
# future::plan("multicore")
trial <- Trial$new(
covariates = \(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = \(data, ate = 0) with(data, rnorm(nrow(data), a * ate)),
estimators = list(glm = est_glm())
)
trial$args_summary(alternative = "<")
# using previously defined estimators and summary.args
trial$estimate_power(n = 100, R = 20)
#> Error in names(estimators) <- paste0("est", seq_along(estimators)): 'names' attribute [1] must be the same length as the vector [0]
# supplying parameters to outcome function
trial$estimate_power(n = 100, R = 20, ate = -100)
#> Error in names(estimators) <- paste0("est", seq_along(estimators)): 'names' attribute [1] must be the same length as the vector [0]
# modifying arguments of summary function
trial$estimate_power(n = 100, R = 20, ate = -100,
summary.args = list(alternative = ">")
)
#> Error in names(estimators) <- paste0("est", seq_along(estimators)): 'names' attribute [1] must be the same length as the vector [0]
# supplying estimators to overrule previously set estimators
trial$estimate_power(n = 100, R = 20,
estimators = list(est_glm(), est_adj()))
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> Warning: partial argument match of 'X.cat' to 'X.cate'
#> est1 est2
#> 0.05 0.05
# \dontrun{}
## ------------------------------------------------
## Method `Trial$estimate_samplesize`
## ------------------------------------------------
if (FALSE) # don't run because of high computational time
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)),
estimators = list(marginal = est_glm())
)
trial$args_model(ate = -1, sd = 5)
trial$args_summary(alternative = "<")
# supply model parameter and estimator to call to overwrite previously
# set values
trial$estimate_samplesize(ate = -2, estimator = est_glm())
#> INFO [2025-11-05 15:03:42] Finding initial sample-size with bisection algorithm
#> INFO [2025-11-05 15:03:42] Evaluating left point: 50
#> INFO [2025-11-05 15:03:44] Evaluating right point: 10000
#> INFO [2025-11-05 15:03:48] Running stochastic approximation algorithm
#> INFO [2025-11-05 15:04:33] Refining estimate and calculating power
#> INFO [2025-11-05 15:04:45] [1/4] - power(10) = 0.941
#> INFO [2025-11-05 15:04:56] [2/4] - power(11) = 0.935
#> INFO [2025-11-05 15:05:08] [3/4] - power(16) = 0.994
#> INFO [2025-11-05 15:05:19] [4/4] - power(21) = 0.998
#> INFO [2025-11-05 15:05:19] Estimated sample size: 10
#> ── Estimated sample-size to reach 90% power ──
#>
#> n = 10 (actual estimated power≈93.94%)
# reduce number of iterations for bisection step but keep R = 100
# (default value)
# trial$estimate_samplesize(bisection.control = list(niter = 2))
# reduce significance level from 0.05 to 0.025, but keep alternative as
# before
# trial$estimate_samplesize(summary.args = list(level = 0.025))
# \dontrun{}
## ------------------------------------------------
## Method `Trial$summary`
## ------------------------------------------------
outcome <- function(data, p = c(0.5, 0.25)) {
a <- rbinom(nrow(data), 1, 0.5)
data.frame(a = a, y = rbinom(nrow(data), 1, p[1] * (1 - a) + p[2] * a)
)
}
trial <- Trial$new(outcome, estimators = est_glm())
trial$run(n = 100, R = 100)
# two-sided test with 0.05 significance level (alpha = 0.05) (default
# values)
trial$summary(level = 0.05, alternative = "!=")
#> estimate std.err std.dev power na
#> est1 -0.2586042 0.09297792 0.08489204 0.83 0
# on-sided test
trial$summary(level = 0.025, alternative = "<")
#> estimate std.err std.dev power na
#> est1 -0.2586042 0.09297792 0.08489204 0.83 0
# non-inferiority test
trial$summary(level = 0.025, ni.margin = -0.5)
#> estimate std.err std.dev power na
#> est1 -0.2586042 0.09297792 0.08489204 0.7 0
# provide simulation results to summary method via estimates argument
res <- trial$run(n = 100, R = 100, p = c(0.5, 0.5))
trial$summary(estimates = res)
#> estimate std.err std.dev power na
#> est1 0.01201245 0.09932767 0.1047944 0.07 0
# calculate empirical bias, rmse and coverage for true target parameter
trial$summary(estimates = res, true.value = 0)
#> estimate std.err std.dev power na bias rmse coverage
#> est1 0.01201245 0.09932767 0.1047944 0.07 0 0.01201245 0.1049588 0.85
## ------------------------------------------------
## Method `Trial$print`
## ------------------------------------------------
trial <- Trial$new(
covariates = function(n) data.frame(a = rbinom(n, 1, 0.5)),
outcome = function(data, sd = 1) rnorm(nrow(data), data$a * -1, sd),
estimators = list(marginal = est_glm()),
info = "Some trial info"
)
trial$args_model(sd = 2)
trial$args_summary(level = 0.025)
print(trial) # only function headers
#> ── Trial Object ──
#> Some trial info
#>
#> Model arguments:
#> • sd: num 2
#>
#> Summary arguments:
#> • level: num 0.025
#> • null: num 0
#> • ni.margin: NULL
#> • alternative: chr "!="
#> • reject.function: NULL
#> • true.value: NULL
#> • nominal.coverage: num 0.9
#>
#> Covariates:
#> function (n, ...)
#>
#> Outcome:
#> function (data, sd = 1, ...)
#>
#> Exclusion:
#> function (x, ...)
#>
#> Estimators:
#> 1. marginal
#> ────────────────────
print(trial, verbose = TRUE) # also print function bodies
#> ── Trial Object ──
#> Some trial info
#>
#> Model arguments:
#> • sd: num 2
#>
#> Summary arguments:
#> • level: num 0.025
#> • null: num 0
#> • ni.margin: NULL
#> • alternative: chr "!="
#> • reject.function: NULL
#> • true.value: NULL
#> • nominal.coverage: num 0.9
#>
#> Covariates:
#> function (n, ...)
#> data.frame(a = rbinom(n, 1, 0.5))
#>
#> Outcome:
#> function (data, sd = 1, ...)
#> rnorm(nrow(data), data$a * -1, sd)
#>
#> Exclusion:
#> function (x, ...)
#> x
#>
#> Estimators:
#> 1. marginal
#> ────────────────────
