@@ -537,7 +537,7 @@ get_before_after_from_window <- function(window_size, align, time_type) {
537537# '
538538# ' @template basic-slide-params
539539# ' @param .col_names <[`tidy-select`][dplyr_tidy_select]> An unquoted column
540- # ' name(e.g., `cases`), multiple column names (e.g., `c(cases, deaths)`),
540+ # ' name (e.g., `cases`), multiple column names (e.g., `c(cases, deaths)`),
541541# ' [other tidy-select expression][tidyselect::language], or a vector of
542542# ' characters (e.g. `c("cases", "deaths")`). Variable names can be used as if
543543# ' they were positions in the data frame, so expressions like `x:y` can be
@@ -564,8 +564,9 @@ get_before_after_from_window <- function(window_size, align, time_type) {
564564# ' functions).
565565# '
566566# ' @importFrom dplyr bind_rows mutate %>% arrange tibble select all_of
567- # ' @importFrom rlang enquo expr_label caller_arg
567+ # ' @importFrom rlang enquo expr_label caller_arg quo_get_env
568568# ' @importFrom tidyselect eval_select
569+ # ' @importFrom glue glue
569570# ' @importFrom purrr map map_lgl
570571# ' @importFrom data.table frollmean frollsum frollapply
571572# ' @importFrom lubridate as.period
@@ -577,8 +578,7 @@ get_before_after_from_window <- function(window_size, align, time_type) {
577578# ' # Compute a 7-day trailing average on cases.
578579# ' cases_deaths_subset %>%
579580# ' group_by(geo_value) %>%
580- # ' epi_slide_opt(cases, .f = data.table::frollmean, .window_size = 7) %>%
581- # ' dplyr::select(geo_value, time_value, cases, cases_7dav = slide_value_cases)
581+ # ' epi_slide_opt(cases, .f = data.table::frollmean, .window_size = 7)
582582# '
583583# ' # Same as above, but adjust `frollmean` settings for speed, accuracy, and
584584# ' # to allow partially-missing windows.
@@ -588,11 +588,11 @@ get_before_after_from_window <- function(window_size, align, time_type) {
588588# ' cases,
589589# ' .f = data.table::frollmean, .window_size = 7,
590590# ' algo = "exact", hasNA = TRUE, na.rm = TRUE
591- # ' ) %>%
592- # ' dplyr::select(geo_value, time_value, cases, cases_7dav = slide_value_cases)
591+ # ' )
593592epi_slide_opt <- function (
594593 .x , .col_names , .f , ... ,
595594 .window_size = NULL , .align = c(" right" , " center" , " left" ),
595+ .prefix = NULL , .suffix = NULL , .new_col_names = NULL ,
596596 .ref_time_values = NULL , .all_rows = FALSE ) {
597597 assert_class(.x , " epi_df" )
598598
@@ -644,21 +644,37 @@ epi_slide_opt <- function(
644644 )
645645 }
646646
647+ # The position of a given column can be differ between input `.x` and
648+ # `.data_group` since the grouping step by default drops grouping columns.
649+ # To avoid rerunning `eval_select` for every `.data_group`, convert
650+ # positions of user-provided `col_names` into string column names. We avoid
651+ # using `names(pos)` directly for robustness and in case we later want to
652+ # allow users to rename fields via tidyselection.
653+ col_names_quo <- enquo(.col_names )
654+ pos <- eval_select(col_names_quo , data = .x , allow_rename = FALSE )
655+ col_names_chr <- names(.x )[pos ]
656+
647657 # Check that slide function `.f` is one of those short-listed from
648658 # `data.table` and `slider` (or a function that has the exact same
649659 # definition, e.g. if the function has been reexported or defined
650660 # locally).
651- if (any(map_lgl(
652- list (frollmean , frollsum , frollapply ),
653- ~ identical(.f , .x )
654- ))) {
655- f_from_package <- " data.table"
656- } else if (any(map_lgl(
657- list (slide_sum , slide_prod , slide_mean , slide_min , slide_max , slide_all , slide_any ),
658- ~ identical(.f , .x )
659- ))) {
660- f_from_package <- " slider"
661- } else {
661+ f_possibilities <-
662+ tibble :: tribble(
663+ ~ f , ~ package , ~ abbr ,
664+ frollmean , " data.table" , " av" ,
665+ frollsum , " data.table" , " sum" ,
666+ frollapply , " data.table" , " slide" ,
667+ slide_sum , " slider" , " sum" ,
668+ slide_prod , " slider" , " prod" ,
669+ slide_mean , " slider" , " av" ,
670+ slide_min , " slider" , " min" ,
671+ slide_max , " slider" , " max" ,
672+ slide_all , " slider" , " all" ,
673+ slide_any , " slider" , " any" ,
674+ )
675+ f_info <- f_possibilities %> %
676+ filter(map_lgl(.data $ f , ~ identical(.f , .x )))
677+ if (nrow(f_info ) == 0L ) {
662678 # `f` is from somewhere else and not supported
663679 cli_abort(
664680 c(
@@ -672,6 +688,7 @@ epi_slide_opt <- function(
672688 epiprocess__f = .f
673689 )
674690 }
691+ f_from_package <- f_info $ package
675692
676693 user_provided_rtvs <- ! is.null(.ref_time_values )
677694 if (! user_provided_rtvs ) {
@@ -702,22 +719,59 @@ epi_slide_opt <- function(
702719 validate_slide_window_arg(.window_size , time_type )
703720 window_args <- get_before_after_from_window(.window_size , .align , time_type )
704721
722+ # Handle output naming
723+ assert_string(.prefix , null.ok = TRUE )
724+ assert_string(.suffix , null.ok = TRUE )
725+ assert_character(.new_col_names , len = length(col_names_chr ), null.ok = TRUE )
726+ if ((! is.null(.prefix ) || ! is.null(.suffix )) && ! is.null(.new_col_names )) {
727+ cli_abort(
728+ " Can't use both .prefix/.suffix and .new_col_names at the same time."
729+ )
730+ }
731+ if (is.null(.prefix ) && is.null(.suffix ) && is.null(.new_col_names )) {
732+ .suffix <- " _{.n}{.time_unit_abbr}{.align_abbr}{.f_abbr}"
733+ }
734+ if (! is.null(.prefix ) || ! is.null(.suffix )) {
735+ .prefix <- .prefix %|| % " "
736+ .suffix <- .suffix %|| % " "
737+ if (identical(.window_size , Inf )) {
738+ n <- " running_"
739+ time_unit_abbr <- " "
740+ align_abbr <- " "
741+ } else {
742+ n <- time_delta_to_n_steps(.window_size , time_type )
743+ time_unit_abbr <- time_type_unit_abbr(time_type )
744+ align_abbr <- c(right = " " , center = " c" , left = " l" )[[.align ]]
745+ }
746+ glue_env <- rlang :: env(
747+ .n = n ,
748+ .time_unit_abbr = time_unit_abbr ,
749+ .align_abbr = align_abbr ,
750+ .f_abbr = f_info $ abbr ,
751+ quo_get_env(col_names_quo )
752+ )
753+ .new_col_names <- unclass(
754+ glue(.prefix , .envir = glue_env ) +
755+ col_names_chr +
756+ glue(.suffix , .envir = glue_env )
757+ )
758+ } else {
759+ # `.new_col_names` was provided by user; we don't need to do anything.
760+ }
761+ if (any(.new_col_names %in% names(.x ))) {
762+ cli_abort(c(
763+ " Naming conflict between new columns and existing columns" ,
764+ " x" = " Overlapping names: {format_varnames(intersect(.new_col_names, names(.x)))}"
765+ ))
766+ }
767+ result_col_names <- .new_col_names
768+
705769 # Make a complete date sequence between min(.x$time_value) and max(.x$time_value).
706770 date_seq_list <- full_date_seq(.x , window_args $ before , window_args $ after , time_type )
707771 all_dates <- date_seq_list $ all_dates
708772 pad_early_dates <- date_seq_list $ pad_early_dates
709773 pad_late_dates <- date_seq_list $ pad_late_dates
710774
711- # The position of a given column can be differ between input `.x` and
712- # `.data_group` since the grouping step by default drops grouping columns.
713- # To avoid rerunning `eval_select` for every `.data_group`, convert
714- # positions of user-provided `col_names` into string column names. We avoid
715- # using `names(pos)` directly for robustness and in case we later want to
716- # allow users to rename fields via tidyselection.
717- pos <- eval_select(enquo(.col_names ), data = .x , allow_rename = FALSE )
718- col_names_chr <- names(.x )[pos ]
719- # Always rename results to "slide_value_<original column name>".
720- result_col_names <- paste0(" slide_value_" , col_names_chr )
721775 slide_one_grp <- function (.data_group , .group_key , ... ) {
722776 missing_times <- all_dates [! (all_dates %in% .data_group $ time_value )]
723777 # `frollmean` requires a full window to compute a result. Add NA values
@@ -843,6 +897,7 @@ epi_slide_opt <- function(
843897epi_slide_mean <- function (
844898 .x , .col_names , ... ,
845899 .window_size = NULL , .align = c(" right" , " center" , " left" ),
900+ .prefix = NULL , .suffix = NULL , .new_col_names = NULL ,
846901 .ref_time_values = NULL , .all_rows = FALSE ) {
847902 # Deprecated argument handling
848903 provided_args <- rlang :: call_args_names(rlang :: call_match())
@@ -885,6 +940,9 @@ epi_slide_mean <- function(
885940 ... ,
886941 .window_size = .window_size ,
887942 .align = .align ,
943+ .prefix = .prefix ,
944+ .suffix = .suffix ,
945+ .new_col_names = .new_col_names ,
888946 .ref_time_values = .ref_time_values ,
889947 .all_rows = .all_rows
890948 )
@@ -904,6 +962,7 @@ epi_slide_mean <- function(
904962epi_slide_sum <- function (
905963 .x , .col_names , ... ,
906964 .window_size = NULL , .align = c(" right" , " center" , " left" ),
965+ .prefix = NULL , .suffix = NULL , .new_col_names = NULL ,
907966 .ref_time_values = NULL , .all_rows = FALSE ) {
908967 # Deprecated argument handling
909968 provided_args <- rlang :: call_args_names(rlang :: call_match())
@@ -945,6 +1004,9 @@ epi_slide_sum <- function(
9451004 ... ,
9461005 .window_size = .window_size ,
9471006 .align = .align ,
1007+ .prefix = .prefix ,
1008+ .suffix = .suffix ,
1009+ .new_col_names = .new_col_names ,
9481010 .ref_time_values = .ref_time_values ,
9491011 .all_rows = .all_rows
9501012 )
0 commit comments