23
23
# ' If `f` is missing, then `...` will specify the computation.
24
24
# ' @param ... Additional arguments to pass to the function or formula specified
25
25
# ' via `f`. Alternatively, if `f` is missing, then the `...` is interpreted as
26
- # ' an expression for tidy evaluation. See details.
26
+ # ' an expression for tidy evaluation; in addition to referring to columns
27
+ # ' directly by name, the expression has access to `.data` and `.env` pronouns
28
+ # ' as in `dplyr` verbs, and can also refer to `.x`, `.group_key`, and
29
+ # ' `.ref_time_value`. See details.
27
30
# ' @param before,after How far `before` and `after` each `ref_time_value` should
28
31
# ' the sliding window extend? At least one of these two arguments must be
29
32
# ' provided; the other's default will be 0. Any value provided for either
119
122
# ' through the `new_col_name` argument.
120
123
# '
121
124
# ' @importFrom lubridate days weeks
122
- # ' @importFrom rlang .data .env !! enquo enquos sym
125
+ # ' @importFrom dplyr bind_rows group_vars filter select
126
+ # ' @importFrom rlang .data .env !! enquo enquos sym env
123
127
# ' @export
124
128
# ' @examples
125
129
# ' # slide a 7-day trailing average formula on cases
@@ -166,11 +170,8 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
166
170
167
171
# Check that `f` takes enough args
168
172
if (! missing(f ) && is.function(f )) {
169
- assert_sufficient_f_args(f , ... )
173
+ assert_sufficient_f_args(f , ... , n_mandatory_f_args = 3L )
170
174
}
171
-
172
- # Arrange by increasing time_value
173
- x = arrange(x , time_value )
174
175
175
176
if (missing(ref_time_values )) {
176
177
ref_time_values = unique(x $ time_value )
@@ -231,6 +232,35 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
231
232
after <- time_step(after )
232
233
}
233
234
235
+ min_ref_time_values = ref_time_values - before
236
+ min_ref_time_values_not_in_x <- min_ref_time_values [! (min_ref_time_values %in% unique(x $ time_value ))]
237
+
238
+ # Do set up to let us recover `ref_time_value`s later.
239
+ # A helper column marking real observations.
240
+ x $ .real = TRUE
241
+
242
+ # Create df containing phony data. Df has the same columns and attributes as
243
+ # `x`, but filled with `NA`s aside from grouping columns. Number of rows is
244
+ # equal to the number of `min_ref_time_values_not_in_x` we have * the
245
+ # number of unique levels seen in the grouping columns.
246
+ before_time_values_df = data.frame (time_value = min_ref_time_values_not_in_x )
247
+ if (length(group_vars(x )) != 0 ) {
248
+ before_time_values_df = dplyr :: cross_join(
249
+ # Get unique combinations of grouping columns seen in real data.
250
+ unique(x [, group_vars(x )]),
251
+ before_time_values_df
252
+ )
253
+ }
254
+ # Automatically fill in all other columns from `x` with `NA`s, and carry
255
+ # attributes over to new df.
256
+ before_time_values_df <- bind_rows(x [0 ,], before_time_values_df )
257
+ before_time_values_df $ .real <- FALSE
258
+
259
+ x <- bind_rows(before_time_values_df , x )
260
+
261
+ # Arrange by increasing time_value
262
+ x = arrange(x , time_value )
263
+
234
264
# Now set up starts and stops for sliding/hopping
235
265
time_range = range(unique(x $ time_value ))
236
266
starts = in_range(ref_time_values - before , time_range )
@@ -272,7 +302,9 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
272
302
o = .data_group $ time_value %in% time_values
273
303
num_ref_rows = sum(o )
274
304
275
- # Count the number of appearances of each reference time value
305
+ # Count the number of appearances of each reference time value (these
306
+ # appearances should all be real for now, but if we allow ref time values
307
+ # outside of .data_group's time values):
276
308
counts = .data_group %> %
277
309
dplyr :: filter(.data $ time_value %in% time_values ) %> %
278
310
dplyr :: count(.data $ time_value ) %> %
@@ -282,7 +314,7 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
282
314
! all(purrr :: map_lgl(slide_values_list , is.data.frame ))) {
283
315
Abort(" The slide computations must return always atomic vectors or data frames (and not a mix of these two structures)." )
284
316
}
285
-
317
+
286
318
# Unlist if appropriate:
287
319
slide_values =
288
320
if (as_list_col ) {
@@ -318,16 +350,24 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
318
350
# fills with NA equivalent.
319
351
vctrs :: vec_slice(slide_values , o ) = orig_values
320
352
} else {
353
+ # This implicitly removes phony (`.real` == FALSE) observations.
321
354
.data_group = filter(.data_group , o )
322
355
}
323
356
return (mutate(.data_group , !! new_col : = slide_values ))
324
357
}
325
358
326
359
# If f is not missing, then just go ahead, slide by group
327
360
if (! missing(f )) {
361
+ if (rlang :: is_formula(f )) f = as_slide_computation(f )
362
+ f_rtv_wrapper = function (x , g , ... ) {
363
+ ref_time_value = min(x $ time_value ) + before
364
+ x <- x [x $ .real ,]
365
+ x $ .real <- NULL
366
+ f(x , g , ref_time_value , ... )
367
+ }
328
368
x = x %> %
329
369
group_modify(slide_one_grp ,
330
- f = f , ... ,
370
+ f = f_rtv_wrapper , ... ,
331
371
starts = starts ,
332
372
stops = stops ,
333
373
time_values = ref_time_values ,
@@ -347,7 +387,18 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
347
387
}
348
388
349
389
quo = quos [[1 ]]
350
- f = function (x , quo , ... ) rlang :: eval_tidy(quo , x )
390
+ f = function (.x , .group_key , quo , ... ) {
391
+ .ref_time_value = min(.x $ time_value ) + before
392
+ .x <- .x [.x $ .real ,]
393
+ .x $ .real <- NULL
394
+ data_mask = rlang :: as_data_mask(.x )
395
+ # We'll also install `.x` directly, not as an `rlang_data_pronoun`, so
396
+ # that we can, e.g., use more dplyr and epiprocess operations.
397
+ data_mask $ .x = .x
398
+ data_mask $ .group_key = .group_key
399
+ data_mask $ .ref_time_value = .ref_time_value
400
+ rlang :: eval_tidy(quo , data_mask )
401
+ }
351
402
new_col = sym(names(rlang :: quos_auto_name(quos )))
352
403
353
404
x = x %> %
@@ -365,5 +416,15 @@ epi_slide = function(x, f, ..., before, after, ref_time_values,
365
416
if (! as_list_col ) {
366
417
x = unnest(x , !! new_col , names_sep = names_sep )
367
418
}
419
+
420
+ # Remove any remaining phony observations. When `all_rows` is TRUE, phony
421
+ # observations aren't necessarily removed in `slide_one_grp`.
422
+ if (all_rows ) {
423
+ x <- x [x $ .real ,]
424
+ }
425
+
426
+ # Drop helper column `.real`.
427
+ x $ .real <- NULL
428
+
368
429
return (x )
369
430
}
0 commit comments