Skip to content

Commit 85503c1

Browse files
committed
WIP
1 parent 94f4efa commit 85503c1

File tree

6 files changed

+119
-39
lines changed

6 files changed

+119
-39
lines changed

NAMESPACE

+2
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ importFrom(rlang,env)
200200
importFrom(rlang,expr_label)
201201
importFrom(rlang,f_env)
202202
importFrom(rlang,f_rhs)
203+
importFrom(rlang,is_bare_integerish)
203204
importFrom(rlang,is_environment)
204205
importFrom(rlang,is_formula)
205206
importFrom(rlang,is_function)
@@ -233,3 +234,4 @@ importFrom(tidyselect,starts_with)
233234
importFrom(tsibble,as_tsibble)
234235
importFrom(utils,capture.output)
235236
importFrom(utils,tail)
237+
importFrom(vctrs,vec_data)

R/epiprocess-package.R

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#' @importFrom dplyr select
1818
#' @importFrom lifecycle deprecated
1919
#' @importFrom rlang %||%
20+
#' @importFrom rlang is_bare_integerish
21+
#' @importFrom vctrs vec_data
2022
## usethis namespace: end
2123
NULL
2224

R/slide.R

+46-35
Original file line numberDiff line numberDiff line change
@@ -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
@@ -692,6 +692,36 @@ epi_slide_opt <- function(
692692
}
693693
f_from_package <- f_info$package
694694

695+
user_provided_rtvs <- !is.null(.ref_time_values)
696+
if (!user_provided_rtvs) {
697+
.ref_time_values <- unique(.x$time_value)
698+
} else {
699+
assert_numeric(.ref_time_values, min.len = 1L, null.ok = FALSE, any.missing = FALSE)
700+
if (!test_subset(.ref_time_values, unique(.x$time_value))) {
701+
cli_abort(
702+
"`ref_time_values` must be a unique subset of the time values in `x`.",
703+
class = "epiprocess__epi_slide_opt_invalid_ref_time_values"
704+
)
705+
}
706+
if (anyDuplicated(.ref_time_values) != 0L) {
707+
cli_abort(
708+
"`ref_time_values` must not contain any duplicates; use `unique` if appropriate.",
709+
class = "epiprocess__epi_slide_opt_invalid_ref_time_values"
710+
)
711+
}
712+
}
713+
ref_time_values <- sort(.ref_time_values)
714+
715+
# Handle window arguments
716+
.align <- rlang::arg_match(.align)
717+
time_type <- attr(.x, "metadata")$time_type
718+
if (is.null(.window_size)) {
719+
cli_abort("epi_slide_opt: `.window_size` must be specified.")
720+
}
721+
validate_slide_window_arg(.window_size, time_type)
722+
window_args <- get_before_after_from_window(.window_size, .align, time_type)
723+
724+
# Handle output naming
695725
assert_string(.prefix, null.ok = TRUE)
696726
assert_string(.suffix, null.ok = TRUE)
697727
assert_character(.new_col_names, len = length(col_names_chr), null.ok = TRUE)
@@ -701,21 +731,31 @@ epi_slide_opt <- function(
701731
)
702732
}
703733
if (is.null(.prefix) && is.null(.suffix) && is.null(.new_col_names)) {
704-
.suffix <- "_{.window_size}{.time_unit}{.f_abbr}"
734+
.suffix <- "_{.n}{.time_unit_abbr}{.align_abbr}{.f_abbr}"
705735
}
706736
if (!is.null(.prefix) || !is.null(.suffix)) {
707737
.prefix <- .prefix %||% ""
708738
.suffix <- .suffix %||% ""
739+
if (identical(.window_size, Inf)) {
740+
n <- "running_"
741+
time_unit_abbr <- ""
742+
align_abbr <- ""
743+
} else {
744+
n <- time_delta_to_n_steps(.window_size, time_type)
745+
time_unit_abbr <- time_type_unit_abbr(time_type)
746+
align_abbr <- c(right = "", center = "c", left = "l")[[.align]]
747+
}
709748
glue_env <- rlang::env(
710-
.window_size = .window_size, # FIXME typing
711-
.time_unit = "d", # FIXME
749+
.n = n,
750+
.time_unit_abbr = time_unit_abbr,
751+
.align_abbr = align_abbr,
712752
.f_abbr = f_info$abbr,
713753
quo_get_env(col_names_quo)
714754
)
715755
.new_col_names <- unclass(
716756
glue(.prefix, .envir = glue_env) +
717-
col_names_chr +
718-
glue(.suffix, .envir = glue_env)
757+
col_names_chr +
758+
glue(.suffix, .envir = glue_env)
719759
)
720760
} else {
721761
# `.new_col_names` was provided by user; we don't need to do anything.
@@ -728,35 +768,6 @@ epi_slide_opt <- function(
728768
}
729769
result_col_names <- .new_col_names
730770

731-
user_provided_rtvs <- !is.null(.ref_time_values)
732-
if (!user_provided_rtvs) {
733-
.ref_time_values <- unique(.x$time_value)
734-
} else {
735-
assert_numeric(.ref_time_values, min.len = 1L, null.ok = FALSE, any.missing = FALSE)
736-
if (!test_subset(.ref_time_values, unique(.x$time_value))) {
737-
cli_abort(
738-
"`ref_time_values` must be a unique subset of the time values in `x`.",
739-
class = "epiprocess__epi_slide_opt_invalid_ref_time_values"
740-
)
741-
}
742-
if (anyDuplicated(.ref_time_values) != 0L) {
743-
cli_abort(
744-
"`ref_time_values` must not contain any duplicates; use `unique` if appropriate.",
745-
class = "epiprocess__epi_slide_opt_invalid_ref_time_values"
746-
)
747-
}
748-
}
749-
ref_time_values <- sort(.ref_time_values)
750-
751-
# Handle window arguments
752-
.align <- rlang::arg_match(.align)
753-
time_type <- attr(.x, "metadata")$time_type
754-
if (is.null(.window_size)) {
755-
cli_abort("epi_slide_opt: `.window_size` must be specified.")
756-
}
757-
validate_slide_window_arg(.window_size, time_type)
758-
window_args <- get_before_after_from_window(.window_size, .align, time_type)
759-
760771
# Make a complete date sequence between min(.x$time_value) and max(.x$time_value).
761772
date_seq_list <- full_date_seq(.x, window_args$before, window_args$after, time_type)
762773
all_dates <- date_seq_list$all_dates

R/utils.R

+66-1
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ guess_time_type <- function(time_value, time_value_arg = rlang::caller_arg(time_
640640
return("day")
641641
} else if (inherits(time_value, "yearmonth")) {
642642
return("yearmonth")
643-
} else if (rlang::is_integerish(time_value)) {
643+
} else if (is_bare_integerish(time_value)) {
644644
return("integer")
645645
}
646646

@@ -1109,3 +1109,68 @@ validate_slide_window_arg <- function(arg, time_type, lower = 1, allow_inf = TRU
11091109
)
11101110
}
11111111
}
1112+
1113+
1114+
#' Convert a time delta to a compatible integerish number of steps between time values
1115+
#'
1116+
#' @param time_delta a vector that can be added to time values of time type
1117+
#' `time_type` to arrive at other time values of that time type, or
1118+
#' `r lifecycle::badge("experimental")` such a vector with Inf/-Inf entries mixed
1119+
#' in, if supported by the class of `time_delta`, even if `time_type` doesn't
1120+
#' necessarily support Inf/-Inf entries. Basically a slide window arg but
1121+
#' without sign and length restrictions.
1122+
#' @param time_type as in [`validate_slide_window_arg`]
1123+
#' @return [bare integerish][rlang::is_integerish] vector (with possible
1124+
#' infinite values) that produces the same result as `time_delta` when added
1125+
#' to time values of time type `time_type`. If the given time type does not
1126+
#' support infinite values, then it should produce +Inf or -Inf for analogous
1127+
#' entries of `time_delta`, and match the addition result match the addition
1128+
#' result for non-infinite values, and product +Inf / -Inf when match the sign
1129+
#' and of `time_delta`.
1130+
#'
1131+
#' @keywords internal
1132+
time_delta_to_n_steps <- function(time_delta, time_type) {
1133+
# could be S3 if we're willing to export
1134+
if (inherits(time_delta, "difftime")) {
1135+
output_units <- switch(time_type,
1136+
day = "days",
1137+
week = "weeks",
1138+
cli_abort("difftime objects not supported for time_type {format_chr_with_quotes(time_type)}")
1139+
)
1140+
units(time_delta) <- output_units # converts number accordingly, doesn't just set attr
1141+
n_steps <- vec_data(time_delta)
1142+
if (!is_bare_integerish(n_steps)) {
1143+
cli_abort("`time_delta` did not appear to contain only integerish numbers
1144+
of steps between time values of time type {format_chr_with_quotes(time_type)}")
1145+
}
1146+
n_steps
1147+
} else if (is_bare_integerish(time_delta)) { # (allows infinite values)
1148+
switch(time_type,
1149+
day = ,
1150+
week = ,
1151+
yearmonth = ,
1152+
integer = time_delta,
1153+
cli_abort("Invalid or unsupported time_type {format_chr_with_quotes(time_type)}")
1154+
)
1155+
} else {
1156+
cli_abort("Invalid or unsupported kind of `time_delta`")
1157+
}
1158+
}
1159+
1160+
# Using these unit abbreviations happens to make our automatic slide output
1161+
# naming look like taking ISO-8601 duration designations, removing the P, and
1162+
# lowercasing any characters. Fortnightly or sub-daily time types would need an
1163+
# adjustment to remain consistent.
1164+
time_type_unit_abbrs <- c(
1165+
day = "d",
1166+
week = "w",
1167+
yearmon = "m"
1168+
)
1169+
1170+
time_type_unit_abbr <- function(time_type) {
1171+
maybe_unit_abbr <- time_type_unit_abbrs[time_type]
1172+
if (is.na(maybe_unit_abbr)) {
1173+
cli_abort("Cannot determine the units of time type {format_chr_with_quotes(time_type)}")
1174+
}
1175+
maybe_unit_abbr
1176+
}

man/epi_slide.Rd

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/epi_slide_opt.Rd

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)