1+ .trigger_to_quosures <- function (trigger ) {
2+ if (! is.null(trigger $ type )) {
3+ expr <- if (identical(trigger $ type , " value" )) {
4+ call(trigger $ op , call(" [[" , quote(.data ), trigger $ col ), trigger $ rhs )
5+ } else {
6+ call(trigger $ op ,
7+ call(" sum" , call(" !" , call(" is.na" , call(" [[" , quote(.data ), trigger $ col )))),
8+ trigger $ rhs )
9+ }
10+ return (list (rlang :: new_quosure(expr , env = rlang :: current_env())))
11+ }
12+
13+ if (identical(trigger $ combinator , " &" )) {
14+ return (unlist(lapply(trigger $ predicates , .trigger_to_quosures ), recursive = FALSE ))
15+ }
16+
17+ left_quos <- .trigger_to_quosures(trigger $ left )
18+ right_quos <- .trigger_to_quosures(trigger $ right )
19+ expr <- call(" |" , rlang :: get_expr(left_quos [[1L ]]), rlang :: get_expr(right_quos [[1L ]]))
20+ list (rlang :: new_quosure(expr , env = rlang :: current_env()))
21+ }
22+
123# ' Condition: Stateful trigger and analysis unit
224# '
325# ' @description
2951# ' is not updated.
3052# '
3153# ' On a successful trigger, the condition calls
32- # ' `analysis(filtered_data, current_time)` and stores the result under
33- # ' `name` (or `1L` when no name is set). If no analysis function is
54+ # ' `analysis(df, current_time, ...)` and stores the result under
55+ # ' `name` (or `1L` when no name is set). Any values in `analysis_args` are
56+ # ' appended as additional named arguments. If no analysis function is
3457# ' provided, the filtered data frame is returned as-is with a warning.
3558# '
3659# ' @section Fields:
3962# ' `dplyr::filter()` predicates. Pass `NULL` or an empty list to skip
4063# ' filtering and pass the full snapshot to the analysis.}
4164# ' \item{\code{analysis}}{`function` or `NULL`. Called as
42- # ' `analysis(filtered_data, current_time)` on a successful trigger.
43- # ' Should return a `data.frame` or named list. If `NULL`, the filtered
44- # ' data frame is returned with a warning.}
65+ # ' `analysis(df, current_time, ...)` on a successful trigger, where `...`
66+ # ' are any values from `analysis_args`. Should return a `data.frame` or
67+ # ' named list. If `NULL`, the filtered data frame is returned with a warning.}
68+ # ' \item{\code{analysis_args}}{`list` or `NULL`. Named list of extra arguments
69+ # ' injected into every call to `analysis`.}
4570# ' \item{\code{name}}{`character` or `NULL`. Key used to label the result
4671# ' in the returned list. Falls back to `1L` when `NULL`.}
4772# ' \item{\code{cooldown}}{`numeric`. Minimum time units that must elapse
7499# ' \item [`Trial`] for running the simulation and iterating over conditions
75100# ' \item [`trigger_by_calendar()`] and [`trigger_by_fraction()`] for
76101# ' convenient `Condition` constructors
102+ # ' \item [value_trigger()], [count_trigger()], [enroll_trigger()],
103+ # ' [calendar_trigger()] for building safe trigger specifications
77104# ' \item [`dplyr::filter()`] for predicate syntax
78105# ' }
79106# '
86113# ' )
87114# '
88115# ' # Analysis function: count active subjects per arm
89- # ' count_fn <- function(dat , current_time) {
90- # ' data.frame(n_active = nrow(dat ), fired_at = current_time)
116+ # ' count_fn <- function(df , current_time) {
117+ # ' data.frame(n_active = nrow(df ), fired_at = current_time)
91118# ' }
92119# '
93120# ' # Condition fires once when arm A has active subjects (max_triggers = 1)
@@ -115,13 +142,19 @@ Condition <- R6::R6Class(
115142 public = list (
116143 # --- fields ---
117144 # ' @field where `list` of quosures (`rlang::quos()`) used as `dplyr::filter()`
118- # ' predicates. `NULL` or empty list passes the full snapshot.
145+ # ' predicates, or an `rxsim_trigger` (converted automatically). `NULL` or
146+ # ' empty list passes the full snapshot.
119147 where = NULL ,
120148
121149 # ' @field analysis `function` or `NULL`. Called as
122- # ' `analysis(filtered_data, current_time)` on a successful trigger.
150+ # ' `analysis(df, current_time, ...)` on a successful trigger, where `...`
151+ # ' are any values from `analysis_args`.
123152 analysis = NULL ,
124153
154+ # ' @field analysis_args `list` or `NULL`. Named list of extra values injected
155+ # ' into the analysis function call as additional named arguments.
156+ analysis_args = NULL ,
157+
125158 # ' @field name `character` or `NULL`. Key labelling the result in the output
126159 # ' list. Falls back to `1L` when `NULL`.
127160 name = NULL ,
@@ -146,26 +179,33 @@ Condition <- R6::R6Class(
146179 # ' @description
147180 # ' Create a new `Condition` instance.
148181 # '
149- # ' @param where `list` of quosures (from `rlang::quos()`) used as filter
150- # ' predicates. Pass `NULL` or omit to use the full snapshot.
182+ # ' @param where `rxsim_trigger` (converted automatically to quosures), a
183+ # ' `list` of quosures from `rlang::quos()`, or `NULL` to use the full
184+ # ' snapshot.
151185 # ' @param analysis `function` or `NULL`. Called as
152- # ' `analysis(filtered_data, current_time)` on a successful trigger.
186+ # ' `analysis(df, current_time, ...)` on a successful trigger, where `...`
187+ # ' are the values from `analysis_args`.
188+ # ' @param analysis_args `list` or `NULL`. Named list of extra arguments
189+ # ' passed to the analysis function after `df` and `current_time`.
153190 # ' @param name `character` or `NULL`. Result key. Defaults to `1L`.
154191 # ' @param cooldown `numeric`. Minimum time between triggers. Default `0`.
155192 # ' @param max_triggers `integer`. Maximum trigger count. Default `1L`.
156193 # ' Use `Inf` for unlimited.
157194 # '
158195 # ' @return A new `Condition` instance.
159196 initialize = function (
160- where = NULL ,
161- analysis = NULL ,
162- name = NULL ,
163- cooldown = 0 ,
164- max_triggers = 1L
197+ where = NULL ,
198+ analysis = NULL ,
199+ analysis_args = NULL ,
200+ name = NULL ,
201+ cooldown = 0 ,
202+ max_triggers = 1L
165203 ) {
166- self $ where <- where
167- self $ analysis <- analysis
168- self $ name <- name
204+ if (inherits(where , " rxsim_trigger" )) where <- .trigger_to_quosures(where )
205+ self $ where <- where
206+ self $ analysis <- analysis
207+ self $ analysis_args <- analysis_args
208+ self $ name <- name
169209
170210 cooldown <- as.numeric(cooldown )
171211 if (length(cooldown ) != 1L || cooldown < 0 || is.na(cooldown )) {
@@ -230,7 +270,7 @@ Condition <- R6::R6Class(
230270 }
231271
232272 if (is.function(self $ analysis )) {
233- results [[key ]] <- self $ analysis( df_i , current_time )
273+ results [[key ]] <- do.call( self $ analysis , c( list ( df_i , current_time ), self $ analysis_args ) )
234274 } else {
235275 results [[key ]] <- df_i
236276 warning(
0 commit comments