Skip to content

Commit e188e2b

Browse files
committed
move quosure -> function creation to as_slide_computation
1 parent 3ae827d commit e188e2b

File tree

3 files changed

+98
-73
lines changed

3 files changed

+98
-73
lines changed

R/grouped_epi_archive.R

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ grouped_epi_archive =
293293

294294
# If f is not missing, then just go ahead, slide by group
295295
if (!missing(f)) {
296-
f = as_slide_computation(f, ...)
296+
f = as_slide_computation(f, calc_ref_time_value = FALSE, ...)
297297
x = purrr::map_dfr(ref_time_values, function(ref_time_value) {
298298
# Ungrouped as-of data; `epi_df` if `all_versions` is `FALSE`,
299299
# `epi_archive` if `all_versions` is `TRUE`:
@@ -365,21 +365,7 @@ grouped_epi_archive =
365365
}
366366

367367
quo = quos[[1]]
368-
f = function(.x, .group_key, .ref_time_value, quo, ...) {
369-
# Convert to environment to standardize between tibble and R6
370-
# based inputs. In both cases, we should get a simple
371-
# environment with the empty environment as its parent.
372-
data_env = rlang::as_environment(.x)
373-
data_mask = rlang::new_data_mask(bottom = data_env, top = data_env)
374-
data_mask$.data <- rlang::as_data_pronoun(data_mask)
375-
# We'll also install `.x` directly, not as an
376-
# `rlang_data_pronoun`, so that we can, e.g., use more dplyr and
377-
# epiprocess operations.
378-
data_mask$.x = .x
379-
data_mask$.group_key = .group_key
380-
data_mask$.ref_time_value = .ref_time_value
381-
rlang::eval_tidy(quo, data_mask)
382-
}
368+
f = as_slide_computation(quo, calc_ref_time_value = FALSE, ...)
383369
new_col = sym(names(rlang::quos_auto_name(quos)))
384370

385371
x = purrr::map_dfr(ref_time_values, function(ref_time_value) {

R/slide.R

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -353,24 +353,18 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
353353

354354
# If f is not missing, then just go ahead, slide by group
355355
if (!missing(f)) {
356-
f = as_slide_computation(f, ...)
357-
f_rtv_wrapper = function(x, g, ...) {
358-
ref_time_value = min(x$time_value) + before
359-
x <- x[x$.real,]
360-
x$.real <- NULL
361-
f(x, g, ref_time_value, ...)
362-
}
363-
x = x %>%
356+
f = as_slide_computation(f, calc_ref_time_value = TRUE, before = before, ...)
357+
x = x %>%
364358
group_modify(slide_one_grp,
365-
f = f_rtv_wrapper, ...,
359+
f = f, ...,
366360
starts = starts,
367361
stops = stops,
368-
time_values = ref_time_values,
362+
time_values = ref_time_values,
369363
all_rows = all_rows,
370364
new_col = new_col,
371365
.keep = FALSE)
372366
}
373-
367+
374368
# Else interpret ... as an expression for tidy evaluation
375369
else {
376370
quos = enquos(...)
@@ -382,29 +376,15 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
382376
}
383377

384378
quo = quos[[1]]
385-
f = function(.x, .group_key, quo, ...) {
386-
.ref_time_value = min(.x$time_value) + before
387-
.x <- .x[.x$.real,]
388-
.x$.real <- NULL
389-
390-
data_env = rlang::as_environment(.x)
391-
data_mask = rlang::new_data_mask(bottom = data_env, top = data_env)
392-
data_mask$.data <- rlang::as_data_pronoun(data_mask)
393-
# We'll also install `.x` directly, not as an `rlang_data_pronoun`, so
394-
# that we can, e.g., use more dplyr and epiprocess operations.
395-
data_mask$.x = .x
396-
data_mask$.group_key = .group_key
397-
data_mask$.ref_time_value = .ref_time_value
398-
rlang::eval_tidy(quo, data_mask)
399-
}
400379
new_col = sym(names(rlang::quos_auto_name(quos)))
401-
402-
x = x %>%
380+
381+
f = as_slide_computation(quo, calc_ref_time_value = TRUE, before = before, ...)
382+
x = x %>%
403383
group_modify(slide_one_grp,
404384
f = f, quo = quo,
405385
starts = starts,
406386
stops = stops,
407-
time_values = ref_time_values,
387+
time_values = ref_time_values,
408388
all_rows = all_rows,
409389
new_col = new_col,
410390
.keep = FALSE)

R/utils.R

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,15 @@ assert_sufficient_f_args <- function(f, ...) {
215215
#' scoping issues involved. Package developers should avoid
216216
#' supplying functions by name and instead supply them by value.
217217
#'
218+
#' @param calc_ref_time_value Boolean indicating whether the computation
219+
#' function should include a step to calculate `ref_time_value` based on the
220+
#' contents of the group data `.x`. This is used in `epi_slide`. When this
221+
#' flag is `FALSE`, as is the default, the resulting computation takes the
222+
#' three standard arguments, group data, group key(s), and reference time
223+
#' value, plus any extra arguments. When this flag is `TRUE`, the resulting
224+
#' computation only takes two of the standard arguments, group data and
225+
#' group key(s), plus any extra arguments. The `ref_time_value` argument is
226+
#' unnecessary since its value is being calculated within the computation.
218227
#' @param env Environment in which to fetch the function in case `x`
219228
#' is a string.
220229
#' @inheritParams rlang::args_dots_empty
@@ -235,47 +244,97 @@ assert_sufficient_f_args <- function(f, ...) {
235244
#'
236245
#' @noRd
237246
as_slide_computation <- function(x,
247+
calc_ref_time_value = FALSE,
248+
before,
238249
env = global_env(),
239250
...,
240251
arg = caller_arg(x),
241252
call = caller_env()) {
242-
if (is_function(x)) {
243-
# Check that `f` takes enough args
244-
assert_sufficient_f_args(x, ...)
245-
return(x)
253+
# A quosure is a type of formula, so be careful with `if` logic here.
254+
if (is_quosure(x)) {
255+
if (calc_ref_time_value) {
256+
f_wrapper = function(.x, .group_key, quo, ...) {
257+
.ref_time_value = min(.x$time_value) + before
258+
.x <- .x[.x$.real,]
259+
.x$.real <- NULL
260+
261+
data_env = rlang::as_environment(.x)
262+
data_mask = rlang::new_data_mask(bottom = data_env, top = data_env)
263+
data_mask$.data <- rlang::as_data_pronoun(data_mask)
264+
# We'll also install `.x` directly, not as an `rlang_data_pronoun`, so
265+
# that we can, e.g., use more dplyr and epiprocess operations.
266+
data_mask$.x = .x
267+
data_mask$.group_key = .group_key
268+
data_mask$.ref_time_value = .ref_time_value
269+
rlang::eval_tidy(quo, data_mask)
270+
}
271+
return(f_wrapper)
272+
}
273+
274+
f_wrapper = function(.x, .group_key, .ref_time_value, quo, ...) {
275+
# Convert to environment to standardize between tibble and R6
276+
# based inputs. In both cases, we should get a simple
277+
# environment with the empty environment as its parent.
278+
data_env = rlang::as_environment(.x)
279+
data_mask = rlang::new_data_mask(bottom = data_env, top = data_env)
280+
data_mask$.data <- rlang::as_data_pronoun(data_mask)
281+
# We'll also install `.x` directly, not as an
282+
# `rlang_data_pronoun`, so that we can, e.g., use more dplyr and
283+
# epiprocess operations.
284+
data_mask$.x = .x
285+
data_mask$.group_key = .group_key
286+
data_mask$.ref_time_value = .ref_time_value
287+
rlang::eval_tidy(quo, data_mask)
288+
}
289+
return(f_wrapper)
246290
}
247291

248-
if (is_formula(x)) {
249-
if (length(x) > 2) {
250-
Abort(sprintf("%s must be a one-sided formula", arg),
251-
class = "epiprocess__as_slide_computation__formula_is_twosided",
252-
epiprocess__x = x,
253-
call = call)
292+
if (is_function(x) || is_formula(x)) {
293+
if (is_function(x)) {
294+
# Check that `f` takes enough args
295+
assert_sufficient_f_args(x, ...)
296+
fn <- x
297+
}
298+
299+
if (is_formula(x)) {
300+
if (length(x) > 2) {
301+
Abort(sprintf("%s must be a one-sided formula", arg),
302+
class = "epiprocess__as_slide_computation__formula_is_twosided",
303+
epiprocess__x = x,
304+
call = call)
305+
}
306+
307+
env <- f_env(x)
308+
if (!is_environment(env)) {
309+
Abort("Formula must carry an environment.",
310+
class = "epiprocess__as_slide_computation__formula_has_no_env",
311+
epiprocess__x = x,
312+
epiprocess__x_env = env,
313+
arg = arg, call = call)
314+
}
315+
316+
args <- list(
317+
... = missing_arg(),
318+
.x = quote(..1), .y = quote(..2), .z = quote(..3),
319+
. = quote(..1), .group_key = quote(..2), .ref_time_value = quote(..3)
320+
)
321+
fn <- new_function(args, f_rhs(x), env)
322+
fn <- structure(fn, class = c("epiprocess_slide_computation", "function"))
254323
}
255324

256-
env <- f_env(x)
257-
if (!is_environment(env)) {
258-
Abort("Formula must carry an environment.",
259-
class = "epiprocess__as_slide_computation__formula_has_no_env",
260-
epiprocess__x = x,
261-
epiprocess__x_env = env,
262-
arg = arg, call = call)
325+
if (calc_ref_time_value) {
326+
f_wrapper = function(.x, .group_key, ...) {
327+
.ref_time_value = min(.x$time_value) + before
328+
.x <- .x[.x$.real,]
329+
.x$.real <- NULL
330+
fn(.x, .group_key, .ref_time_value, ...)
331+
}
332+
return(f_wrapper)
263333
}
264334

265-
args <- list(
266-
... = missing_arg(),
267-
.x = quote(..1), .y = quote(..2), .z = quote(..3),
268-
. = quote(..1), .group_key = quote(..2), .ref_time_value = quote(..3)
269-
)
270-
fn <- new_function(args, f_rhs(x), env)
271-
fn <- structure(fn, class = c("epiprocess_slide_computation", "function"))
272335
return(fn)
273336
}
274337

275-
if (is_string(x)) {
276-
return(get(x, envir = env, mode = "function"))
277-
}
278-
279338
Abort(sprintf("Can't convert a %s to a slide computation", class(x)),
280339
class = "epiprocess__as_slide_computation__cant_convert_catchall",
281340
epiprocess__x = x,

0 commit comments

Comments
 (0)