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.
current_time - last_trigger_time >= cooldown(or the condition has never fired before).trigger_count < max_triggers.
If any gate fails, check_conditions() returns an empty list and state
is not updated.
On a successful trigger, the condition calls
analysis(filtered_data, current_time) and stores the result under
name (or 1L when no name is set). If no analysis function is
provided, the filtered data frame is returned as-is with a warning.
Fields
wherelistof quosures (fromrlang::quos()) used asdplyr::filter()predicates. PassNULLor an empty list to skip filtering and pass the full snapshot to the analysis.analysisfunctionorNULL. Called asanalysis(filtered_data, current_time)on a successful trigger. Should return adata.frameor named list. IfNULL, the filtered data frame is returned with a warning.namecharacterorNULL. Key used to label the result in the returned list. Falls back to1LwhenNULL.cooldownnumeric. Minimum time units that must elapse between consecutive triggers. Default0(no cooldown).max_triggersinteger. Maximum number of times this condition may fire. UseInffor unlimited. Default1L.trigger_countinteger. Number of successful triggers so far. Initialised to0L.last_trigger_timenumeric. Calendar time of the most recent successful trigger. Initialised toNA_real_.
Methods
$new(where, analysis, name, cooldown, max_triggers)Construct a new
Condition. All arguments exceptwhereare optional.cooldownmust be a single non-negative number;max_triggersmust be a single non-negative integer orInf.$check_conditions(locked_data, current_time)Evaluate the condition against
locked_dataatcurrent_time. Returns a namedlistcontaining the analysis result (or filtered data frame) if the condition fires, or an emptylistotherwise. On a successful trigger,trigger_countis incremented andlast_trigger_timeis updated.
See also
Timerfor managing trial timepointsTrialfor running the simulation and iterating over conditionstrigger_by_calendar()andtrigger_by_fraction()for convenientConditionconstructorsdplyr::filter()for predicate syntax
Public fields
wherelistof quosures (rlang::quos()) used asdplyr::filter()predicates.NULLor empty list passes the full snapshot.analysisfunctionorNULL. Called asanalysis(filtered_data, current_time)on a successful trigger.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
Method new()
Create a new Condition instance.
Usage
Condition$new(
where = NULL,
analysis = NULL,
name = NULL,
cooldown = 0,
max_triggers = 1L
)Arguments
wherelistof quosures (fromrlang::quos()) used as filter predicates. PassNULLor omit to use the full snapshot.analysisfunctionorNULL. Called asanalysis(filtered_data, current_time)on a successful trigger.namecharacterorNULL. Result key. Defaults to1L.cooldownnumeric. Minimum time between triggers. Default0.max_triggersinteger. Maximum trigger count. Default1L. UseInffor unlimited.
Method 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
snapshot <- data.frame(
arm = c("A", "A", "A", "B"),
status = c("active", "active", "active", "active"),
stringsAsFactors = FALSE
)
# Analysis function: count active subjects per arm
count_fn <- function(dat, current_time) {
data.frame(n_active = nrow(dat), fired_at = current_time)
}
# Condition fires once when arm A has active subjects (max_triggers = 1)
cond <- Condition$new(
where = rlang::quos(arm == "A", status == "active"),
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 = 3, 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