Condition: Stateful trigger and analysis unit
Condition.RdA Condition encapsulates a single trigger rule that is evaluated against
a data snapshot at each simulated timepoint. It combines three concerns:
Filtering - a
dplyr::filter()expression selects the rows relevant to this condition (e.g. "only enrolled subjects in arm A").Analysis - an optional function transforms the filtered snapshot into a result (e.g. a t-test, a subject count, a Go/No-Go decision).
Trigger bookkeeping - the condition fires only when the filtered data is non-empty, the cooldown period has elapsed since the last trigger, and the maximum trigger count has not been reached.
Condition objects are stored in trial$conditions and evaluated by
Trial$run() at each timepoint.
Details
Three-gate logic. A trigger fires only when all three gates pass:
The filtered snapshot contains at least one row.
trigger_count < max_triggers.current_time - last_trigger_time >= cooldown(or the condition has never fired before).
If any gate fails, check_conditions() returns an empty list and state
is not updated.
On a successful trigger, the condition calls
analysis(df, current_time, ...) and stores the result under
name (or 1L when no name is set). Any values in analysis_args are
appended as additional named arguments. If no analysis function is
provided, the filtered data frame is returned as-is with a warning.
See also
Timerfor managing trial timepointsTrialfor running the simulation and iterating over conditionsvalue_trigger(),count_trigger(),enroll_trigger(),calendar_trigger()for building trigger specificationscondition_calendar_time()andcondition_enrollment_fraction()for convenientConditionconstructors
Public fields
wherelistof quosures (rlang::quos()) used asdplyr::filter()predicates, or atriggerobject (converted automatically).NULLor empty list passes the full snapshot.trigger_specThe original
triggerobject passed towhere(before quosure conversion), orNULL. Used by the fixed fast path to compute the earliest possible firing time and skip non-firing timepoints.NULLmeans "evaluate at every timepoint" (safe fallback).analysisfunctionorNULL. Called asanalysis(df, current_time, ...)on a successful trigger, where...are any values fromanalysis_args.analysis_argslistorNULL. Named list of extra values injected into the analysis function call as additional named arguments.namecharacterorNULL. Key labelling the result in the output list. Falls back to1LwhenNULL.cooldownnumeric. Minimum time units between consecutive triggers. Default0.max_triggersintegerorInf. Maximum number of times this condition may fire. Default1L.trigger_countinteger. Number of successful triggers so far. Initialised to0L.last_trigger_timenumeric. Calendar time of the most recent successful trigger.NA_real_until first trigger.
Methods
Condition$new()
Create a new Condition instance.
Usage
Condition$new(
where = NULL,
analysis = NULL,
analysis_args = NULL,
name = NULL,
cooldown = 0,
max_triggers = 1L
)Arguments
wheretriggerobject (fromvalue_trigger(),count_trigger(),enroll_trigger(), orcalendar_trigger()), alistof quosures fromrlang::quos(), orNULLto use the full snapshot.analysisfunctionorNULL. Called asanalysis(df, current_time, ...)on a successful trigger, where...are the values fromanalysis_args.analysis_argslistorNULL. Named list of extra arguments passed to the analysis function afterdfandcurrent_time.namecharacterorNULL. Result key. Defaults to1L.cooldownnumeric. Minimum time between triggers. Default0.max_triggersinteger. Maximum trigger count. Default1L. UseInffor unlimited.
Condition$check_conditions()
Evaluate this condition against a data snapshot.
Applies the three-gate logic: non-empty filter result, cooldown
elapsed, and trigger count below max_triggers. Returns the analysis
result (or filtered data) on a successful trigger, or an empty list
otherwise.
Examples
# Build a snapshot data frame (enroll_time = NA means not yet enrolled)
snapshot <- data.frame(
arm = c("A", "A", "A", "B"),
status = c("active", "active", "active", "active"),
enroll_time = c(1, 2, 3, NA_real_),
stringsAsFactors = FALSE
)
# Analysis function: count enrolled subjects and record fire time
count_fn <- function(df, current_time) {
data.frame(n_active = nrow(df), fired_at = current_time)
}
# Condition fires once when 3+ of 4 subjects are enrolled (max_triggers = 1)
cond <- Condition$new(
where = enroll_trigger(fraction = 0.75, sample_size = 4),
analysis = count_fn,
name = "interim_A",
cooldown = 0,
max_triggers = 1L
)
# First call: fires and returns analysis result
res <- cond$check_conditions(snapshot, current_time = 5)
res[["interim_A"]] # data.frame(n_active = 4, fired_at = 5)
#> n_active fired_at
#> 1 3 5
# Second call: does not fire (max_triggers already reached)
res2 <- cond$check_conditions(snapshot, current_time = 6)
length(res2) # 0
#> [1] 0