@@ -103,17 +103,19 @@ Warn = function(msg, ...) rlang::warn(break_str(msg, init = "Warning: "), ...)
103
103
# ' Assert that a sliding computation function takes enough args
104
104
# '
105
105
# ' @param f Function; specifies a computation to slide over an `epi_df` or
106
- # ' `epi_archive` in `epi_slide` or `epix_slide`.
106
+ # ' `epi_archive` in `epi_slide` or `epix_slide`.
107
107
# ' @param ... Dots that will be forwarded to `f` from the dots of `epi_slide` or
108
108
# ' `epix_slide`.
109
+ # ' @param n_mandatory_f_args Integer; specifies the number of arguments `f`
110
+ # ' is required to take before any `...` arg. Defaults to 2.
109
111
# '
110
112
# ' @importFrom rlang is_missing
111
113
# ' @importFrom purrr map_lgl
112
114
# ' @importFrom utils tail
113
115
# '
114
116
# ' @noRd
115
- assert_sufficient_f_args <- function (f , ... ) {
116
- mandatory_f_args_labels <- c(" window data" , " group key" )
117
+ assert_sufficient_f_args <- function (f , ... , n_mandatory_f_args = 2L ) {
118
+ mandatory_f_args_labels <- c(" window data" , " group key" , " reference time value " )[seq( n_mandatory_f_args )]
117
119
n_mandatory_f_args <- length(mandatory_f_args_labels )
118
120
args = formals(args(f ))
119
121
args_names = names(args )
@@ -181,6 +183,109 @@ assert_sufficient_f_args <- function(f, ...) {
181
183
}
182
184
}
183
185
186
+ # ' Convert to function
187
+ # '
188
+ # ' @description
189
+ # ' `as_slide_computation()` transforms a one-sided formula into a function.
190
+ # ' This powers the lambda syntax in packages like purrr.
191
+ # '
192
+ # ' This code and documentation borrows heavily from [`rlang::as_function`]
193
+ # ' (https://github.com/r-lib/rlang/blob/c55f6027928d3104ed449e591e8a225fcaf55e13/R/fn.R#L343-L427).
194
+ # '
195
+ # ' This code extends `rlang::as_function` to create functions that take three
196
+ # ' arguments. The arguments can be accessed via the idiomatic `.x`, `.y`,
197
+ # ' etc, positional references (`..1`, `..2`, etc), and also by `epi
198
+ # ' [x]_slide`-specific names.
199
+ # '
200
+ # ' @source https://github.com/r-lib/rlang/blob/c55f6027928d3104ed449e591e8a225fcaf55e13/R/fn.R#L343-L427
201
+ # '
202
+ # ' @param x A function or formula.
203
+ # '
204
+ # ' If a **function**, it is used as is.
205
+ # '
206
+ # ' If a **formula**, e.g. `~ mean(.x$cases)`, it is converted to a function with up
207
+ # ' to three arguments: `.x` (single argument), or `.x` and `.y`
208
+ # ' (two arguments), or `.x`, `.y`, and `.z` (three arguments). The `.`
209
+ # ' placeholder can be used instead of `.x`, `.group_key` can be used in
210
+ # ' place of `.y`, and `.ref_time_value` can be used in place of `.z`. This
211
+ # ' allows you to create very compact anonymous functions (lambdas) with up
212
+ # ' to three inputs. Functions created from formulas have a special class. Use
213
+ # ' `rlang::is_lambda()` to test for it.
214
+ # '
215
+ # ' If a **string**, the function is looked up in `env`. Note that
216
+ # ' this interface is strictly for user convenience because of the
217
+ # ' scoping issues involved. Package developers should avoid
218
+ # ' supplying functions by name and instead supply them by value.
219
+ # '
220
+ # ' @param env Environment in which to fetch the function in case `x`
221
+ # ' is a string.
222
+ # ' @inheritParams rlang::args_dots_empty
223
+ # ' @inheritParams rlang::args_error_context
224
+ # ' @examples
225
+ # ' f <- as_slide_computation(~ .x + 1)
226
+ # ' f(10)
227
+ # '
228
+ # ' g <- as_slide_computation(~ -1 * .)
229
+ # ' g(4)
230
+ # '
231
+ # ' h <- as_slide_computation(~ .x - .group_key)
232
+ # ' h(6, 3)
233
+ # '
234
+ # ' @importFrom rlang check_dots_empty0 is_function new_function f_env
235
+ # ' is_environment missing_arg f_rhs is_string is_formula caller_arg
236
+ # ' caller_env global_env
237
+ # '
238
+ # ' @noRd
239
+ as_slide_computation <- function (x ,
240
+ env = global_env(),
241
+ ... ,
242
+ arg = caller_arg(x ),
243
+ call = caller_env()) {
244
+ check_dots_empty0(... )
245
+
246
+ if (is_function(x )) {
247
+ return (x )
248
+ }
249
+
250
+ if (is_formula(x )) {
251
+ if (length(x ) > 2 ) {
252
+ Abort(sprintf(" %s must be a one-sided formula" , arg ),
253
+ class = " epiprocess__as_slide_computation__formula_is_twosided" ,
254
+ epiprocess__x = x ,
255
+ call = call )
256
+ }
257
+
258
+ env <- f_env(x )
259
+ if (! is_environment(env )) {
260
+ Abort(" Formula must carry an environment." ,
261
+ class = " epiprocess__as_slide_computation__formula_has_no_env" ,
262
+ epiprocess__x = x ,
263
+ epiprocess__x_env = env ,
264
+ arg = arg , call = call )
265
+ }
266
+
267
+ args <- list (
268
+ ... = missing_arg(),
269
+ .x = quote(..1 ), .y = quote(..2 ), .z = quote(..3 ),
270
+ . = quote(..1 ), .group_key = quote(..2 ), .ref_time_value = quote(..3 )
271
+ )
272
+ fn <- new_function(args , f_rhs(x ), env )
273
+ fn <- structure(fn , class = c(" epiprocess_slide_computation" , " function" ))
274
+ return (fn )
275
+ }
276
+
277
+ if (is_string(x )) {
278
+ return (get(x , envir = env , mode = " function" ))
279
+ }
280
+
281
+ Abort(sprintf(" Can't convert a %s to a slide computation" , class(x )),
282
+ class = " epiprocess__as_slide_computation__cant_convert_catchall" ,
283
+ epiprocess__x = x ,
284
+ epiprocess__x_class = class(x ),
285
+ arg = arg ,
286
+ call = call )
287
+ }
288
+
184
289
# #########
185
290
186
291
in_range = function (x , rng ) pmin(pmax(x , rng [1 ]), rng [2 ])
0 commit comments