From 9ede25a053c476591d92f70d9c4ea9cd94aa7354 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Mon, 27 Nov 2023 15:38:27 -0800 Subject: [PATCH 01/27] add S3 method to quickly access the keys in an epi_df or epi_archive --- R/epi_keys.R | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 R/epi_keys.R diff --git a/R/epi_keys.R b/R/epi_keys.R new file mode 100644 index 00000000..324b4eff --- /dev/null +++ b/R/epi_keys.R @@ -0,0 +1,42 @@ +#' Grab any keys associated to an epi_df +#' +#' @param x a data.frame, tibble, or epi_df +#' @param ... additional arguments passed on to methods +#' +#' @return If an `epi_df`, this returns all "keys". Otherwise `NULL` +#' @keywords internal +#' @export +epi_keys <- function(x, ...) { + UseMethod("epi_keys") +} + +#' @export +epi_keys.default <- function(x, ...) { + character(0L) +} + +#' @export +epi_keys.data.frame <- function(x, other_keys = character(0L), ...) { + arg_is_chr(other_keys, allow_empty = TRUE) + nm <- c("time_value", "geo_value", other_keys) + intersect(nm, names(x)) +} + +#' @export +epi_keys.epi_df <- function(x, ...) { + c("time_value", "geo_value", attr(x, "metadata")$other_keys) +} + +#' @export +epi_keys.epi_archive <- function(x, ...) { + c("time_value", "geo_value", attr(x, "metadata")$other_keys) +} + +kill_time_value <- function(v) { + arg_is_chr(v) + v[v != "time_value"] +} + +epi_keys_only <- function(x, ...) { + kill_time_value(epi_keys(x, ...)) +} From 96c7500901047fe0598e2e100d3867207bc0481f Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Mon, 27 Nov 2023 15:39:04 -0800 Subject: [PATCH 02/27] grab autoplot method from ggplot2 and export --- DESCRIPTION | 5 ++++- R/reexports.R | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index d9a8dea6..7895ee26 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -33,6 +33,7 @@ Imports: feasts, generics, genlasso, + ggplot2, lifecycle (>= 1.0.1), lubridate, magrittr, @@ -49,7 +50,6 @@ Imports: Suggests: covidcast, epidatr, - ggplot2, knitr, outbreaks, rmarkdown, @@ -72,9 +72,11 @@ Depends: URL: https://cmu-delphi.github.io/epiprocess/ Collate: 'archive.R' + 'autoplot.R' 'correlation.R' 'data.R' 'epi_df.R' + 'epi_keys.R' 'epiprocess.R' 'methods-epi_archive.R' 'grouped_epi_archive.R' @@ -83,5 +85,6 @@ Collate: 'outliers.R' 'reexports.R' 'slide.R' + 'utils-arg.R' 'utils.R' 'utils_pipe.R' diff --git a/R/reexports.R b/R/reexports.R index 4cc45e29..02f5af53 100644 --- a/R/reexports.R +++ b/R/reexports.R @@ -55,3 +55,10 @@ dplyr::slice #' @importFrom tidyr unnest #' @export tidyr::unnest + + +# ggplot2 ----------------------------------------------------------------- + +#' @importFrom ggplot2 autoplot +#' @export +ggplot2::autoplot From 1638430040896f8366e952732c7a3f586393de5b Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Mon, 27 Nov 2023 15:39:30 -0800 Subject: [PATCH 03/27] simplify enlist() --- R/utils.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/R/utils.R b/R/utils.R index 471fb053..c815161a 100644 --- a/R/utils.R +++ b/R/utils.R @@ -454,13 +454,13 @@ quiet = function(x) { # Create an auto-named list enlist = function(...) { - x = list(...) - n = as.character(sys.call())[-1] - if (!is.null(n0 <- names(x))) { - n[n0 != ""] = n0[n0 != ""] - } - names(x) = n - return(x) + # converted to thin wrapper around + rlang::dots_list( + ..., + .homonyms = "error", + .named = TRUE, + .check_assign = TRUE + ) } # Variable assignment from a list. NOT USED. Something is broken, this doesn't From a3cb78bb1e3ac68284fdd2028d0cf7bb137e28de Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Mon, 27 Nov 2023 15:41:28 -0800 Subject: [PATCH 04/27] add a few argument checking functions, see also #380 --- R/utils-arg.R | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 R/utils-arg.R diff --git a/R/utils-arg.R b/R/utils-arg.R new file mode 100644 index 00000000..b5700b6d --- /dev/null +++ b/R/utils-arg.R @@ -0,0 +1,63 @@ +handle_arg_list <- function(..., tests) { + values <- list(...) + names <- eval(substitute(alist(...))) + names <- purrr::map(names, deparse) + + purrr::walk2(names, values, tests) +} + +arg_is_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { + handle_arg_list( + ..., + tests = function(name, value) { + if (length(value) > 1 | (!allow_null & length(value) == 0)) { + cli::cli_abort("Argument {.val {name}} must be of length 1.") + } + if (!is.null(value)) { + if (is.na(value) & !allow_na) { + cli::cli_abort( + "Argument {.val {name}} must not be a missing value ({.val {NA}})." + ) + } + } + } + ) +} + +arg_is_int <- function(..., allow_null = FALSE) { + arg_is_numeric(..., allow_null = allow_null) + handle_arg_list( + ..., + tests = function(name, value) { + if (!(all(value %% 1 == 0) | (is.null(value) & allow_null))) { + cli::cli_abort("All {.val {name}} must be whole positive number(s).") + } + } + ) +} + +arg_is_chr <- function(..., allow_null = FALSE, allow_na = FALSE, allow_empty = FALSE) { + handle_arg_list( + ..., + tests = function(name, value) { + if (is.null(value) & !allow_null) { + cli::cli_abort("Argument {.val {name}} may not be `NULL`.") + } + if (any(is.na(value)) & !allow_na) { + cli::cli_abort("Argument {.val {name}} must not contain any missing values ({.val {NA}}).") + } + if (!is.null(value) & (length(value) == 0L & !allow_empty)) { + cli::cli_abort("Argument {.val {name}} must have length > 0.") + } + if (!(is.character(value) | is.null(value) | all(is.na(value)))) { + cli::cli_abort("Argument {.val {name}} must be of character type.") + } + } + ) +} + +arg_is_chr_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { + arg_is_chr(..., allow_null = allow_null, allow_na = allow_na) + arg_is_scalar(..., allow_null = allow_null, allow_na = allow_na) +} + From f29936cf2df067de117f62fd4b2d36d80544f7be Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Mon, 27 Nov 2023 15:41:56 -0800 Subject: [PATCH 05/27] add autoplot() for epi_df's --- R/autoplot.R | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 R/autoplot.R diff --git a/R/autoplot.R b/R/autoplot.R new file mode 100644 index 00000000..8263818d --- /dev/null +++ b/R/autoplot.R @@ -0,0 +1,154 @@ +#' Automatically plot an epi_df +#' +#' @param object An `epi_df` +#' @param ... <[`tidy-select`][dplyr_tidy_select]> One or more unquoted +#' expressions separated by commas. Variable names can be used as if they +#' were positions in the data frame, so expressions like `x:y` can +#' be used to select a range of variables. +#' @param .color_by Which variables should determine the color(s) used to plot +#' lines. Options include: +#' * `all_keys` - the default uses the interaction of any key variables +#' including the `geo_value` +#' * `geo_value` - `geo_value` only +#' * `other_keys` - any available keys that are not `geo_value` +#' * `.response` - the numeric variables (same as the y-axis) +#' * `all` - uses the interaction of all keys and numeric variables +#' * `none` - no coloring aesthetic is applied +#' @param .facet_by Similar to `.color_by` except that the default is to display +#' each numeric variable on a separate facet +#' @param .base_color Lines will be shown with this color. For example, with a +#' single numeric variable and faceting by `geo_value`, all locations would +#' share the same color line. +#' @param .max_facets Cut down of the number of facets displayed. Especially +#' useful for testing when there are many `geo_value`'s or keys. +#' +#' @return +#' @export +#' +#' @examples +#' autoplot(jhu_csse_daily_subset, cases, death_rate_7d_av) +#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .facet_by = "geo_value") +#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", +#' .facet_by = "geo_value") +autoplot.epi_df <- function( + object, ..., + .color_by = c("all_keys", "geo_value", "other_keys", ".response", "all", "none"), + .facet_by = c(".response", "other_keys", "all_keys", "geo_value", "all", "none"), + .base_color = "#3A448F", + .max_facets = Inf) { + .color_by <- match.arg(.color_by) + .facet_by <- match.arg(.facet_by) + + arg_is_scalar(.max_facets) + if (is.finite(.max_facets)) arg_is_int(.max_facets) + arg_is_chr_scalar(.base_color) + + ek <- epi_keys(object) + mv <- setdiff(names(object), ek) + ek <- kill_time_value(ek) + + # --- check for numeric variables + allowed <- purrr::map_lgl(object[mv], is.numeric) + if (length(allowed) == 0) { + cli::cli_abort("No numeric variables were available to plot automatically.") + } + vars <- tidyselect::eval_select(rlang::expr(c(...)), object) + if (rlang::is_empty(vars)) { # find them automatically if unspecified + vars <- tidyselect::eval_select(names(allowed)[1], object) + cli::cli_warn( + "Plot variable was unspecified. Automatically selecting {.var {names(allowed)[1]}}." + ) + } else { # if variables were specified, ensure that they are numeric + ok <- names(vars) %in% names(allowed) + if (!any(ok)) { + cli::cli_abort( + "None of the requested variables {.var {names(vars)}} are numeric." + ) + } else if (!all(ok)) { + cli::cli_warn(c( + "Only the requested variables {.var {names(vars)[ok]}} are numeric.", + i = "`autoplot()` cannot display {.var {names(vars)[!ok]}}." + )) + vars <- vars[ok] + } + } + + # --- create a viable df to plot + pos <- tidyselect::eval_select( + rlang::expr(c("time_value", ek, names(vars))), object + ) + if (length(vars) > 1) { + object <- tidyr::pivot_longer( + object[pos], tidyselect::all_of(names(vars)), + values_to = ".response", + names_to = ".response_name" + ) + } else { + object <- dplyr::rename(object[pos], .response := !!names(vars)) + } + all_keys <- rlang::syms(as.list(ek)) + other_keys <- rlang::syms(as.list(setdiff(ek, "geo_value"))) + all_avail <- rlang::syms(as.list(c(ek, ".response_name"))) + + object <- object %>% + dplyr::mutate( + .colours = switch(.color_by, + all_keys = interaction(!!!all_keys, sep = "/"), + geo_value = geo_value, + other_keys = interaction(!!!other_keys, sep = "/"), + all = interaction(!!!all_avail, sep = "/"), + NULL + ), + .facets = switch(.facet_by, + all_keys = interaction(!!!all_keys, sep = "/"), + geo_value = as.factor(geo_value), + other_keys = interaction(!!!other_keys, sep = "/"), + all = interaction(!!!all_avail, sep = "/"), + NULL + ) + ) + + if (.max_facets < Inf && ".facets" %in% names(object)) { + n_facets <- nlevels(object$.facets) + if (n_facets > .max_facets) { + top_n <- levels(as.factor(object$.facets))[seq_len(.max_facets)] + object <- dplyr::filter(object, .facets %in% top_n) %>% + dplyr::mutate(.facets = droplevels(.facets)) + if (".colours" %in% names(object)) { + object <- dplyr::mutate(object, .colours = droplevels(.colours)) + } + } + } + + p <- ggplot2::ggplot(object, ggplot2::aes(x = .data$time_value)) + + ggplot2::theme_bw() + + if (".colours" %in% names(object)) { + p <- p + ggplot2::geom_line( + ggplot2::aes(y = .data$.response, colour = .data$.colours), + key_glyph = "timeseries" + ) + + ggplot2::scale_colour_viridis_d(name = "") + } else if (length(vars) > 1 && .color_by == ".response") { + p <- p + + ggplot2::geom_line(ggplot2::aes( + y = .data$.response, colour = .data$.response_name + )) + + ggplot2::scale_colour_viridis_d(name = "") + } else { # none + p <- p + + ggplot2::geom_line(ggplot2::aes(y = .data$.response), color = .base_color) + } + + if (".facets" %in% names(object)) { + p <- p + ggplot2::facet_wrap(~.facets, scales = "free_y") + + ggplot2::ylab(names(vars)) + if (.facet_by == "all") p <- p + ggplot2::ylab("") + } else if ((length(vars) > 1 && .facet_by == ".response")) { + p <- p + ggplot2::facet_wrap(~.response_name, scales = "free_y") + + ggplot2::ylab("") + } else { + p <- p + ggplot2::ylab(names(vars)) + } + p +} From cc58ddc1d935374a26cd18ce899f76dd14151472 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Mon, 27 Nov 2023 15:42:11 -0800 Subject: [PATCH 06/27] document() --- NAMESPACE | 8 +++++++ man/autoplot.epi_df.Rd | 54 ++++++++++++++++++++++++++++++++++++++++++ man/epi_keys.Rd | 20 ++++++++++++++++ man/reexports.Rd | 3 +++ 4 files changed, 85 insertions(+) create mode 100644 man/autoplot.epi_df.Rd create mode 100644 man/epi_keys.Rd diff --git a/NAMESPACE b/NAMESPACE index 4f9b8151..eb88c7a4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,10 +8,15 @@ S3method(as_epi_df,tbl_df) S3method(as_epi_df,tbl_ts) S3method(as_tibble,epi_df) S3method(as_tsibble,epi_df) +S3method(autoplot,epi_df) S3method(dplyr_col_modify,col_modify_recorder_df) S3method(dplyr_col_modify,epi_df) S3method(dplyr_reconstruct,epi_df) S3method(dplyr_row_slice,epi_df) +S3method(epi_keys,data.frame) +S3method(epi_keys,default) +S3method(epi_keys,epi_archive) +S3method(epi_keys,epi_df) S3method(epix_truncate_versions_after,epi_archive) S3method(epix_truncate_versions_after,grouped_epi_archive) S3method(group_by,epi_archive) @@ -33,11 +38,13 @@ export(arrange) export(as_epi_archive) export(as_epi_df) export(as_tsibble) +export(autoplot) export(detect_outlr) export(detect_outlr_rm) export(detect_outlr_stl) export(epi_archive) export(epi_cor) +export(epi_keys) export(epi_slide) export(epix_as_of) export(epix_merge) @@ -85,6 +92,7 @@ importFrom(dplyr,rename) importFrom(dplyr,select) importFrom(dplyr,slice) importFrom(dplyr,ungroup) +importFrom(ggplot2,autoplot) importFrom(lubridate,days) importFrom(lubridate,weeks) importFrom(magrittr,"%>%") diff --git a/man/autoplot.epi_df.Rd b/man/autoplot.epi_df.Rd new file mode 100644 index 00000000..134a83fe --- /dev/null +++ b/man/autoplot.epi_df.Rd @@ -0,0 +1,54 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/autoplot.R +\name{autoplot.epi_df} +\alias{autoplot.epi_df} +\title{Automatically plot an epi_df} +\usage{ +\method{autoplot}{epi_df}( + object, + ..., + .color_by = c("all_keys", "geo_value", "other_keys", ".response", "all", "none"), + .facet_by = c(".response", "other_keys", "all_keys", "geo_value", "all", "none"), + .base_color = "#3A448F", + .max_facets = Inf +) +} +\arguments{ +\item{object}{An \code{epi_df}} + +\item{...}{<\code{\link[=dplyr_tidy_select]{tidy-select}}> One or more unquoted +expressions separated by commas. Variable names can be used as if they +were positions in the data frame, so expressions like \code{x:y} can +be used to select a range of variables.} + +\item{.color_by}{Which variables should determine the color(s) used to plot +lines. Options include: +\itemize{ +\item \code{all_keys} - the default uses the interaction of any key variables +including the \code{geo_value} +\item \code{geo_value} - \code{geo_value} only +\item \code{other_keys} - any available keys that are not \code{geo_value} +\item \code{.response} - the numeric variables (same as the y-axis) +\item \code{all} - uses the interaction of all keys and numeric variables +\item \code{none} - no coloring aesthetic is applied +}} + +\item{.facet_by}{Similar to \code{.color_by} except that the default is to display +each numeric variable on a separate facet} + +\item{.base_color}{Lines will be shown with this color. For example, with a +single numeric variable and faceting by \code{geo_value}, all locations would +share the same color line.} + +\item{.max_facets}{Cut down of the number of facets displayed. Especially +useful for testing when there are many \code{geo_value}'s or keys.} +} +\description{ +Automatically plot an epi_df +} +\examples{ +autoplot(jhu_csse_daily_subset, cases, death_rate_7d_av) +autoplot(jhu_csse_daily_subset, case_rate_7d_av, .facet_by = "geo_value") +autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", + .facet_by = "geo_value") +} diff --git a/man/epi_keys.Rd b/man/epi_keys.Rd new file mode 100644 index 00000000..8026fc14 --- /dev/null +++ b/man/epi_keys.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/epi_keys.R +\name{epi_keys} +\alias{epi_keys} +\title{Grab any keys associated to an epi_df} +\usage{ +epi_keys(x, ...) +} +\arguments{ +\item{x}{a data.frame, tibble, or epi_df} + +\item{...}{additional arguments passed on to methods} +} +\value{ +If an \code{epi_df}, this returns all "keys". Otherwise \code{NULL} +} +\description{ +Grab any keys associated to an epi_df +} +\keyword{internal} diff --git a/man/reexports.Rd b/man/reexports.Rd index 46e961d9..fdda2925 100644 --- a/man/reexports.Rd +++ b/man/reexports.Rd @@ -14,6 +14,7 @@ \alias{rename} \alias{slice} \alias{unnest} +\alias{autoplot} \title{Objects exported from other packages} \keyword{internal} \description{ @@ -23,6 +24,8 @@ below to see their documentation. \describe{ \item{dplyr}{\code{\link[dplyr]{arrange}}, \code{\link[dplyr]{filter}}, \code{\link[dplyr]{group_by}}, \code{\link[dplyr:group_map]{group_modify}}, \code{\link[dplyr]{mutate}}, \code{\link[dplyr]{relocate}}, \code{\link[dplyr]{rename}}, \code{\link[dplyr]{slice}}, \code{\link[dplyr:group_by]{ungroup}}} + \item{ggplot2}{\code{\link[ggplot2]{autoplot}}} + \item{tidyr}{\code{\link[tidyr]{unnest}}} \item{tsibble}{\code{\link[tsibble:as-tsibble]{as_tsibble}}} From d29b79fdf348ef09f480ab575cef4d39857f12af Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Mon, 27 Nov 2023 15:59:35 -0800 Subject: [PATCH 07/27] add new functions to pkgdown --- _pkgdown.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index bba3ea8d..00110b01 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -66,10 +66,13 @@ reference: - archive_cases_dv_subset - incidence_num_outlier_example - contains("jhu_csse") +- title: Basic automatic plotting + contents: + - autoplot.epi_df - title: internal contents: - epiprocess - max_version_with_row_in - next_after - guess_period - + - epi_keys From becc554277869496bf600bb9e332b1c20cc9cc8d Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Fri, 19 Jan 2024 11:43:15 -0800 Subject: [PATCH 08/27] style: run styler --- R/autoplot.R | 44 +++++++++++++++++++++++--------------------- R/utils-arg.R | 3 +-- R/utils.R | 2 +- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/R/autoplot.R b/R/autoplot.R index 8263818d..6c6bf5eb 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -28,8 +28,10 @@ #' @examples #' autoplot(jhu_csse_daily_subset, cases, death_rate_7d_av) #' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .facet_by = "geo_value") -#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", -#' .facet_by = "geo_value") +#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, +#' .color_by = "none", +#' .facet_by = "geo_value" +#' ) autoplot.epi_df <- function( object, ..., .color_by = c("all_keys", "geo_value", "other_keys", ".response", "all", "none"), @@ -38,15 +40,15 @@ autoplot.epi_df <- function( .max_facets = Inf) { .color_by <- match.arg(.color_by) .facet_by <- match.arg(.facet_by) - + arg_is_scalar(.max_facets) if (is.finite(.max_facets)) arg_is_int(.max_facets) arg_is_chr_scalar(.base_color) - + ek <- epi_keys(object) mv <- setdiff(names(object), ek) ek <- kill_time_value(ek) - + # --- check for numeric variables allowed <- purrr::map_lgl(object[mv], is.numeric) if (length(allowed) == 0) { @@ -72,7 +74,7 @@ autoplot.epi_df <- function( vars <- vars[ok] } } - + # --- create a viable df to plot pos <- tidyselect::eval_select( rlang::expr(c("time_value", ek, names(vars))), object @@ -89,25 +91,25 @@ autoplot.epi_df <- function( all_keys <- rlang::syms(as.list(ek)) other_keys <- rlang::syms(as.list(setdiff(ek, "geo_value"))) all_avail <- rlang::syms(as.list(c(ek, ".response_name"))) - + object <- object %>% dplyr::mutate( .colours = switch(.color_by, - all_keys = interaction(!!!all_keys, sep = "/"), - geo_value = geo_value, - other_keys = interaction(!!!other_keys, sep = "/"), - all = interaction(!!!all_avail, sep = "/"), - NULL + all_keys = interaction(!!!all_keys, sep = "/"), + geo_value = geo_value, + other_keys = interaction(!!!other_keys, sep = "/"), + all = interaction(!!!all_avail, sep = "/"), + NULL ), .facets = switch(.facet_by, - all_keys = interaction(!!!all_keys, sep = "/"), - geo_value = as.factor(geo_value), - other_keys = interaction(!!!other_keys, sep = "/"), - all = interaction(!!!all_avail, sep = "/"), - NULL + all_keys = interaction(!!!all_keys, sep = "/"), + geo_value = as.factor(geo_value), + other_keys = interaction(!!!other_keys, sep = "/"), + all = interaction(!!!all_avail, sep = "/"), + NULL ) ) - + if (.max_facets < Inf && ".facets" %in% names(object)) { n_facets <- nlevels(object$.facets) if (n_facets > .max_facets) { @@ -119,10 +121,10 @@ autoplot.epi_df <- function( } } } - + p <- ggplot2::ggplot(object, ggplot2::aes(x = .data$time_value)) + ggplot2::theme_bw() - + if (".colours" %in% names(object)) { p <- p + ggplot2::geom_line( ggplot2::aes(y = .data$.response, colour = .data$.colours), @@ -139,7 +141,7 @@ autoplot.epi_df <- function( p <- p + ggplot2::geom_line(ggplot2::aes(y = .data$.response), color = .base_color) } - + if (".facets" %in% names(object)) { p <- p + ggplot2::facet_wrap(~.facets, scales = "free_y") + ggplot2::ylab(names(vars)) diff --git a/R/utils-arg.R b/R/utils-arg.R index b5700b6d..dca21646 100644 --- a/R/utils-arg.R +++ b/R/utils-arg.R @@ -2,7 +2,7 @@ handle_arg_list <- function(..., tests) { values <- list(...) names <- eval(substitute(alist(...))) names <- purrr::map(names, deparse) - + purrr::walk2(names, values, tests) } @@ -60,4 +60,3 @@ arg_is_chr_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { arg_is_chr(..., allow_null = allow_null, allow_na = allow_na) arg_is_scalar(..., allow_null = allow_null, allow_na = allow_na) } - diff --git a/R/utils.R b/R/utils.R index 787da5bd..f0a586d7 100644 --- a/R/utils.R +++ b/R/utils.R @@ -481,7 +481,7 @@ quiet <- function(x) { ########## # Create an auto-named list -enlist = function(...) { +enlist <- function(...) { # converted to thin wrapper around rlang::dots_list( ..., From ede6c9c92440eb2c3f9659a3f112880e8807e482 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Fri, 19 Jan 2024 11:56:01 -0800 Subject: [PATCH 09/27] rename epi_keys to key_colnames --- R/epi_keys.R | 42 ------------------------------------------ R/key_colnames.R | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 42 deletions(-) delete mode 100644 R/epi_keys.R create mode 100644 R/key_colnames.R diff --git a/R/epi_keys.R b/R/epi_keys.R deleted file mode 100644 index 324b4eff..00000000 --- a/R/epi_keys.R +++ /dev/null @@ -1,42 +0,0 @@ -#' Grab any keys associated to an epi_df -#' -#' @param x a data.frame, tibble, or epi_df -#' @param ... additional arguments passed on to methods -#' -#' @return If an `epi_df`, this returns all "keys". Otherwise `NULL` -#' @keywords internal -#' @export -epi_keys <- function(x, ...) { - UseMethod("epi_keys") -} - -#' @export -epi_keys.default <- function(x, ...) { - character(0L) -} - -#' @export -epi_keys.data.frame <- function(x, other_keys = character(0L), ...) { - arg_is_chr(other_keys, allow_empty = TRUE) - nm <- c("time_value", "geo_value", other_keys) - intersect(nm, names(x)) -} - -#' @export -epi_keys.epi_df <- function(x, ...) { - c("time_value", "geo_value", attr(x, "metadata")$other_keys) -} - -#' @export -epi_keys.epi_archive <- function(x, ...) { - c("time_value", "geo_value", attr(x, "metadata")$other_keys) -} - -kill_time_value <- function(v) { - arg_is_chr(v) - v[v != "time_value"] -} - -epi_keys_only <- function(x, ...) { - kill_time_value(epi_keys(x, ...)) -} diff --git a/R/key_colnames.R b/R/key_colnames.R new file mode 100644 index 00000000..91be7ab6 --- /dev/null +++ b/R/key_colnames.R @@ -0,0 +1,44 @@ +#' Grab any keys associated to an epi_df +#' +#' @param x a data.frame, tibble, or epi_df +#' @param ... additional arguments passed on to methods +#' +#' @return If an `epi_df`, this returns all "keys". Otherwise `NULL` +#' @keywords internal +#' @export +key_colnames <- function(x, ...) { + UseMethod("key_colnames") +} + +#' @export +key_colnames.default <- function(x, ...) { + character(0L) +} + +#' @export +key_colnames.data.frame <- function(x, other_keys = character(0L), ...) { + arg_is_chr(other_keys, allow_empty = TRUE) + nm <- c("time_value", "geo_value", other_keys) + intersect(nm, colnames(x)) +} + +#' @export +key_colnames.epi_df <- function(x, ...) { + other_keys <- attr(x, "metadata")$other_keys + c("time_value", "geo_value", other_keys) +} + +#' @export +key_colnames.epi_archive <- function(x, ...) { + other_keys <- attr(x, "metadata")$other_keys + c("time_value", "geo_value", other_keys) +} + +kill_time_value <- function(v) { + arg_is_chr(v) + v[v != "time_value"] +} + +key_colnames_only <- function(x, ...) { + kill_time_value(key_colnames(x, ...)) +} From 989ee8c84994e97fc3aa0c4322adecccd8a6148c Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Fri, 19 Jan 2024 15:23:05 -0800 Subject: [PATCH 10/27] rename based on review --- DESCRIPTION | 2 +- NAMESPACE | 10 ++--- R/autoplot.R | 18 ++++---- man/as_epi_archive.Rd | 56 +++++++++++++++---------- man/as_epi_df.Rd | 44 ++++++++++++-------- man/autoplot.epi_df.Rd | 9 +++- man/detect_outlr.Rd | 62 +++++++++++++++++----------- man/detect_outlr_rm.Rd | 7 ++-- man/detect_outlr_stl.Rd | 7 ++-- man/epi_archive.Rd | 18 ++++---- man/epi_cor.Rd | 48 ++++++++++++--------- man/epi_slide.Rd | 22 ++++++---- man/epix_as_of.Rd | 25 +++++++---- man/epix_merge.Rd | 10 ++--- man/epix_slide.Rd | 55 +++++++++++++----------- man/group_by.epi_archive.Rd | 48 ++++++++++++--------- man/growth_rate.Rd | 12 +++--- man/is_epi_archive.Rd | 4 +- man/{epi_keys.Rd => key_colnames.Rd} | 8 ++-- 19 files changed, 273 insertions(+), 192 deletions(-) rename man/{epi_keys.Rd => key_colnames.Rd} (76%) diff --git a/DESCRIPTION b/DESCRIPTION index 3ce7b860..211c5212 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -76,12 +76,12 @@ Collate: 'correlation.R' 'data.R' 'epi_df.R' - 'epi_keys.R' 'epiprocess.R' 'group_by_epi_df_methods.R' 'methods-epi_archive.R' 'grouped_epi_archive.R' 'growth_rate.R' + 'key_colnames.R' 'methods-epi_df.R' 'outliers.R' 'reexports.R' diff --git a/NAMESPACE b/NAMESPACE index 9b2134ae..a843813d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,10 +13,6 @@ S3method(dplyr_col_modify,col_modify_recorder_df) S3method(dplyr_col_modify,epi_df) S3method(dplyr_reconstruct,epi_df) S3method(dplyr_row_slice,epi_df) -S3method(epi_keys,data.frame) -S3method(epi_keys,default) -S3method(epi_keys,epi_archive) -S3method(epi_keys,epi_df) S3method(epix_truncate_versions_after,epi_archive) S3method(epix_truncate_versions_after,grouped_epi_archive) S3method(group_by,epi_archive) @@ -25,6 +21,10 @@ S3method(group_by,grouped_epi_archive) S3method(group_by_drop_default,grouped_epi_archive) S3method(group_modify,epi_df) S3method(groups,grouped_epi_archive) +S3method(key_colnames,data.frame) +S3method(key_colnames,default) +S3method(key_colnames,epi_archive) +S3method(key_colnames,epi_df) S3method(next_after,Date) S3method(next_after,integer) S3method(print,epi_df) @@ -45,7 +45,6 @@ export(detect_outlr_rm) export(detect_outlr_stl) export(epi_archive) export(epi_cor) -export(epi_keys) export(epi_slide) export(epix_as_of) export(epix_merge) @@ -58,6 +57,7 @@ export(growth_rate) export(is_epi_archive) export(is_epi_df) export(is_grouped_epi_archive) +export(key_colnames) export(max_version_with_row_in) export(mutate) export(new_epi_df) diff --git a/R/autoplot.R b/R/autoplot.R index 6c6bf5eb..bcd0b54d 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -22,7 +22,7 @@ #' @param .max_facets Cut down of the number of facets displayed. Especially #' useful for testing when there are many `geo_value`'s or keys. #' -#' @return +#' @return A ggplot object #' @export #' #' @examples @@ -45,12 +45,12 @@ autoplot.epi_df <- function( if (is.finite(.max_facets)) arg_is_int(.max_facets) arg_is_chr_scalar(.base_color) - ek <- epi_keys(object) - mv <- setdiff(names(object), ek) - ek <- kill_time_value(ek) + key_cols <- key_colnames(object) + non_key_cols <- setdiff(names(object), key_cols) + geo_and_other_keys <- kill_time_value(key_cols) # --- check for numeric variables - allowed <- purrr::map_lgl(object[mv], is.numeric) + allowed <- purrr::map_lgl(object[non_key_cols], is.numeric) if (length(allowed) == 0) { cli::cli_abort("No numeric variables were available to plot automatically.") } @@ -77,7 +77,7 @@ autoplot.epi_df <- function( # --- create a viable df to plot pos <- tidyselect::eval_select( - rlang::expr(c("time_value", ek, names(vars))), object + rlang::expr(c("time_value", geo_and_other_keys, names(vars))), object ) if (length(vars) > 1) { object <- tidyr::pivot_longer( @@ -88,9 +88,9 @@ autoplot.epi_df <- function( } else { object <- dplyr::rename(object[pos], .response := !!names(vars)) } - all_keys <- rlang::syms(as.list(ek)) - other_keys <- rlang::syms(as.list(setdiff(ek, "geo_value"))) - all_avail <- rlang::syms(as.list(c(ek, ".response_name"))) + all_keys <- rlang::syms(as.list(geo_and_other_keys)) + other_keys <- rlang::syms(as.list(setdiff(geo_and_other_keys, "geo_value"))) + all_avail <- rlang::syms(as.list(c(geo_and_other_keys, ".response_name"))) object <- object %>% dplyr::mutate( diff --git a/man/as_epi_archive.Rd b/man/as_epi_archive.Rd index e3604341..93b10736 100644 --- a/man/as_epi_archive.Rd +++ b/man/as_epi_archive.Rd @@ -99,32 +99,44 @@ would be equivalent to: # Simple ex. with necessary keys tib <- tibble::tibble( geo_value = rep(c("ca", "hi"), each = 5), - time_value = rep(seq(as.Date("2020-01-01"), - by = 1, length.out = 5), times = 2), - version = rep(seq(as.Date("2020-01-02"), - by = 1, length.out = 5), times = 2), + time_value = rep(seq(as.Date("2020-01-01"), + by = 1, length.out = 5 + ), times = 2), + version = rep(seq(as.Date("2020-01-02"), + by = 1, length.out = 5 + ), times = 2), value = rnorm(10, mean = 2, sd = 1) ) -toy_epi_archive <- tib \%>\% as_epi_archive(geo_type = "state", - time_type = "day") -toy_epi_archive +toy_epi_archive <- tib \%>\% as_epi_archive( + geo_type = "state", + time_type = "day" +) +toy_epi_archive # Ex. with an additional key for county -df <- data.frame (geo_value = c(replicate(2, "ca"), replicate(2, "fl")), - county = c(1, 3, 2, 5), - time_value = c("2020-06-01", - "2020-06-02", - "2020-06-01", - "2020-06-02"), - version = c("2020-06-02", - "2020-06-03", - "2020-06-02", - "2020-06-03"), - cases = c(1, 2, 3, 4), - cases_rate = c(0.01, 0.02, 0.01, 0.05)) +df <- data.frame( + geo_value = c(replicate(2, "ca"), replicate(2, "fl")), + county = c(1, 3, 2, 5), + time_value = c( + "2020-06-01", + "2020-06-02", + "2020-06-01", + "2020-06-02" + ), + version = c( + "2020-06-02", + "2020-06-03", + "2020-06-02", + "2020-06-03" + ), + cases = c(1, 2, 3, 4), + cases_rate = c(0.01, 0.02, 0.01, 0.05) +) -x <- df \%>\% as_epi_archive(geo_type = "state", - time_type = "day", - other_keys = "county") +x <- df \%>\% as_epi_archive( + geo_type = "state", + time_type = "day", + other_keys = "county" +) } diff --git a/man/as_epi_df.Rd b/man/as_epi_df.Rd index 851aed7e..40c0a1c5 100644 --- a/man/as_epi_df.Rd +++ b/man/as_epi_df.Rd @@ -76,19 +76,22 @@ the \code{tbl_ts} class is dropped, and any key variables (other than ex1_input <- tibble::tibble( geo_value = rep(c("ca", "fl", "pa"), each = 3), - county_code = c("06059","06061","06067", - "12111","12113","12117", - "42101", "42103","42105"), + county_code = c( + "06059", "06061", "06067", + "12111", "12113", "12117", + "42101", "42103", "42105" + ), time_value = rep(seq(as.Date("2020-06-01"), as.Date("2020-06-03"), - by = "day"), length.out = length(geo_value)), + by = "day" + ), length.out = length(geo_value)), value = 1:length(geo_value) + 0.01 * rnorm(length(geo_value)) -) \%>\% +) \%>\% tsibble::as_tsibble(index = time_value, key = c(geo_value, county_code)) # The `other_keys` metadata (`"county_code"` in this case) is automatically # inferred from the `tsibble`'s `key`: ex1 <- as_epi_df(x = ex1_input, geo_type = "state", time_type = "day", as_of = "2020-06-03") -attr(ex1,"metadata")[["other_keys"]] +attr(ex1, "metadata")[["other_keys"]] @@ -102,17 +105,21 @@ ex2_input <- tibble::tibble( state = rep(c("ca", "fl", "pa"), each = 3), # misnamed pol = rep(c("blue", "swing", "swing"), each = 3), # extra key reported_date = rep(seq(as.Date("2020-06-01"), as.Date("2020-06-03"), - by = "day"), length.out = length(state)), # misnamed + by = "day" + ), length.out = length(state)), # misnamed value = 1:length(state) + 0.01 * rnorm(length(state)) -) +) print(ex2_input) -ex2 <- ex2_input \%>\% dplyr::rename(geo_value = state, time_value = reported_date) \%>\% - as_epi_df(geo_type = "state", as_of = "2020-06-03", - additional_metadata = list(other_keys = "pol")) +ex2 <- ex2_input \%>\% + dplyr::rename(geo_value = state, time_value = reported_date) \%>\% + as_epi_df( + geo_type = "state", as_of = "2020-06-03", + additional_metadata = list(other_keys = "pol") + ) -attr(ex2,"metadata") +attr(ex2, "metadata") @@ -120,17 +127,18 @@ attr(ex2,"metadata") ex3_input <- jhu_csse_county_level_subset \%>\% dplyr::filter(time_value > "2021-12-01", state_name == "Massachusetts") \%>\% - dplyr::slice_tail(n = 6) + dplyr::slice_tail(n = 6) -ex3 <- ex3_input \%>\% +ex3 <- ex3_input \%>\% tsibble::as_tsibble() \%>\% # needed to add the additional metadata # add 2 extra keys dplyr::mutate( - state = rep("MA",6), - pol = rep(c("blue", "swing", "swing"), each = 2)) \%>\% - # the 2 extra keys we added have to be specified in the other_keys + state = rep("MA", 6), + pol = rep(c("blue", "swing", "swing"), each = 2) + ) \%>\% + # the 2 extra keys we added have to be specified in the other_keys # component of additional_metadata. as_epi_df(additional_metadata = list(other_keys = c("state", "pol"))) -attr(ex3,"metadata") +attr(ex3, "metadata") } diff --git a/man/autoplot.epi_df.Rd b/man/autoplot.epi_df.Rd index 134a83fe..68aef0c4 100644 --- a/man/autoplot.epi_df.Rd +++ b/man/autoplot.epi_df.Rd @@ -43,12 +43,17 @@ share the same color line.} \item{.max_facets}{Cut down of the number of facets displayed. Especially useful for testing when there are many \code{geo_value}'s or keys.} } +\value{ +A ggplot object +} \description{ Automatically plot an epi_df } \examples{ autoplot(jhu_csse_daily_subset, cases, death_rate_7d_av) autoplot(jhu_csse_daily_subset, case_rate_7d_av, .facet_by = "geo_value") -autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", - .facet_by = "geo_value") +autoplot(jhu_csse_daily_subset, case_rate_7d_av, + .color_by = "none", + .facet_by = "geo_value" +) } diff --git a/man/detect_outlr.Rd b/man/detect_outlr.Rd index 4aa0b79c..3a793ebf 100644 --- a/man/detect_outlr.Rd +++ b/man/detect_outlr.Rd @@ -64,29 +64,43 @@ For convenience, the outlier detection method can be specified (in the STL decomposition. } \examples{ - detection_methods = dplyr::bind_rows( - dplyr::tibble(method = "rm", - args = list(list(detect_negatives = TRUE, - detection_multiplier = 2.5)), - abbr = "rm"), - dplyr::tibble(method = "stl", - args = list(list(detect_negatives = TRUE, - detection_multiplier = 2.5, - seasonal_period = 7)), - abbr = "stl_seasonal"), - dplyr::tibble(method = "stl", - args = list(list(detect_negatives = TRUE, - detection_multiplier = 2.5, - seasonal_period = NULL)), - abbr = "stl_nonseasonal")) +detection_methods <- dplyr::bind_rows( + dplyr::tibble( + method = "rm", + args = list(list( + detect_negatives = TRUE, + detection_multiplier = 2.5 + )), + abbr = "rm" + ), + dplyr::tibble( + method = "stl", + args = list(list( + detect_negatives = TRUE, + detection_multiplier = 2.5, + seasonal_period = 7 + )), + abbr = "stl_seasonal" + ), + dplyr::tibble( + method = "stl", + args = list(list( + detect_negatives = TRUE, + detection_multiplier = 2.5, + seasonal_period = NULL + )), + abbr = "stl_nonseasonal" + ) +) - x <- incidence_num_outlier_example \%>\% - dplyr::select(geo_value,time_value,cases) \%>\% - as_epi_df() \%>\% - group_by(geo_value) \%>\% - mutate(outlier_info = detect_outlr( - x = time_value, y = cases, - methods = detection_methods, - combiner = "median")) \%>\% - unnest(outlier_info) +x <- incidence_num_outlier_example \%>\% + dplyr::select(geo_value, time_value, cases) \%>\% + as_epi_df() \%>\% + group_by(geo_value) \%>\% + mutate(outlier_info = detect_outlr( + x = time_value, y = cases, + methods = detection_methods, + combiner = "median" + )) \%>\% + unnest(outlier_info) } diff --git a/man/detect_outlr_rm.Rd b/man/detect_outlr_rm.Rd index 3efae55d..0d011619 100644 --- a/man/detect_outlr_rm.Rd +++ b/man/detect_outlr_rm.Rd @@ -59,10 +59,11 @@ terms of multiples of the rolling interquartile range (IQR). \examples{ # Detect outliers based on a rolling median incidence_num_outlier_example \%>\% - dplyr::select(geo_value,time_value,cases) \%>\% + dplyr::select(geo_value, time_value, cases) \%>\% as_epi_df() \%>\% group_by(geo_value) \%>\% - mutate(outlier_info = detect_outlr_rm( - x = time_value, y = cases)) \%>\% + mutate(outlier_info = detect_outlr_rm( + x = time_value, y = cases + )) \%>\% unnest(outlier_info) } diff --git a/man/detect_outlr_stl.Rd b/man/detect_outlr_stl.Rd index 7e724a4e..34a550d5 100644 --- a/man/detect_outlr_stl.Rd +++ b/man/detect_outlr_stl.Rd @@ -78,11 +78,12 @@ are exactly as in \code{detect_outlr_rm()}. \examples{ # Detects outliers based on a seasonal-trend decomposition using LOESS incidence_num_outlier_example \%>\% - dplyr::select(geo_value,time_value,cases) \%>\% + dplyr::select(geo_value, time_value, cases) \%>\% as_epi_df() \%>\% group_by(geo_value) \%>\% - mutate(outlier_info = detect_outlr_stl( + mutate(outlier_info = detect_outlr_stl( x = time_value, y = cases, - seasonal_period = 7 )) \%>\% # weekly seasonality for daily data + seasonal_period = 7 + )) \%>\% # weekly seasonality for daily data unnest(outlier_info) } diff --git a/man/epi_archive.Rd b/man/epi_archive.Rd index f328eb44..6a25b2af 100644 --- a/man/epi_archive.Rd +++ b/man/epi_archive.Rd @@ -100,16 +100,20 @@ are documented in the wrapper function \code{\link[=epix_slide]{epix_slide()}}. \examples{ tib <- tibble::tibble( geo_value = rep(c("ca", "hi"), each = 5), - time_value = rep(seq(as.Date("2020-01-01"), - by = 1, length.out = 5), times = 2), - version = rep(seq(as.Date("2020-01-02"), - by = 1, length.out = 5), times = 2), + time_value = rep(seq(as.Date("2020-01-01"), + by = 1, length.out = 5 + ), times = 2), + version = rep(seq(as.Date("2020-01-02"), + by = 1, length.out = 5 + ), times = 2), value = rnorm(10, mean = 2, sd = 1) ) -toy_epi_archive <- tib \%>\% epi_archive$new(geo_type = "state", - time_type = "day") -toy_epi_archive +toy_epi_archive <- tib \%>\% epi_archive$new( + geo_type = "state", + time_type = "day" +) +toy_epi_archive } \section{Public fields}{ \if{html}{\out{
}} diff --git a/man/epi_cor.Rd b/man/epi_cor.Rd index 6b2279db..fb56073f 100644 --- a/man/epi_cor.Rd +++ b/man/epi_cor.Rd @@ -58,30 +58,38 @@ grouping by geo value, time value, or any other variables. See the for examples. } \examples{ - + # linear association of case and death rates on any given day -epi_cor(x = jhu_csse_daily_subset, - var1 = case_rate_7d_av, - var2 = death_rate_7d_av, - cor_by = "time_value") +epi_cor( + x = jhu_csse_daily_subset, + var1 = case_rate_7d_av, + var2 = death_rate_7d_av, + cor_by = "time_value" +) # correlation of death rates and lagged case rates -epi_cor(x = jhu_csse_daily_subset, - var1 = case_rate_7d_av, - var2 = death_rate_7d_av, - cor_by = time_value, - dt1 = -2) +epi_cor( + x = jhu_csse_daily_subset, + var1 = case_rate_7d_av, + var2 = death_rate_7d_av, + cor_by = time_value, + dt1 = -2 +) -# correlation grouped by location -epi_cor(x = jhu_csse_daily_subset, - var1 = case_rate_7d_av, - var2 = death_rate_7d_av, - cor_by = geo_value) +# correlation grouped by location +epi_cor( + x = jhu_csse_daily_subset, + var1 = case_rate_7d_av, + var2 = death_rate_7d_av, + cor_by = geo_value +) # correlation grouped by location and incorporates lagged cases rates -epi_cor(x = jhu_csse_daily_subset, - var1 = case_rate_7d_av, - var2 = death_rate_7d_av, - cor_by = geo_value, - dt1 = -2) +epi_cor( + x = jhu_csse_daily_subset, + var1 = case_rate_7d_av, + var2 = death_rate_7d_av, + cor_by = geo_value, + dt1 = -2 +) } diff --git a/man/epi_slide.Rd b/man/epi_slide.Rd index 33c3a7fb..668be9ff 100644 --- a/man/epi_slide.Rd +++ b/man/epi_slide.Rd @@ -156,9 +156,9 @@ through the \code{new_col_name} argument. # slide a 7-day trailing average formula on cases jhu_csse_daily_subset \%>\% group_by(geo_value) \%>\% - epi_slide(cases_7dav = mean(cases), before = 6) \%>\% + epi_slide(cases_7dav = mean(cases), before = 6) \%>\% # rmv a nonessential var. to ensure new col is printed - dplyr::select(-death_rate_7d_av) + dplyr::select(-death_rate_7d_av) # slide a 7-day leading average jhu_csse_daily_subset \%>\% @@ -170,21 +170,25 @@ jhu_csse_daily_subset \%>\% # slide a 7-day centre-aligned average jhu_csse_daily_subset \%>\% group_by(geo_value) \%>\% - epi_slide(cases_7dav = mean(cases), before = 3, after = 3) \%>\% + epi_slide(cases_7dav = mean(cases), before = 3, after = 3) \%>\% # rmv a nonessential var. to ensure new col is printed - dplyr::select(-death_rate_7d_av) + dplyr::select(-death_rate_7d_av) # slide a 14-day centre-aligned average jhu_csse_daily_subset \%>\% group_by(geo_value) \%>\% - epi_slide(cases_7dav = mean(cases), before = 6, after = 7) \%>\% + epi_slide(cases_7dav = mean(cases), before = 6, after = 7) \%>\% # rmv a nonessential var. to ensure new col is printed - dplyr::select(-death_rate_7d_av) + dplyr::select(-death_rate_7d_av) # nested new columns jhu_csse_daily_subset \%>\% group_by(geo_value) \%>\% - epi_slide(a = data.frame(cases_2dav = mean(cases), - cases_2dma = mad(cases)), - before = 1, as_list_col = TRUE) + epi_slide( + a = data.frame( + cases_2dav = mean(cases), + cases_2dma = mad(cases) + ), + before = 1, as_list_col = TRUE + ) } diff --git a/man/epix_as_of.Rd b/man/epix_as_of.Rd index 51884597..9a0a53ce 100644 --- a/man/epix_as_of.Rd +++ b/man/epix_as_of.Rd @@ -55,14 +55,18 @@ in the future. } \examples{ # warning message of data latency shown -epix_as_of(x = archive_cases_dv_subset, - max_version = max(archive_cases_dv_subset$DT$version)) +epix_as_of( + x = archive_cases_dv_subset, + max_version = max(archive_cases_dv_subset$DT$version) +) range(archive_cases_dv_subset$DT$version) # 2020-06-02 -- 2021-12-01 -epix_as_of(x = archive_cases_dv_subset, - max_version = as.Date("2020-06-12")) +epix_as_of( + x = archive_cases_dv_subset, + max_version = as.Date("2020-06-12") +) # When fetching a snapshot as of the latest version with update data in the # archive, a warning is issued by default, as this update data might not yet @@ -72,10 +76,15 @@ epix_as_of(x = archive_cases_dv_subset, # based on database queries, the latest available update might still be # subject to change, but previous versions should be finalized). We can # muffle such warnings with the following pattern: -withCallingHandlers({ - epix_as_of(x = archive_cases_dv_subset, - max_version = max(archive_cases_dv_subset$DT$version)) -}, epiprocess__snapshot_as_of_clobberable_version = function(wrn) invokeRestart("muffleWarning")) +withCallingHandlers( + { + epix_as_of( + x = archive_cases_dv_subset, + max_version = max(archive_cases_dv_subset$DT$version) + ) + }, + epiprocess__snapshot_as_of_clobberable_version = function(wrn) invokeRestart("muffleWarning") +) # Since R 4.0, there is a `globalCallingHandlers` function that can be used # to globally toggle these warnings. diff --git a/man/epix_merge.Rd b/man/epix_merge.Rd index 09f67fa2..53dea071 100644 --- a/man/epix_merge.Rd +++ b/man/epix_merge.Rd @@ -60,13 +60,13 @@ be clobbered in either input archive. \examples{ # create two example epi_archive datasets x <- archive_cases_dv_subset$DT \%>\% - dplyr::select(geo_value,time_value,version,case_rate_7d_av) \%>\% - as_epi_archive(compactify=TRUE) + dplyr::select(geo_value, time_value, version, case_rate_7d_av) \%>\% + as_epi_archive(compactify = TRUE) y <- archive_cases_dv_subset$DT \%>\% - dplyr::select(geo_value,time_value,version,percent_cli) \%>\% - as_epi_archive(compactify=TRUE) + dplyr::select(geo_value, time_value, version, percent_cli) \%>\% + as_epi_archive(compactify = TRUE) # merge results stored in a third object: -xy = epix_merge(x, y) +xy <- epix_merge(x, y) # vs. mutating x to hold the merge result: x$merge(y) diff --git a/man/epix_slide.Rd b/man/epix_slide.Rd index d94460af..3ac55a18 100644 --- a/man/epix_slide.Rd +++ b/man/epix_slide.Rd @@ -193,17 +193,20 @@ library(dplyr) # Reference time points for which we want to compute slide values: ref_time_values <- seq(as.Date("2020-06-01"), - as.Date("2020-06-15"), - by = "1 day") + as.Date("2020-06-15"), + by = "1 day" +) # A simple (but not very useful) example (see the archive vignette for a more # realistic one): archive_cases_dv_subset \%>\% group_by(geo_value) \%>\% - epix_slide(f = ~ mean(.x$case_rate_7d_av), - before = 2, - ref_time_values = ref_time_values, - new_col_name = 'case_rate_7d_av_recent_av') \%>\% + epix_slide( + f = ~ mean(.x$case_rate_7d_av), + before = 2, + ref_time_values = ref_time_values, + new_col_name = "case_rate_7d_av_recent_av" + ) \%>\% ungroup() # We requested time windows that started 2 days before the corresponding time # values. The actual number of `time_value`s in each computation depends on @@ -221,23 +224,24 @@ archive_cases_dv_subset \%>\% # Examining characteristics of the data passed to each computation with # `all_versions=FALSE`. archive_cases_dv_subset \%>\% - group_by(geo_value) \%>\% - epix_slide( - function(x, gk, rtv) { - tibble( - time_range = if(nrow(x) == 0L) { - "0 `time_value`s" - } else { - sprintf("\%s -- \%s", min(x$time_value), max(x$time_value)) - }, - n = nrow(x), - class1 = class(x)[[1L]] - ) - }, - before = 5, all_versions = FALSE, - ref_time_values = ref_time_values, names_sep=NULL) \%>\% - ungroup() \%>\% - arrange(geo_value, time_value) + group_by(geo_value) \%>\% + epix_slide( + function(x, gk, rtv) { + tibble( + time_range = if (nrow(x) == 0L) { + "0 `time_value`s" + } else { + sprintf("\%s -- \%s", min(x$time_value), max(x$time_value)) + }, + n = nrow(x), + class1 = class(x)[[1L]] + ) + }, + before = 5, all_versions = FALSE, + ref_time_values = ref_time_values, names_sep = NULL + ) \%>\% + ungroup() \%>\% + arrange(geo_value, time_value) # --- Advanced: --- @@ -259,7 +263,7 @@ archive_cases_dv_subset \%>\% toString(min(x$DT$version)) }, versions_end = x$versions_end, - time_range = if(nrow(x$DT) == 0L) { + time_range = if (nrow(x$DT) == 0L) { "0 `time_value`s" } else { sprintf("\%s -- \%s", min(x$DT$time_value), max(x$DT$time_value)) @@ -269,7 +273,8 @@ archive_cases_dv_subset \%>\% ) }, before = 5, all_versions = TRUE, - ref_time_values = ref_time_values, names_sep=NULL) \%>\% + ref_time_values = ref_time_values, names_sep = NULL + ) \%>\% ungroup() \%>\% # Focus on one geo_value so we can better see the columns above: filter(geo_value == "ca") \%>\% diff --git a/man/group_by.epi_archive.Rd b/man/group_by.epi_archive.Rd index aee0a07b..5e867bf3 100644 --- a/man/group_by.epi_archive.Rd +++ b/man/group_by.epi_archive.Rd @@ -93,7 +93,7 @@ to \code{group_by_drop_default.default} (but there is a dedicated method for } \examples{ -grouped_archive = archive_cases_dv_subset \%>\% group_by(geo_value) +grouped_archive <- archive_cases_dv_subset \%>\% group_by(geo_value) # `print` for metadata and method listing: grouped_archive \%>\% print() @@ -102,10 +102,12 @@ grouped_archive \%>\% print() archive_cases_dv_subset \%>\% group_by(geo_value) \%>\% - epix_slide(f = ~ mean(.x$case_rate_7d_av), - before = 2, - ref_time_values = as.Date("2020-06-11") + 0:2, - new_col_name = 'case_rate_3d_av') \%>\% + epix_slide( + f = ~ mean(.x$case_rate_7d_av), + before = 2, + ref_time_values = as.Date("2020-06-11") + 0:2, + new_col_name = "case_rate_3d_av" + ) \%>\% ungroup() # ----------------------------------------------------------------- @@ -113,34 +115,42 @@ archive_cases_dv_subset \%>\% # Advanced: some other features of dplyr grouping are implemented: library(dplyr) -toy_archive = +toy_archive <- tribble( - ~geo_value, ~age_group, ~time_value, ~version, ~value, - "us", "adult", "2000-01-01", "2000-01-02", 121, - "us", "pediatric", "2000-01-02", "2000-01-03", 5, # (addition) - "us", "adult", "2000-01-01", "2000-01-03", 125, # (revision) - "us", "adult", "2000-01-02", "2000-01-03", 130 # (addition) + ~geo_value, ~age_group, ~time_value, ~version, ~value, + "us", "adult", "2000-01-01", "2000-01-02", 121, + "us", "pediatric", "2000-01-02", "2000-01-03", 5, # (addition) + "us", "adult", "2000-01-01", "2000-01-03", 125, # (revision) + "us", "adult", "2000-01-02", "2000-01-03", 130 # (addition) + ) \%>\% + mutate( + age_group = ordered(age_group, c("pediatric", "adult")), + time_value = as.Date(time_value), + version = as.Date(version) ) \%>\% - mutate(age_group = ordered(age_group, c("pediatric", "adult")), - time_value = as.Date(time_value), - version = as.Date(version)) \%>\% as_epi_archive(other_keys = "age_group") # The following are equivalent: toy_archive \%>\% group_by(geo_value, age_group) -toy_archive \%>\% group_by(geo_value) \%>\% group_by(age_group, .add=TRUE) -grouping_cols = c("geo_value", "age_group") +toy_archive \%>\% + group_by(geo_value) \%>\% + group_by(age_group, .add = TRUE) +grouping_cols <- c("geo_value", "age_group") toy_archive \%>\% group_by(across(all_of(grouping_cols))) # And these are equivalent: toy_archive \%>\% group_by(geo_value) -toy_archive \%>\% group_by(geo_value, age_group) \%>\% ungroup(age_group) +toy_archive \%>\% + group_by(geo_value, age_group) \%>\% + ungroup(age_group) # To get the grouping variable names as a `list` of `name`s (a.k.a. symbols): -toy_archive \%>\% group_by(geo_value) \%>\% groups() +toy_archive \%>\% + group_by(geo_value) \%>\% + groups() toy_archive \%>\% - group_by(geo_value, age_group, .drop=FALSE) \%>\% + group_by(geo_value, age_group, .drop = FALSE) \%>\% epix_slide(f = ~ sum(.x$value), before = 20) \%>\% ungroup() diff --git a/man/growth_rate.Rd b/man/growth_rate.Rd index 203d5d7d..7a3f1151 100644 --- a/man/growth_rate.Rd +++ b/man/growth_rate.Rd @@ -136,12 +136,12 @@ user. \examples{ # COVID cases growth rate by state using default method relative change -jhu_csse_daily_subset \%>\% - group_by(geo_value) \%>\% - mutate(cases_gr = growth_rate(x = time_value, y = cases)) +jhu_csse_daily_subset \%>\% + group_by(geo_value) \%>\% + mutate(cases_gr = growth_rate(x = time_value, y = cases)) # Log scale, degree 4 polynomial and 6-fold cross validation -jhu_csse_daily_subset \%>\% - group_by(geo_value) \%>\% - mutate(gr_poly = growth_rate( x = time_value, y = cases, log_scale = TRUE, ord = 4, k = 6)) +jhu_csse_daily_subset \%>\% + group_by(geo_value) \%>\% + mutate(gr_poly = growth_rate(x = time_value, y = cases, log_scale = TRUE, ord = 4, k = 6)) } diff --git a/man/is_epi_archive.Rd b/man/is_epi_archive.Rd index 5b133004..2beb3a8c 100644 --- a/man/is_epi_archive.Rd +++ b/man/is_epi_archive.Rd @@ -25,9 +25,9 @@ is_epi_archive(archive_cases_dv_subset) # TRUE # By default, grouped_epi_archives don't count as epi_archives, as they may # support a different set of operations from regular `epi_archives`. This # behavior can be controlled by `grouped_okay`. -grouped_archive = archive_cases_dv_subset$group_by(geo_value) +grouped_archive <- archive_cases_dv_subset$group_by(geo_value) is_epi_archive(grouped_archive) # FALSE -is_epi_archive(grouped_archive, grouped_okay=TRUE) # TRUE +is_epi_archive(grouped_archive, grouped_okay = TRUE) # TRUE } \seealso{ diff --git a/man/epi_keys.Rd b/man/key_colnames.Rd similarity index 76% rename from man/epi_keys.Rd rename to man/key_colnames.Rd index 8026fc14..fbaa3c11 100644 --- a/man/epi_keys.Rd +++ b/man/key_colnames.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/epi_keys.R -\name{epi_keys} -\alias{epi_keys} +% Please edit documentation in R/key_colnames.R +\name{key_colnames} +\alias{key_colnames} \title{Grab any keys associated to an epi_df} \usage{ -epi_keys(x, ...) +key_colnames(x, ...) } \arguments{ \item{x}{a data.frame, tibble, or epi_df} From 3c68f1db0676b48197c38b1806443402074a97f0 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Fri, 19 Jan 2024 15:34:08 -0800 Subject: [PATCH 11/27] pkgdown fix --- _pkgdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index 1d9b1955..1b11ee6f 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -91,4 +91,4 @@ reference: - max_version_with_row_in - next_after - guess_period - - epi_keys + - key_colnames From f6464c02c6406a68f4cbffe4043c5cdb288f2303 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Thu, 25 Jan 2024 08:43:51 -0800 Subject: [PATCH 12/27] bugfix: add missing arg_is_numeric --- R/utils-arg.R | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/R/utils-arg.R b/R/utils-arg.R index dca21646..b48a3642 100644 --- a/R/utils-arg.R +++ b/R/utils-arg.R @@ -24,6 +24,17 @@ arg_is_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { ) } +arg_is_numeric <- function(..., allow_null = FALSE) { + handle_arg_list( + ..., + tests = function(name, value) { + if (!(is.numeric(value) | (is.null(value) & allow_null))) { + cli::cli_abort("All {.val {name}} must numeric.") + } + } + ) +} + arg_is_int <- function(..., allow_null = FALSE) { arg_is_numeric(..., allow_null = allow_null) handle_arg_list( From b3d876d85a077fe606700ca3bd9643458bac5d92 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Thu, 25 Jan 2024 09:05:37 -0800 Subject: [PATCH 13/27] bugfix: remove ability to plot non-numeric vars --- R/autoplot.R | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/R/autoplot.R b/R/autoplot.R index bcd0b54d..c193e01f 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -32,6 +32,12 @@ #' .color_by = "none", #' .facet_by = "geo_value" #' ) +#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", +#' .base_color = "red", .facet_by = "geo_value") +#' +#' # .base_color specification won't have any effect due .color_by default +#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, +#' .base_color = "red", .facet_by = "geo_value") autoplot.epi_df <- function( object, ..., .color_by = c("all_keys", "geo_value", "other_keys", ".response", "all", "none"), @@ -51,6 +57,7 @@ autoplot.epi_df <- function( # --- check for numeric variables allowed <- purrr::map_lgl(object[non_key_cols], is.numeric) + allowed <- allowed[allowed] if (length(allowed) == 0) { cli::cli_abort("No numeric variables were available to plot automatically.") } From b1a2eef72b6898e08dcff0d33705f65e4d7901ea Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Thu, 25 Jan 2024 09:09:21 -0800 Subject: [PATCH 14/27] remove unused --- R/key_colnames.R | 4 ---- 1 file changed, 4 deletions(-) diff --git a/R/key_colnames.R b/R/key_colnames.R index 91be7ab6..158c5a86 100644 --- a/R/key_colnames.R +++ b/R/key_colnames.R @@ -38,7 +38,3 @@ kill_time_value <- function(v) { arg_is_chr(v) v[v != "time_value"] } - -key_colnames_only <- function(x, ...) { - kill_time_value(key_colnames(x, ...)) -} From d30fc8542f5f7e11a9b19055200e41c42458f424 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Thu, 25 Jan 2024 09:17:03 -0800 Subject: [PATCH 15/27] redocument --- man/autoplot.epi_df.Rd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/man/autoplot.epi_df.Rd b/man/autoplot.epi_df.Rd index 68aef0c4..df30528e 100644 --- a/man/autoplot.epi_df.Rd +++ b/man/autoplot.epi_df.Rd @@ -56,4 +56,10 @@ autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", .facet_by = "geo_value" ) +autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", + .base_color = "red", .facet_by = "geo_value") + +# .base_color specification won't have any effect due .color_by default +autoplot(jhu_csse_daily_subset, case_rate_7d_av, + .base_color = "red", .facet_by = "geo_value") } From f2927136333e46ee6448463f8903fe5d1f3c930d Mon Sep 17 00:00:00 2001 From: Nat DeFries <42820733+nmdefries@users.noreply.github.com> Date: Thu, 25 Jan 2024 15:26:06 -0500 Subject: [PATCH 16/27] class warnings and errors --- R/autoplot.R | 12 ++++++++---- R/utils-arg.R | 31 +++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/R/autoplot.R b/R/autoplot.R index c193e01f..ef0878a1 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -59,25 +59,29 @@ autoplot.epi_df <- function( allowed <- purrr::map_lgl(object[non_key_cols], is.numeric) allowed <- allowed[allowed] if (length(allowed) == 0) { - cli::cli_abort("No numeric variables were available to plot automatically.") + cli::cli_abort("No numeric variables were available to plot automatically.", + class = "epiprocess__no_numeric_vars_available") } vars <- tidyselect::eval_select(rlang::expr(c(...)), object) if (rlang::is_empty(vars)) { # find them automatically if unspecified vars <- tidyselect::eval_select(names(allowed)[1], object) cli::cli_warn( - "Plot variable was unspecified. Automatically selecting {.var {names(allowed)[1]}}." + "Plot variable was unspecified. Automatically selecting {.var {names(allowed)[1]}}.", + class = "epiprocess__unspecified_plot_var" ) } else { # if variables were specified, ensure that they are numeric ok <- names(vars) %in% names(allowed) if (!any(ok)) { cli::cli_abort( - "None of the requested variables {.var {names(vars)}} are numeric." + "None of the requested variables {.var {names(vars)}} are numeric.", + class = "epiprocess__all_requested_vars_not_numeric" ) } else if (!all(ok)) { cli::cli_warn(c( "Only the requested variables {.var {names(vars)[ok]}} are numeric.", i = "`autoplot()` cannot display {.var {names(vars)[!ok]}}." - )) + ), + class = "epiprocess__some_requested_vars_not_numeric") vars <- vars[ok] } } diff --git a/R/utils-arg.R b/R/utils-arg.R index b48a3642..dc761e55 100644 --- a/R/utils-arg.R +++ b/R/utils-arg.R @@ -11,12 +11,15 @@ arg_is_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { ..., tests = function(name, value) { if (length(value) > 1 | (!allow_null & length(value) == 0)) { - cli::cli_abort("Argument {.val {name}} must be of length 1.") + cli::cli_abort("Argument {.val {name}} must be of length 1.", + class = "epiprocess__value_not_length_1" + ) } if (!is.null(value)) { if (is.na(value) & !allow_na) { cli::cli_abort( - "Argument {.val {name}} must not be a missing value ({.val {NA}})." + "Argument {.val {name}} must not be a missing value ({.val {NA}}).", + class = "epiprocess__value_is_na" ) } } @@ -29,7 +32,9 @@ arg_is_numeric <- function(..., allow_null = FALSE) { ..., tests = function(name, value) { if (!(is.numeric(value) | (is.null(value) & allow_null))) { - cli::cli_abort("All {.val {name}} must numeric.") + cli::cli_abort("All {.val {name}} must be numeric.", + class = "epiprocess__value_is_null_or_not_numeric" + ) } } ) @@ -41,7 +46,9 @@ arg_is_int <- function(..., allow_null = FALSE) { ..., tests = function(name, value) { if (!(all(value %% 1 == 0) | (is.null(value) & allow_null))) { - cli::cli_abort("All {.val {name}} must be whole positive number(s).") + cli::cli_abort("All {.val {name}} must be whole positive number(s).", + class = "epiprocess__some_decimal_or_negative_elements" + ) } } ) @@ -52,16 +59,24 @@ arg_is_chr <- function(..., allow_null = FALSE, allow_na = FALSE, allow_empty = ..., tests = function(name, value) { if (is.null(value) & !allow_null) { - cli::cli_abort("Argument {.val {name}} may not be `NULL`.") + cli::cli_abort("Argument {.val {name}} may not be `NULL`.", + class = "epiprocess__value_is_null" + ) } if (any(is.na(value)) & !allow_na) { - cli::cli_abort("Argument {.val {name}} must not contain any missing values ({.val {NA}}).") + cli::cli_abort("Argument {.val {name}} must not contain any missing values ({.val {NA}}).", + class = "epiprocess__some_na_elements" + ) } if (!is.null(value) & (length(value) == 0L & !allow_empty)) { - cli::cli_abort("Argument {.val {name}} must have length > 0.") + cli::cli_abort("Argument {.val {name}} must have length > 0.", + class = "epiprocess__value_length_0" + ) } if (!(is.character(value) | is.null(value) | all(is.na(value)))) { - cli::cli_abort("Argument {.val {name}} must be of character type.") + cli::cli_abort("Argument {.val {name}} must be of character type.", + class = "epiprocess__not_character_type" + ) } } ) From 42da7860f4990dcaee5b4bf1ed7af7663db33bcf Mon Sep 17 00:00:00 2001 From: Nat DeFries <42820733+nmdefries@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:11:42 -0500 Subject: [PATCH 17/27] test autoplot warnings and errors --- tests/testthat/test-autoplot.R | 87 ++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/testthat/test-autoplot.R diff --git a/tests/testthat/test-autoplot.R b/tests/testthat/test-autoplot.R new file mode 100644 index 00000000..ba3f8d53 --- /dev/null +++ b/tests/testthat/test-autoplot.R @@ -0,0 +1,87 @@ +library(dplyr) + +d <- as.Date("2020-01-01") + +raw_df_chr <- dplyr::bind_rows( + dplyr::tibble(geo_value = "ak", time_value = d + 1:5, value = "a"), + dplyr::tibble(geo_value = "al", time_value = d + 1:5, value = "d") +) +ungrouped_chr <- as_epi_df(raw_df_chr, as_of = d + 6) +grouped_chr <- ungrouped_chr %>% + group_by(geo_value) + +raw_df_num <- dplyr::bind_rows( + dplyr::tibble(geo_value = "ak", time_value = d + 1:5, value = 11:15), + dplyr::tibble(geo_value = "al", time_value = d + 1:5, value = 1:5) +) +ungrouped_num <- as_epi_df(raw_df_num, as_of = d + 6) +grouped_num <- ungrouped_num %>% + group_by(geo_value) + +test_that("autoplot fails if no non-key columns are numeric", { + expect_error(autoplot(ungrouped_chr), + class = "epiprocess__no_numeric_vars_available" + ) + + # Multiple non-numeric columns + testdf <- mutate(ungrouped_chr, value2 = "d") + expect_error(autoplot(testdf), + class = "epiprocess__no_numeric_vars_available" + ) + + expect_error(autoplot(grouped_chr), + class = "epiprocess__no_numeric_vars_available" + ) + + # A numeric column is available, but is a key not a value. + testdf <- mutate(raw_df_chr, key1 = c(1:5, 5:9)) %>% + as_tsibble(index = time_value, key = c(geo_value, key1)) %>% + as_epi_df(as_of = d + 6) + expect_error(autoplot(testdf), + class = "epiprocess__no_numeric_vars_available" + ) +}) + +test_that("autoplot warns when a variable is not specified, and lists the auto-selected column", { + expect_warning(autoplot(ungrouped_num), + regexp = ".*selecting `value`[.]", + class = "epiprocess__unspecified_plot_var" + ) + + expect_warning(autoplot(grouped_num), + regexp = ".*selecting `value`[.]", + class = "epiprocess__unspecified_plot_var" + ) +}) + +test_that("autoplot errors when all specified columns are not numeric, and lists column names", { + expect_error(autoplot(ungrouped_chr, value), + regexp = ".*value.*", + class = "epiprocess__all_requested_vars_not_numeric" + ) + + testdf <- mutate(ungrouped_chr, value2 = "d") + expect_error(autoplot(testdf, value, value2), + regexp = ".*variables `value` and `value2` are.*", + class = "epiprocess__all_requested_vars_not_numeric" + ) + + expect_error(autoplot(grouped_chr, value), + regexp = ".*variables `value` are.*", + class = "epiprocess__all_requested_vars_not_numeric" + ) +}) + +test_that("autoplot warns when some specified columns are not numeric, and lists column names", { + testdf <- mutate(ungrouped_num, value2 = "d") + expect_warning(autoplot(testdf, value, value2), + regexp = ".*`value` are numeric.*cannot display `value2`.*", + class = "epiprocess__some_requested_vars_not_numeric" + ) + + testdf <- mutate(grouped_num, value2 = "d") + expect_warning(autoplot(testdf, value, value2), + regexp = ".*`value` are numeric.*cannot display `value2`.*", + class = "epiprocess__some_requested_vars_not_numeric" + ) +}) From 7cd1d258b1293e071c5186323d2d2d5eb3f9e146 Mon Sep 17 00:00:00 2001 From: Nat DeFries <42820733+nmdefries@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:28:33 -0500 Subject: [PATCH 18/27] raise 'plot automatically' error only when dots are empty because user didn't pass col names --- R/autoplot.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/autoplot.R b/R/autoplot.R index ef0878a1..d5fe871f 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -58,7 +58,7 @@ autoplot.epi_df <- function( # --- check for numeric variables allowed <- purrr::map_lgl(object[non_key_cols], is.numeric) allowed <- allowed[allowed] - if (length(allowed) == 0) { + if (length(allowed) == 0 && rlang::dots_n(...) == 0L) { cli::cli_abort("No numeric variables were available to plot automatically.", class = "epiprocess__no_numeric_vars_available") } From 9409ae7730c6e71505b57d0e170677ab70cd5b07 Mon Sep 17 00:00:00 2001 From: Nat DeFries <42820733+nmdefries@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:41:17 -0500 Subject: [PATCH 19/27] wrap cols list in all_of to suppress warning Using an external vector in selections was deprecated in tidyselect 1.1.0. i Please use `all_of()` or `any_of()` instead. # Was: data %>% select(geo_and_other_keys) # Now: data %>% select(all_of(geo_and_other_keys)) --- R/autoplot.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/autoplot.R b/R/autoplot.R index d5fe871f..34bf55e0 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -88,7 +88,7 @@ autoplot.epi_df <- function( # --- create a viable df to plot pos <- tidyselect::eval_select( - rlang::expr(c("time_value", geo_and_other_keys, names(vars))), object + rlang::expr(c("time_value", tidyselect::all_of(geo_and_other_keys), names(vars))), object ) if (length(vars) > 1) { object <- tidyr::pivot_longer( From 39f3704c968c6db223d4947db1bf0957684be920 Mon Sep 17 00:00:00 2001 From: Nat DeFries <42820733+nmdefries@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:56:39 -0500 Subject: [PATCH 20/27] check int positive --- R/utils-arg.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/utils-arg.R b/R/utils-arg.R index dc761e55..85331dd6 100644 --- a/R/utils-arg.R +++ b/R/utils-arg.R @@ -45,7 +45,7 @@ arg_is_int <- function(..., allow_null = FALSE) { handle_arg_list( ..., tests = function(name, value) { - if (!(all(value %% 1 == 0) | (is.null(value) & allow_null))) { + if (!( (all(value %% 1 == 0) && all(value > 0)) | (is.null(value) & allow_null))) { cli::cli_abort("All {.val {name}} must be whole positive number(s).", class = "epiprocess__some_decimal_or_negative_elements" ) From 2e301ad568459976f8de63085efc60a64f7dd5bd Mon Sep 17 00:00:00 2001 From: Nat DeFries <42820733+nmdefries@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:42:29 -0500 Subject: [PATCH 21/27] test utils-arg --- tests/testthat/test-utils-arg.R | 80 +++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/testthat/test-utils-arg.R diff --git a/tests/testthat/test-utils-arg.R b/tests/testthat/test-utils-arg.R new file mode 100644 index 00000000..ab29e061 --- /dev/null +++ b/tests/testthat/test-utils-arg.R @@ -0,0 +1,80 @@ +test_that("arg_is_scalar basic behavior", { + expect_no_error(arg_is_scalar(d = 1, "a", 2, c = c("1"), a = list(2))) + + expect_error(arg_is_scalar(c(3, 5, 5)), + class = "epiprocess__value_not_length_1" + ) + + expect_no_error(arg_is_scalar(NULL, allow_null = TRUE)) + expect_error(arg_is_scalar(NULL), + class = "epiprocess__value_not_length_1" + ) + + expect_no_error(arg_is_scalar(NA, allow_na = TRUE)) + expect_error(arg_is_scalar(NA), + class = "epiprocess__value_is_na" + ) +}) + +test_that("arg_is_numeric basic behavior", { + expect_no_error(arg_is_numeric(c = 1.25, b = 2:5, 1, c(2.22, 2.12))) + + for (val in list(list(1), "a", list(NULL))) { + expect_error(arg_is_numeric(val), + class = "epiprocess__value_is_null_or_not_numeric" + ) + } + + expect_no_error(arg_is_numeric(1, c(1.255, 2.33, 3), NULL, allow_null = TRUE)) + expect_error(arg_is_numeric(1, c(1.255, 2.33, 3), NULL), + class = "epiprocess__value_is_null_or_not_numeric" + ) +}) + +test_that("arg_is_int basic behavior", { + expect_no_error(arg_is_int(c = 1, 1, 3, b = 2:5)) + expect_no_error(arg_is_int(NULL, 1, allow_null = TRUE)) + + for (val in list(1.25, -(1:3))) { + expect_error(arg_is_int(val), + class = "epiprocess__some_decimal_or_negative_elements" + ) + } +}) + +test_that("arg_is_chr basic behavior", { + expect_no_error(arg_is_chr(c = c("a", "value"), d = "a", "d")) + + expect_no_error(arg_is_chr(NULL, allow_null = TRUE)) # + for (val in list(NULL)) { + expect_error(arg_is_chr(val), # + class = "epiprocess__value_is_null" + ) + } + + expect_no_error(arg_is_chr(NA, c(NA, NA, NA), c(NA, "a"), allow_na = TRUE)) + for (val in list(NA, c(NA, NA, NA), c(NA, "a"))) { + expect_error(arg_is_chr(val), + class = "epiprocess__some_na_elements" + ) + } + + expect_no_error(arg_is_chr(c("a", "value"), character(0), list(), allow_empty = TRUE)) + for (val in list(character(0), list())) { + expect_error(arg_is_chr(val), + class = "epiprocess__value_length_0" + ) + } + + for (val in list(c(5, 4), list(5, 4), 5)) { + expect_error(arg_is_chr(val), + class = "epiprocess__not_character_type" + ) + } +}) + +test_that("arg_is_chr_scalar basic behavior", { + expect_no_error(arg_is_chr_scalar("a", "b", c = "c")) + expect_no_error(arg_is_chr_scalar(c = "c")) +}) + From 0bb93e2d1af009ca9acb50bb05456ee62772a938 Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 26 Jan 2024 10:46:30 -0800 Subject: [PATCH 22/27] refactor: use checkmate checks --- DESCRIPTION | 2 +- NAMESPACE | 4 +++ R/autoplot.R | 13 ++++---- R/key_colnames.R | 6 ++-- R/utils-arg.R | 73 ------------------------------------------ man/autoplot.epi_df.Rd | 4 +-- 6 files changed, 18 insertions(+), 84 deletions(-) delete mode 100644 R/utils-arg.R diff --git a/DESCRIPTION b/DESCRIPTION index 211c5212..14437481 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -26,6 +26,7 @@ Description: This package introduces a common data structure for epidemiological License: MIT + file LICENSE Copyright: file inst/COPYRIGHTS Imports: + checkmate, cli, data.table, dplyr (>= 1.0.0), @@ -86,6 +87,5 @@ Collate: 'outliers.R' 'reexports.R' 'slide.R' - 'utils-arg.R' 'utils.R' 'utils_pipe.R' diff --git a/NAMESPACE b/NAMESPACE index a843813d..23140464 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -68,6 +68,10 @@ export(slice) export(ungroup) export(unnest) importFrom(R6,R6Class) +importFrom(checkmate,anyInfinite) +importFrom(checkmate,assert) +importFrom(checkmate,assert_character) +importFrom(checkmate,assert_int) importFrom(data.table,":=") importFrom(data.table,address) importFrom(data.table,as.data.table) diff --git a/R/autoplot.R b/R/autoplot.R index c193e01f..73bcaf0b 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -25,6 +25,8 @@ #' @return A ggplot object #' @export #' +#' @importFrom checkmate assert assert_int anyInfinite assert_character +#' #' @examples #' autoplot(jhu_csse_daily_subset, cases, death_rate_7d_av) #' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .facet_by = "geo_value") @@ -32,12 +34,12 @@ #' .color_by = "none", #' .facet_by = "geo_value" #' ) -#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", +#' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", #' .base_color = "red", .facet_by = "geo_value") -#' +#' #' # .base_color specification won't have any effect due .color_by default #' autoplot(jhu_csse_daily_subset, case_rate_7d_av, -#' .base_color = "red", .facet_by = "geo_value") +#' .base_color = "red", .facet_by = "geo_value") autoplot.epi_df <- function( object, ..., .color_by = c("all_keys", "geo_value", "other_keys", ".response", "all", "none"), @@ -47,9 +49,8 @@ autoplot.epi_df <- function( .color_by <- match.arg(.color_by) .facet_by <- match.arg(.facet_by) - arg_is_scalar(.max_facets) - if (is.finite(.max_facets)) arg_is_int(.max_facets) - arg_is_chr_scalar(.base_color) + assert(anyInfinite(.max_facets), assert_int(.max_facets), combine = "or") + assert_character(.base_color, len = 1) key_cols <- key_colnames(object) non_key_cols <- setdiff(names(object), key_cols) diff --git a/R/key_colnames.R b/R/key_colnames.R index 158c5a86..0d34f5f4 100644 --- a/R/key_colnames.R +++ b/R/key_colnames.R @@ -15,9 +15,10 @@ key_colnames.default <- function(x, ...) { character(0L) } +#' @importFrom checkmate assert_character #' @export key_colnames.data.frame <- function(x, other_keys = character(0L), ...) { - arg_is_chr(other_keys, allow_empty = TRUE) + assert_character(other_keys) nm <- c("time_value", "geo_value", other_keys) intersect(nm, colnames(x)) } @@ -34,7 +35,8 @@ key_colnames.epi_archive <- function(x, ...) { c("time_value", "geo_value", other_keys) } +#' @importFrom checkmate assert_character kill_time_value <- function(v) { - arg_is_chr(v) + assert_character(v) v[v != "time_value"] } diff --git a/R/utils-arg.R b/R/utils-arg.R deleted file mode 100644 index b48a3642..00000000 --- a/R/utils-arg.R +++ /dev/null @@ -1,73 +0,0 @@ -handle_arg_list <- function(..., tests) { - values <- list(...) - names <- eval(substitute(alist(...))) - names <- purrr::map(names, deparse) - - purrr::walk2(names, values, tests) -} - -arg_is_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { - handle_arg_list( - ..., - tests = function(name, value) { - if (length(value) > 1 | (!allow_null & length(value) == 0)) { - cli::cli_abort("Argument {.val {name}} must be of length 1.") - } - if (!is.null(value)) { - if (is.na(value) & !allow_na) { - cli::cli_abort( - "Argument {.val {name}} must not be a missing value ({.val {NA}})." - ) - } - } - } - ) -} - -arg_is_numeric <- function(..., allow_null = FALSE) { - handle_arg_list( - ..., - tests = function(name, value) { - if (!(is.numeric(value) | (is.null(value) & allow_null))) { - cli::cli_abort("All {.val {name}} must numeric.") - } - } - ) -} - -arg_is_int <- function(..., allow_null = FALSE) { - arg_is_numeric(..., allow_null = allow_null) - handle_arg_list( - ..., - tests = function(name, value) { - if (!(all(value %% 1 == 0) | (is.null(value) & allow_null))) { - cli::cli_abort("All {.val {name}} must be whole positive number(s).") - } - } - ) -} - -arg_is_chr <- function(..., allow_null = FALSE, allow_na = FALSE, allow_empty = FALSE) { - handle_arg_list( - ..., - tests = function(name, value) { - if (is.null(value) & !allow_null) { - cli::cli_abort("Argument {.val {name}} may not be `NULL`.") - } - if (any(is.na(value)) & !allow_na) { - cli::cli_abort("Argument {.val {name}} must not contain any missing values ({.val {NA}}).") - } - if (!is.null(value) & (length(value) == 0L & !allow_empty)) { - cli::cli_abort("Argument {.val {name}} must have length > 0.") - } - if (!(is.character(value) | is.null(value) | all(is.na(value)))) { - cli::cli_abort("Argument {.val {name}} must be of character type.") - } - } - ) -} - -arg_is_chr_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { - arg_is_chr(..., allow_null = allow_null, allow_na = allow_na) - arg_is_scalar(..., allow_null = allow_null, allow_na = allow_na) -} diff --git a/man/autoplot.epi_df.Rd b/man/autoplot.epi_df.Rd index df30528e..a87bc8ca 100644 --- a/man/autoplot.epi_df.Rd +++ b/man/autoplot.epi_df.Rd @@ -56,10 +56,10 @@ autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", .facet_by = "geo_value" ) -autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", +autoplot(jhu_csse_daily_subset, case_rate_7d_av, .color_by = "none", .base_color = "red", .facet_by = "geo_value") # .base_color specification won't have any effect due .color_by default autoplot(jhu_csse_daily_subset, case_rate_7d_av, - .base_color = "red", .facet_by = "geo_value") + .base_color = "red", .facet_by = "geo_value") } From 8671c0c554fa4e99da96292ad3ef0634e379e612 Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 26 Jan 2024 15:20:52 -0800 Subject: [PATCH 23/27] refactor: move checkmate imports to one place --- R/autoplot.R | 2 -- R/epiprocess.R | 1 + R/key_colnames.R | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/R/autoplot.R b/R/autoplot.R index 73bcaf0b..a6cf99bc 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -25,8 +25,6 @@ #' @return A ggplot object #' @export #' -#' @importFrom checkmate assert assert_int anyInfinite assert_character -#' #' @examples #' autoplot(jhu_csse_daily_subset, cases, death_rate_7d_av) #' autoplot(jhu_csse_daily_subset, case_rate_7d_av, .facet_by = "geo_value") diff --git a/R/epiprocess.R b/R/epiprocess.R index bbdcf4f3..79f9635d 100644 --- a/R/epiprocess.R +++ b/R/epiprocess.R @@ -4,6 +4,7 @@ #' measured over space and time, and offers associated utilities to perform #' basic signal processing tasks. #' +#' @importFrom checkmate assert assert_character assert_int anyInfinite #' @docType package #' @name epiprocess NULL diff --git a/R/key_colnames.R b/R/key_colnames.R index 0d34f5f4..99d8a9ed 100644 --- a/R/key_colnames.R +++ b/R/key_colnames.R @@ -15,7 +15,6 @@ key_colnames.default <- function(x, ...) { character(0L) } -#' @importFrom checkmate assert_character #' @export key_colnames.data.frame <- function(x, other_keys = character(0L), ...) { assert_character(other_keys) @@ -35,7 +34,6 @@ key_colnames.epi_archive <- function(x, ...) { c("time_value", "geo_value", other_keys) } -#' @importFrom checkmate assert_character kill_time_value <- function(v) { assert_character(v) v[v != "time_value"] From 1d45d65d78e495dfa424f6e16918d97fd52135ae Mon Sep 17 00:00:00 2001 From: Nat DeFries <42820733+nmdefries@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:47:00 -0500 Subject: [PATCH 24/27] utils-arg removed in #410, so no longer need to test --- R/utils-arg.R | 88 --------------------------------- tests/testthat/test-utils-arg.R | 80 ------------------------------ 2 files changed, 168 deletions(-) delete mode 100644 R/utils-arg.R delete mode 100644 tests/testthat/test-utils-arg.R diff --git a/R/utils-arg.R b/R/utils-arg.R deleted file mode 100644 index 85331dd6..00000000 --- a/R/utils-arg.R +++ /dev/null @@ -1,88 +0,0 @@ -handle_arg_list <- function(..., tests) { - values <- list(...) - names <- eval(substitute(alist(...))) - names <- purrr::map(names, deparse) - - purrr::walk2(names, values, tests) -} - -arg_is_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { - handle_arg_list( - ..., - tests = function(name, value) { - if (length(value) > 1 | (!allow_null & length(value) == 0)) { - cli::cli_abort("Argument {.val {name}} must be of length 1.", - class = "epiprocess__value_not_length_1" - ) - } - if (!is.null(value)) { - if (is.na(value) & !allow_na) { - cli::cli_abort( - "Argument {.val {name}} must not be a missing value ({.val {NA}}).", - class = "epiprocess__value_is_na" - ) - } - } - } - ) -} - -arg_is_numeric <- function(..., allow_null = FALSE) { - handle_arg_list( - ..., - tests = function(name, value) { - if (!(is.numeric(value) | (is.null(value) & allow_null))) { - cli::cli_abort("All {.val {name}} must be numeric.", - class = "epiprocess__value_is_null_or_not_numeric" - ) - } - } - ) -} - -arg_is_int <- function(..., allow_null = FALSE) { - arg_is_numeric(..., allow_null = allow_null) - handle_arg_list( - ..., - tests = function(name, value) { - if (!( (all(value %% 1 == 0) && all(value > 0)) | (is.null(value) & allow_null))) { - cli::cli_abort("All {.val {name}} must be whole positive number(s).", - class = "epiprocess__some_decimal_or_negative_elements" - ) - } - } - ) -} - -arg_is_chr <- function(..., allow_null = FALSE, allow_na = FALSE, allow_empty = FALSE) { - handle_arg_list( - ..., - tests = function(name, value) { - if (is.null(value) & !allow_null) { - cli::cli_abort("Argument {.val {name}} may not be `NULL`.", - class = "epiprocess__value_is_null" - ) - } - if (any(is.na(value)) & !allow_na) { - cli::cli_abort("Argument {.val {name}} must not contain any missing values ({.val {NA}}).", - class = "epiprocess__some_na_elements" - ) - } - if (!is.null(value) & (length(value) == 0L & !allow_empty)) { - cli::cli_abort("Argument {.val {name}} must have length > 0.", - class = "epiprocess__value_length_0" - ) - } - if (!(is.character(value) | is.null(value) | all(is.na(value)))) { - cli::cli_abort("Argument {.val {name}} must be of character type.", - class = "epiprocess__not_character_type" - ) - } - } - ) -} - -arg_is_chr_scalar <- function(..., allow_null = FALSE, allow_na = FALSE) { - arg_is_chr(..., allow_null = allow_null, allow_na = allow_na) - arg_is_scalar(..., allow_null = allow_null, allow_na = allow_na) -} diff --git a/tests/testthat/test-utils-arg.R b/tests/testthat/test-utils-arg.R deleted file mode 100644 index ab29e061..00000000 --- a/tests/testthat/test-utils-arg.R +++ /dev/null @@ -1,80 +0,0 @@ -test_that("arg_is_scalar basic behavior", { - expect_no_error(arg_is_scalar(d = 1, "a", 2, c = c("1"), a = list(2))) - - expect_error(arg_is_scalar(c(3, 5, 5)), - class = "epiprocess__value_not_length_1" - ) - - expect_no_error(arg_is_scalar(NULL, allow_null = TRUE)) - expect_error(arg_is_scalar(NULL), - class = "epiprocess__value_not_length_1" - ) - - expect_no_error(arg_is_scalar(NA, allow_na = TRUE)) - expect_error(arg_is_scalar(NA), - class = "epiprocess__value_is_na" - ) -}) - -test_that("arg_is_numeric basic behavior", { - expect_no_error(arg_is_numeric(c = 1.25, b = 2:5, 1, c(2.22, 2.12))) - - for (val in list(list(1), "a", list(NULL))) { - expect_error(arg_is_numeric(val), - class = "epiprocess__value_is_null_or_not_numeric" - ) - } - - expect_no_error(arg_is_numeric(1, c(1.255, 2.33, 3), NULL, allow_null = TRUE)) - expect_error(arg_is_numeric(1, c(1.255, 2.33, 3), NULL), - class = "epiprocess__value_is_null_or_not_numeric" - ) -}) - -test_that("arg_is_int basic behavior", { - expect_no_error(arg_is_int(c = 1, 1, 3, b = 2:5)) - expect_no_error(arg_is_int(NULL, 1, allow_null = TRUE)) - - for (val in list(1.25, -(1:3))) { - expect_error(arg_is_int(val), - class = "epiprocess__some_decimal_or_negative_elements" - ) - } -}) - -test_that("arg_is_chr basic behavior", { - expect_no_error(arg_is_chr(c = c("a", "value"), d = "a", "d")) - - expect_no_error(arg_is_chr(NULL, allow_null = TRUE)) # - for (val in list(NULL)) { - expect_error(arg_is_chr(val), # - class = "epiprocess__value_is_null" - ) - } - - expect_no_error(arg_is_chr(NA, c(NA, NA, NA), c(NA, "a"), allow_na = TRUE)) - for (val in list(NA, c(NA, NA, NA), c(NA, "a"))) { - expect_error(arg_is_chr(val), - class = "epiprocess__some_na_elements" - ) - } - - expect_no_error(arg_is_chr(c("a", "value"), character(0), list(), allow_empty = TRUE)) - for (val in list(character(0), list())) { - expect_error(arg_is_chr(val), - class = "epiprocess__value_length_0" - ) - } - - for (val in list(c(5, 4), list(5, 4), 5)) { - expect_error(arg_is_chr(val), - class = "epiprocess__not_character_type" - ) - } -}) - -test_that("arg_is_chr_scalar basic behavior", { - expect_no_error(arg_is_chr_scalar("a", "b", c = "c")) - expect_no_error(arg_is_chr_scalar(c = "c")) -}) - From a53e98154327a6f4e9583f2c6c9d4b03269f8e54 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Tue, 6 Feb 2024 09:38:39 -0800 Subject: [PATCH 25/27] roxygen note --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index c014ad78..7f36ad7c 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -67,7 +67,7 @@ Config/testthat/edition: 3 Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 Depends: R (>= 2.10) URL: https://cmu-delphi.github.io/epiprocess/ From ab96bad5b9ca67ee02aa0e49d630f6795897bf25 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Tue, 6 Feb 2024 09:45:38 -0800 Subject: [PATCH 26/27] redocument --- NAMESPACE | 1 + man/epiprocess.Rd | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index 23140464..5a1ddfa0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -72,6 +72,7 @@ importFrom(checkmate,anyInfinite) importFrom(checkmate,assert) importFrom(checkmate,assert_character) importFrom(checkmate,assert_int) +importFrom(cli,cli_inform) importFrom(data.table,":=") importFrom(data.table,address) importFrom(data.table,as.data.table) diff --git a/man/epiprocess.Rd b/man/epiprocess.Rd index 9d501aeb..7c3ecd8a 100644 --- a/man/epiprocess.Rd +++ b/man/epiprocess.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/epiprocess.R \docType{package} \name{epiprocess} +\alias{epiprocess-package} \alias{epiprocess} \title{epiprocess: Tools for basic signal processing in epidemiology} \description{ @@ -9,3 +10,36 @@ This package introduces a common data structure for epidemiological data sets measured over space and time, and offers associated utilities to perform basic signal processing tasks. } +\seealso{ +Useful links: +\itemize{ + \item \url{https://cmu-delphi.github.io/epiprocess/} +} + +} +\author{ +\strong{Maintainer}: Ryan Tibshirani \email{ryantibs@cmu.edu} + +Authors: +\itemize{ + \item Logan Brooks + \item Daniel McDonald + \item Evan Ray +} + +Other contributors: +\itemize{ + \item Jacob Bien [contributor] + \item Rafael Catoia [contributor] + \item Nat DeFries [contributor] + \item Rachel Lobay [contributor] + \item Ken Mawer [contributor] + \item Chloe You [contributor] + \item Quang Nguyen [contributor] + \item Dmitry Shemetov [contributor] + \item Lionel Henry (Author of included rlang fragments) [contributor] + \item Hadley Wickham (Author of included rlang fragments) [contributor] + \item Posit (Copyright holder of included rlang fragments) [copyright holder] +} + +} From c84d67859fc07abd830011b997f57b596fff08f3 Mon Sep 17 00:00:00 2001 From: "Daniel J. McDonald" Date: Tue, 6 Feb 2024 09:46:49 -0800 Subject: [PATCH 27/27] fix: follow instructions for @docType deprecated error --- R/epiprocess.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/epiprocess.R b/R/epiprocess.R index 79f9635d..254ebd01 100644 --- a/R/epiprocess.R +++ b/R/epiprocess.R @@ -5,7 +5,6 @@ #' basic signal processing tasks. #' #' @importFrom checkmate assert assert_character assert_int anyInfinite -#' @docType package #' @name epiprocess -NULL +"_PACKAGE" utils::globalVariables(c(".x", ".group_key", ".ref_time_value"))