Skip to contents

Create a class of trial.

Methods


Method new()

initialize a trial

Usage

Trials$new(
  name,
  n_patients,
  duration,
  description = name,
  seed = NULL,
  enroller,
  dropout = NULL,
  silent = FALSE,
  ...
)

Arguments

name

character. Name of trial.

n_patients

integer. Maximum number of patients could be enrolled to the trial.

duration

Numeric. Trial duration.

description

character. Optional for description of the trial. By default it is set to be trial's name.

seed

random seed. If NULL, set.seed() will not be called, which uses seed set outside.

enroller

a function returning a vector enrollment time for patients. Its first argument is the number of enrolled patients.

dropout

a function returning a vector of dropout time for patients. Its first argument is the number of enrolled patients.

silent

logical. TRUE to mute messages.

...

arguments of enroller and dropout.


Method get_trial_data()

return trial data of enrolled patients at the time of this function is called

Usage

Trials$get_trial_data()


Method get_duration()

return maximum duration of a trial

Usage

Trials$get_duration()


Method set_duration()

set trial duration in an adaptive designed trial. All patients enrolled before resetting the duration are truncated (non-tte endpoints) or censored (tte endpoints) at the original duration. Remaining patients are re-randomized. Now new duration must be longer than the old one.

Usage

Trials$set_duration(duration)

Arguments

duration

new duration of a trial. It must be longer than the current duration.


Method set_enroller()

set recruitment curve when initialize a trial.

Usage

Trials$set_enroller(func, ...)

Arguments

func

function to generate enrollment time. It can be built-in function like `rexp` or customized functions like `StaggeredRecruiter`.

...

arguments for func.


Method get_enroller()

get function of recruitment curve

Usage

Trials$get_enroller()


Method set_dropout()

set distribution of drop out time. This can be done when initialize a trial, or when updating a trial in adaptive design.

Usage

Trials$set_dropout(func, ...)

Arguments

func

function to generate dropout time. It can be built-in function like `rexp` or customized functions.

...

arguments for func.


Method get_dropout()

get generator of dropout time

Usage

Trials$get_dropout()


Method roll_back()

roll back data to current time of trial. By doing so, Trial$trial_data will be cut at current time, and data after then are deleted. However, Trial$enroll_time after current time are kept unchanged because that is planned enrollment curve.

Usage

Trials$roll_back()


Method remove_arms()

remove arms from a trial. enroll_patients() will be always called at the end to enroll all remaining patients after Trial$get_current_time(). This function may be used with futility analysis, dose selection, enrichment analysis (sub-population) or interim analysis (early stop for efficacy)

Usage

Trials$remove_arms(arms_name)

Arguments

arms_name

character vector. Name of arms to be removed.


Method update_sample_ratio()

update sample ratio of an arm. This could happen after an arm is added or removed. We may want to update sample ratio of unaffected arms as well. This function can only update sample ratio for one arm at a time. Once sample ratio is updated, trial data should be rolled back with updated randomization queue. Data of unenrolled patients should be re-sampled as well.

Usage

Trials$update_sample_ratio(arm_name, sample_ratio)

Arguments

arm_name

character. Name of an arm of length 1.

sample_ratio

integer. Sample ratio of the arm.


Method add_arms()

add one or more arms to the trial. enroll_patients() will be called at the end to enroll all remaining patients in private$randomization_queue. This function can be used in two scenarios. (1) add arms right after a trial is created (i.e., Trial$new(...)). sample_ratio and arms added through ... should be of same length. (2) add arms to a trial already with arm(s)

Usage

Trials$add_arms(sample_ratio, ...)

Arguments

sample_ratio

integer vector. Sample ratio for permuted block randomization. It will be appended to existing sample ratio in the trial.

...

one or more objects of class Arm. One exception in ... is an argument enforce. When enforce = TRUE, sample ratio of newly added arm. It rolls back all patients after Trial$get_current_time(), i.e. redo randomization for those patients. This can be useful to add arms one by one when creating a trial. Note that we can run Trial$add_arm(sample_ratio1, arm1) followed by Trial$add_arm(sample_ratio2, enforce = TRUE, arm2). We would expected similar result with Trial$add_arms(c(sample_ratio1, sample_ratio2), arm1, arm2). Note that these two method won't return exactly the same trial because randomization_queue were generated twice in the first approach but only once in the second approach. But statistically, they are equivalent and of the same distribution.


Method get_name()

return name of trial

Usage

Trials$get_name()


Method get_description()

return description of trial

Usage

Trials$get_description()


Method get_arms()

return a list of arms in the trial

Usage

Trials$get_arms()


Method get_arms_name()

return arms' name of trial

Usage

Trials$get_arms_name()


Method get_number_arms()

get number of arms in the trial

Usage

Trials$get_number_arms()


Method has_arm()

check if the trial has any arm. Return TRUE or FALSE.

Usage

Trials$has_arm()


Method get_an_arm()

return an arm

Usage

Trials$get_an_arm(arm_name)

Arguments

arm_name

character, name of arm to be extracted


Method get_sample_ratio()

return current sample ratio of the trial. The ratio can probably change during the trial (e.g., arm is removed or added)

Usage

Trials$get_sample_ratio(arm_names = NULL)

Arguments

arm_names

character vector of arms.


Method get_number_patients()

return number of patients when planning the trial

Usage

Trials$get_number_patients()


Method get_number_enrolled_patients()

return number of enrolled (randomized) patients

Usage

Trials$get_number_enrolled_patients()


Method get_number_unenrolled_patients()

return number of unenrolled patients

Usage

Trials$get_number_unenrolled_patients()


Method get_randomization_queue()

return randomization queue of planned but not yet enrolled patients. This function does not update randomization_queue, just return its value for debugging purpose.

Usage

Trials$get_randomization_queue(index = NULL)

Arguments

index

index to be extracted. Return all queue if NULL.


Method get_enroll_time()

return enrollment time of planned but not yet enrolled patients. This function does not update enroll_time, just return its value for debugging purpose.

Usage

Trials$get_enroll_time(index = NULL)

Arguments

index

index to extract. Return all enroll time if NULL.


Method enroll_patients()

assign new patients to pre-planned randomization queue at pre-specified enrollment time.

Usage

Trials$enroll_patients(n_patients = NULL)

Arguments

n_patients

number of new patients to be enrolled. If NULL, all remaining patients in plan are enrolled. Error may be triggered if n_patients is greater than remaining patients as planned.


Method set_current_time()

set current time of a trial. Any data collected before could not be changed. private$now should be set after a milestone is triggered (through Milestones class, futility, interim, etc), an arm is added or removed at a milestone

Usage

Trials$set_current_time(time)

Arguments

time

current calendar time of a trial.


Method get_current_time()

return current time of a trial

Usage

Trials$get_current_time()


Method get_event_tables()

count accumulative number of events (for TTE) or non-missing samples (otherwise) over calendar time (enroll time + tte for TTE, or enroll time + readout otherwise)

Usage

Trials$get_event_tables(arms = NULL)

Arguments

arms

a vector of arms' name on which the event tables are created. if NULL, all arms in the trial will be used.


Method get_data_lock_time_by_event_number()

given a set of endpoints and target number of events, determine the data lock time for a milestone (futility, interim, final, etc.). This function does not change trial object (e.g. rolling back not yet randomized patients after the found data lock time).

Usage

Trials$get_data_lock_time_by_event_number(
  endpoints,
  arms,
  target_n_events,
  type = c("all", "any")
)

Arguments

endpoints

character vector. Data lock time is determined by a set of endpoints.

arms

a vector of arms' name on which number of events will be counted.

target_n_events

target number of events for each of the endpoints.

type

all if all target number of events are reached. any if the any target number of events is reached.

Returns

data lock time

Examples

## trial$get_data_lock_time_by_event_number(c('pfs','orr'), c(200,500), 'any')


Method get_data_lock_time_by_calendar_time()

given the calendar time to lock the data, return it with event counts of each of the endpoints.

Usage

Trials$get_data_lock_time_by_calendar_time(calendar_time, arms)

Arguments

calendar_time

numeric. Calendar time to lock the data

arms

a vector of arms' name on which number of events will be counted.

Returns

data lock time

Examples

## trial$get_data_lock_time_by_calendar_time(20)


Method get_locked_data()

return locked data for a milestone

Usage

Trials$get_locked_data(milestone_name)

Arguments

milestone_name

character, milestone name of which the locked data to be extracted.


Method get_locked_data_name()

return names of locked data

Usage

Trials$get_locked_data_name()


Method get_event_number()

return number of events at lock time of milestones

Usage

Trials$get_event_number(milestone_name = NULL)

Arguments

milestone_name

names of triggered milestones. Use all triggered milestones if NULL.


Method save_milestone_time()

save time of a new milestone.

Usage

Trials$save_milestone_time(milestone_time, milestone_name)

Arguments

milestone_time

numeric. Time of new milestone.

milestone_name

character. Name of new milestone.


Method get_milestone_time()

return milestone time when triggering a given milestone

Usage

Trials$get_milestone_time(milestone_name)

Arguments

milestone_name

character. Name of milestone.


Method lock_data()

lock data at specific calendar time. For time-to-event endpoints, their event indicator *_event should be updated accordingly. Locked data should be stored separately. DO NOT OVERWRITE/UPDATE private$trial_data! which can lose actual time-to-event information. For example, a patient may be censored at the first data lock. However, he may have event being observed in a later data lock.

Usage

Trials$lock_data(at_calendar_time, milestone_name)

Arguments

at_calendar_time

time point to lock trial data

milestone_name

assign milestone name as the name of locked data for future reference.


Method event_plot()

plot of cumulative number of events/samples over calendar time.

Usage

Trials$event_plot()


Method censor_trial_data()

censor trial data at calendar time

Usage

Trials$censor_trial_data(
  censor_at = NULL,
  selected_arms = NULL,
  enrolled_before = Inf
)

Arguments

censor_at

time of censoring. It is set to trial duration if NULL.

selected_arms

censoring is applied to selected arms (e.g., removed arms) only. If NULL, it will be set to all available arms in trial data. Otherwise, censoring is applied to user-specified arms only. This is necessary because number of events/sample size in removed arms should be fixed unchanged since corresponding milestone is triggered. In that case, one can update trial data by something like censor_trial_data(censor_at = milestone_time, selected_arms = removed_arms).

enrolled_before

censoring is applied to patients enrolled before specific time. This argument would be used when trial duration is updated by set_duration. Adaptation happens when set_duration is called so we fix duration for patients enrolled before adaptation to maintain independent increment. This should work when trial duration is updated for multiple times.


Method save()

save a single value or a one-row data frame to trial's output for further analysis/summary later.

Usage

Trials$save(value, name = "", overwrite = FALSE)

Arguments

value

value to be saved. It can be a vector (of length 1) or a data frame (of one row).

name

character to name the saved object. It will be used to name a column in trial's output if value is a vector. If value is a data frame, name will be the prefix pasted with the column name of value in trial's output. If user want to use value's column name as is in trial's output, set name to be '' as default. Otherwise, column name would be, e.g., "{name}_<{names(value)}>".

overwrite

logic. TRUE if overwriting existing entries with warning, otherwise, throwing an error and stop.


Method bind()

row bind a data frame to existing data frame. If name is not existing in Trial, then it is equivalent to Trial$save. Extra columns in value are ignored. Columns in Trial$custom_data[[name]] but not in value are filled with NA.

Usage

Trials$bind(value, name)

Arguments

value

a data frame to be saved. It can consist of one or multiple rows.

name

character. Name of object to be saved.


Method save_custom_data()

save arbitrary (number of) objects into a trial so that users can use those to control the workflow. Most common use case is to store simulation parameters to be used in action functions.

Usage

Trials$save_custom_data(value, name, overwrite = FALSE)

Arguments

value

value to be saved. Any type.

name

character. Name of the value to be accessed later.

overwrite

logic. TRUE if overwriting existing entries with warning, otherwise, throwing an error and stop.


Method get_custom_data()

return saved custom data of specified name.

Usage

Trials$get_custom_data(name)

Arguments

name

character. Name of custom data to be accessed.


Method get_output()

return a data frame of all current outputs saved by calling save.

Usage

Trials$get_output(cols = NULL, simplify = TRUE)

Arguments

cols

columns to be returned from Trial$output. If NULL, all columns are returned.

simplify

logical. Return value rather than a data frame of one column when length(col) == 1 and simplify == TRUE.


Method mute()

mute all messages (not including warnings)

Usage

Trials$mute(silent)

Arguments

silent

logical.


Method independentIncrement()

calculate independent increments from a given set of milestones

Usage

Trials$independentIncrement(endpoint, placebo, milestones, planned_info, ...)

Arguments

endpoint

character. Name of time-to-event endpoint in trial's locked data.

placebo

character. String of placebo in trial's locked data.

milestones

a character vector of milestone names in the trial, e.g., listener$get_milestone_names().

planned_info

a vector of planned accumulative number of event of time-to-event endpoint. Note: planned_info can also be a character "oracle" so that planned number of events are set to be observed number of events, in that case inverse normal z statistics equal to one-sided logrank statistics. This is for the purpose of debugging only. In formal simulation, "oracle" should not be used if adaptation is present. Pre-fixed planned_info should be used to create weights in combination test that controls the family-wise error rate in the strong sense.

...

subset condition that is compatible with dplyr::filter. survdiff will be fitted on this subset only to compute one-sided logrank statistics. It could be useful when a trial consists of more than two arms. By default it is not specified, all data will be used to fit the model.

Returns

This function returns a data frame with columns:

p_inverse_normal

one-sided p-value for inverse normal test based on logrank test (alternative hypothesis: risk is higher in placebo arm). Accumulative data is used.

z_inverse_normal

z statistics of p_inverse_normal. Accumulative data is used.

p_lr

one-sided p-value for logrank test (alternative hypothesis: risk is higher in placebo arm). Accumulative data is used.

z_lr

z statistics of p_lr. Accumulative data is used.

info

observed accumulative event number.

planned_info

planned accumulative event number.

info_pbo

observed accumulative event number in placebo.

info_trt

observed accumulative event number in treatment arm.

wt

weights in z_inverse_normal.

Examples


\dontrun{
trial$independentIncrement('pfs', 'pbo', listener$get_milestone_names(), 'oracle')
}


Method dunnettTest()

carry out closed test based on Dunnett method under group sequential design.

Usage

Trials$dunnettTest(
  endpoint,
  placebo,
  treatments,
  milestones,
  planned_info,
  ...
)

Arguments

endpoint

character of an endpoint for Dunnett test.

placebo

character. Name of placebo arm.

treatments

character vector. Name of treatment arms to be used in comparison.

milestones

character vector. Names of triggered milestones at which either adaptation is applied or statistical testing for endpoint is performed. Milestones in milestones does not need to be sorted by their triggering time.

planned_info

a data frame of planned number of events of time-to-event endpoint in each stage and each arm. Milestone names, i.e., milestones are row names of planned_info, and arm names, i.e., c(placebo, treatments) are column names. Note that it is not the accumulative but stage-wise event numbers. It is usually not easy to determine these numbers in practice, simulation may be used to get estimates. Note: planned_info can also be a character "default" so that planned_info are set to be number of newly randomized patients in the control arm in each of the stages. This assumes that event rate do not change over time and/or sample ratio between placebo and a treatment arm does not change as well, which may not be true. It is for the purpose of debugging or rapid implementation only. Using simulation to pick planned_info is recommended in formal simulation study. Another issue with planned_info set to be "default" is that it is possible patient recruitment is done before a specific stage, as a result, planned_info is zero which can crash the program.

...

subset condition that is compatible with dplyr::filter. survdiff will be fitted on this subset only to compute one-sided logrank statistics. It could be useful when comparison is made on a subset of treatment arms. By default it is not specified, all data (placebo plus one treatment arm at a time) in the locked data are used to fit the model.

Details

This function computes stage-wise p-values for each of the intersection hypotheses based on Dunnett test. If only one treatment arm is present, it is equivalent to compute the stage-wise p-values of elemental hypotheses. This function also computes inverse normal combination test statistics at each of the stages. The choice of planned_info can affect the calculation of stage-wise p-values. Specifically, it is used to compute the columns observed_info and p_inverse_normal in returned data frame, which will be used in Trial$closedTest(). The choice of planned_info can affect the result of Trial$closedTest() so user should chose it with caution.

Note that in Trial$closedTest(), observed_info, which is derived from planned_info, will lead to the same closed testing results up to a constant. This is because the closed test uses information fraction observed_info/sum(observed_info). As a result, setting planned_info to, e.g., 10 * planned_info should give same closed test results.

Based on numerical study, setting planned_info = "default" leads to a much higher power (roughly 10%) than setting planned_info to median of event numbers at stages, which can be determined by simulation. I am not sure if regulator would support such practice. For example, if a milestone (e.g., interim analysis) is triggered at a pre-specified calendar time, the number of randomized patients is random and is unknown when planning the trial. If I understand it correctly, regulator may want the information fraction in closed test (combined with Dunnett test) to be pre-fixed. In addition, this choice for planned_info assumes that the event rates does not change over time which is obviously not true. It is recommended to always use pre-fixed planned_info for restrict control of family-wise error rate. It should be pointed out that the choice of pre-fixed planned_info can affect statistical power significantly so fine-tuning may be required.

Returns

a list with element names like arm_name, arm1_name|arm2_name, arm1_name|arm2_name|arm3_name, etc., i.e., all possible combination of treatment arms in comparison. Each element is a data frame, with its column names self-explained. Specifically, the columns p_inverse_normal, observed_info, is_final can be used with GroupSequentialTest to perform significance test.

Examples

\dontrun{
trial$dunnettTest('pfs', 'pbo', c('high dose', 'low dose'),
                  listener$get_milestone_names(), 'default')
}


Method closedTest()

perform closed test based on Dunnett test

Usage

Trials$closedTest(
  dunnett_test,
  treatments,
  milestones,
  alpha,
  alpha_spending = c("asP", "asOF")
)

Arguments

dunnett_test

object returned by Trial$dunnettTest().

treatments

character vector. Name of treatment arms to be used in comparison.

milestones

character vector. Names of triggered milestones at which significance testing for endpoint is performed in closed test. Milestones in milestones does not need to be sorted by their triggering time.

alpha

numeric. Allocated alpha.

alpha_spending

alpha spending function. It can be "asP" or "asOF". Note that theoretically it can be "asUser", but it is not tested. It may be supported in the future.

Returns

a data frame of columns arm, decision, milestone_at_reject, and reject_time.

Examples

\dontrun{
dt <- trial$dunnettTest(
  'pfs', 'pbo', c('high dose', 'low dose'),
  listener$get_milestone_names(), 'default')

trial$closedTest(dt, c('high dose', 'low dose'),
                 c('pfs interim', 'pfs final'),
                 0.025, 'asOF')
}


Method get_seed()

return random seed

Usage

Trials$get_seed()


Method print()

print a trial

Usage

Trials$print()


Method get_snapshot_copy()

return a snapshot of a trial before it is executed.

Usage

Trials$get_snapshot_copy()


Method make_snapshot()

make a snapshot before running a trial. This can be useful when resetting a trial. This is only called when initializing a `Trial` object, when arms have not been added yet.

Usage

Trials$make_snapshot()


Method make_arms_snapshot()

make a snapshot of arms

Usage

Trials$make_arms_snapshot()


Method reset()

reset a trial to its snapshot taken before it was executed. Seed will be reassigned with a new one. Enrollment time are re-generated. If the trial already have arms when this function is called, they are added back to recruit patients again.

Usage

Trials$reset()


Method clone()

The objects of this class are cloneable with this method.

Usage

Trials$clone(deep = FALSE)

Arguments

deep

Whether to make a deep clone.

Examples

# Instead of using Trial$new, please use trial(), a user-friendly
# wrapper. See examples in ?trial.


## ------------------------------------------------
## Method `Trials$get_data_lock_time_by_event_number`
## ------------------------------------------------

## trial$get_data_lock_time_by_event_number(c('pfs','orr'), c(200,500), 'any')

## ------------------------------------------------
## Method `Trials$get_data_lock_time_by_calendar_time`
## ------------------------------------------------

## trial$get_data_lock_time_by_calendar_time(20)

## ------------------------------------------------
## Method `Trials$independentIncrement`
## ------------------------------------------------


if (FALSE) { # \dontrun{
trial$independentIncrement('pfs', 'pbo', listener$get_milestone_names(), 'oracle')
} # }

## ------------------------------------------------
## Method `Trials$dunnettTest`
## ------------------------------------------------

if (FALSE) { # \dontrun{
trial$dunnettTest('pfs', 'pbo', c('high dose', 'low dose'),
                  listener$get_milestone_names(), 'default')
} # }


## ------------------------------------------------
## Method `Trials$closedTest`
## ------------------------------------------------

if (FALSE) { # \dontrun{
dt <- trial$dunnettTest(
  'pfs', 'pbo', c('high dose', 'low dose'),
  listener$get_milestone_names(), 'default')

trial$closedTest(dt, c('high dose', 'low dose'),
                 c('pfs interim', 'pfs final'),
                 0.025, 'asOF')
} # }